From 5cac40654f0c2df7f079facc3d4d08e009df3b97 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 17 Jun 2018 15:09:52 +0200 Subject: [PATCH] DockAccess: draft code reorganisation (#1144) --- src/gui/workspace/dock-area.cpp | 371 ++++++++++++++++++++++++++++ src/gui/workspace/dock-area.hpp | 339 +++++++++++++++++++++++++ src/gui/workspace/panel-manager.cpp | 3 +- src/gui/workspace/panel-manager.hpp | 1 + wiki/renderengine.html | 9 +- wiki/thinkPad.ichthyo.mm | 99 ++++++-- 6 files changed, 794 insertions(+), 28 deletions(-) create mode 100644 src/gui/workspace/dock-area.cpp create mode 100644 src/gui/workspace/dock-area.hpp diff --git a/src/gui/workspace/dock-area.cpp b/src/gui/workspace/dock-area.cpp new file mode 100644 index 000000000..6019ec724 --- /dev/null +++ b/src/gui/workspace/dock-area.cpp @@ -0,0 +1,371 @@ +/* + DockArea - maintain a docking area within the WorkspaceWindow + + Copyright (C) Lumiera.org + 2008, Joel Holdsworth + 2018, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +/** @file dock-area.cpp + ** Implementation of dockable UI panels, implemented with the + ** help of lib GDL (»Gnome Docking Library«, formerly aka »Gnome Design Library«) + ** @todo will be transformed into a Dock entity as of 6/2018 /////////////////////////////////////////////TICKET #1144 refactor dock handling + */ + + +#include "gui/workspace/dock-area.hpp" + +#include "gui/panel/assets-panel.hpp" +#include "gui/panel/viewer-panel.hpp" +#include "gui/panel/infobox-panel.hpp" +#include "gui/panel/timeline-panel.hpp" +#include "gui/panel/timeline-panel-obsolete.hpp" + +#include "include/logging.h" + +using namespace boost; ////////////////////////////////////////////////////////////////////////////////TICKET #1071 no wildcard includes please! +using namespace std; ////////////////////////////////////////////////////////////////////////////////TICKET #1071 no wildcard includes please! +using namespace Gtk; ////////////////////////////////////////////////////////////////////////////////TICKET #1071 no wildcard includes please! + +namespace gui { +namespace workspace { + + const DockArea::PanelDescription + DockArea::panelDescriptionList[] = { + DockArea::Panel(), + DockArea::Panel(), + DockArea::Panel(), + DockArea::Panel(), + DockArea::Panel() + }; + + unsigned short DockArea::panelID = 0; + + + + DockArea::DockArea (WorkspaceWindow& owner) + : workspaceWindow_(owner) + , dock_() + , dockBar_(dock_) + , dockLayout_() + { + /* Create the DockLayout */ + dockLayout_ = Gdl::DockLayout::create(dock_); + + /* Setup the Switcher Style */ + Glib::RefPtr dock_master = dock_.property_master(); + dock_master->property_switcher_style() = Gdl::SWITCHER_STYLE_ICON; + + memset(&dockPlaceholders_, 0, sizeof(dockPlaceholders_)); + } + + + + DockArea::~DockArea() + { + ///////////////////////////////////////////////////////TICKET #195 : violation of policy, dtors must not do any work + ///////////////////////////////////////////////////////TICKET #172 : observed as a reason for crashes when closing the GUI. It was invoked after end of main, when the GUI as already gone. + +#if false ///////////////////////////////////////////////////TICKET #937 : disabled for GTK-3 transition. TODO investigate why this logic existed... + ///////////////////////////////////////////////////////TICKET #1027 + for(int i = 0; i < 4; i++) + if(dockPlaceholders_[i]) + g_object_unref(dockPlaceholders_[i]); + + clearPanels(); +#endif ///////////////////////////////////////////////////TICKET #937 : (End)disabled for GTK-3 transition. + } + + + + void + DockArea::setupDock() + { + ///////////////////////////////////////////////////////TICKET #1027 : investigate what would be the proper way to do this with gdlmm (C++ binding). No direct usage of GDL ! + + REQUIRE(dockPlaceholders_[0] == NULL && dockPlaceholders_[1] == NULL && + dockPlaceholders_[2] == NULL && dockPlaceholders_[3] == NULL); + dockPlaceholders_[0] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new( + "ph1", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_TOP, FALSE)); + dockPlaceholders_[1] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new( + "ph2", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_BOTTOM, FALSE)); + dockPlaceholders_[2] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new( + "ph3", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_LEFT, FALSE)); + dockPlaceholders_[3] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new( + "ph4", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_RIGHT, FALSE)); + ENSURE(dockPlaceholders_[0] && dockPlaceholders_[1] && + dockPlaceholders_[2] && dockPlaceholders_[3]); + + createPanels(); + } + + + Gdl::Dock& + DockArea::getDock() + { + return dock_; + } + + + Gdl::DockBar& + DockArea::getDockBar() + { + return dockBar_; + } + + + WorkspaceWindow& + DockArea::getWorkspaceWindow() + { + return workspaceWindow_; + } + + + void + DockArea::showPanel (const int description_index) + { + // Try and find the panel and present it if possible + list< panel::Panel* >::iterator i; + for(i = panels_.begin(); i != panels_.end(); i++) + { + panel::Panel* const panel = *i; + if (getPanelType(panel) == description_index) + { + if (!panel->is_shown()) panel->show(); + + Gdl::DockItem &dock_item = panel->getDockItem(); + // ENSURE(dock_item); + dock_item.present(dock_); + return; + } + } + + // Create the new panel + panel::Panel *new_panel = createPanel_by_index (description_index); + + // Dock the item + dock_.add_item(new_panel->getDockItem(), Gdl::DOCK_FLOATING); + } + + + void + DockArea::switchPanel (panel::Panel& old_panel, const int description_index) + { + REQUIRE (description_index >= 0 && + description_index < getPanelDescriptionCount()); + + // Get the dock item + Gdl::DockItem &dock_item = old_panel.getDockItem(); + + // Release the old panel + removePanel (&old_panel); + + // Create the new panel + createPanel_by_index (description_index, dock_item); + } + + + void + DockArea::splitPanel (panel::Panel& panel, Gtk::Orientation split_direction) + { + + // Create the new panel + const int index = getPanelType(&panel); + panel::Panel *new_panel = createPanel_by_index(index); + + // Dock the panel + Gdl::DockPlacement placement = Gdl::DOCK_NONE; + switch(split_direction) + { + case ORIENTATION_HORIZONTAL: + placement = Gdl::DOCK_RIGHT; + break; + + case ORIENTATION_VERTICAL: + placement = Gdl::DOCK_BOTTOM; + break; + + default: + ERROR(gui, "Unknown split_direction: %d", split_direction); + return; + break; + } + + panel.getDockItem().dock( + new_panel->getDockItem(),placement); + } + + + int + DockArea::getPanelDescriptionCount() + { + return sizeof(panelDescriptionList) / sizeof(PanelDescription); + } + + + const gchar* + DockArea::getPanelStockID (int index) + { + REQUIRE (index >= 0 && index < getPanelDescriptionCount()); + return panelDescriptionList[index].getStockID(); + } + + + const char* + DockArea::getPanelTitle (int index) + { + REQUIRE (index >= 0 && index < getPanelDescriptionCount()); + return panelDescriptionList[index].getTitle(); + } + + + void + DockArea::createPanels() + { + ///////////////////////////////TICKET #1026 : code smell, use types directly instead + panel::Panel* assetsPanel = createPanel_by_name("AssetsPanel"); + panel::Panel* viewerPanel = createPanel_by_name("InfoBoxPanel"); + panel::Panel* timelinePanel = createPanel_by_name("TimelinePanel"); + + dock_.add_item(assetsPanel->getDockItem(),Gdl::DOCK_LEFT); + dock_.add_item(timelinePanel->getDockItem(),Gdl::DOCK_BOTTOM); + dock_.add_item(viewerPanel->getDockItem(),Gdl::DOCK_RIGHT); + } + + + int + DockArea::findPanelDescription (const char* class_name) const + { + REQUIRE(class_name); + + const int count = getPanelDescriptionCount(); + for(int i = 0; i < count; i++) + { + if (strstr(panelDescriptionList[i].getClassName(), class_name)) + return i; + } + + ERROR (gui, "Unable to find a description with class name %s", class_name); + return -1; + } + + + panel::Panel* + DockArea::createPanel_by_index (const int index) + { + REQUIRE(index >= 0 && index < getPanelDescriptionCount()); + + // Make a unique name for the panel + char name[5]; + snprintf(name, sizeof(name), "%X", panelID++); + + // Create a dock item + return createPanel_by_index(index, + *new Gdl::DockItem(name,"",Gdl::DOCK_ITEM_BEH_NORMAL)); + } + + + panel::Panel* + DockArea::createPanel_by_index (const int index, Gdl::DockItem &dock_item) + { + // Create the panel object + panel::Panel *panel = panelDescriptionList[index].create(*this, dock_item); + ENSURE(panel); + panel->show_all(); + + // Connect event handlers + panel->signal_hidePanel().connect(sigc::bind( + sigc::mem_fun(*this, &PanelManager::on_panel_shown), panel)); + + // Add the panel to the list + panels_.push_back(panel); + + return panel; + } + + + panel::Panel* + DockArea::createPanel_by_name (const char* class_name) + { + REQUIRE(class_name); + const int index = findPanelDescription(class_name); + return createPanel_by_index(index); + } + + + int + DockArea::getPanelType (panel::Panel* const panel) const + { + REQUIRE(panel); + + const type_info &info = typeid(*panel); + const int count = getPanelDescriptionCount(); + for(int i = 0; i < count; i++) + { + if(info == panelDescriptionList[i].getClassInfo()) + return i; + } + + ERROR(gui, "Unable to find a description with with this class type"); + return -1; + } + + + void + DockArea::removePanel (panel::Panel* const panel) + { + REQUIRE(panel); + + list< panel::Panel* >::iterator i; + for(i = panels_.begin(); i != panels_.end(); i++) + { + if(*i == panel) + { + delete panel; + panels_.erase(i); + break; + } + } + } + + + void + DockArea::clearPanels() + { + ///////////////////////////////////////////////////////TICKET #195 : this whole approach smells like an obsolete "C-style" approach. We should strive to let the runtime system do such stuff for us whenever possible, eg. by using smart pointers + + list< panel::Panel* >::iterator i; + for(i = panels_.begin(); i != panels_.end(); i++) + delete *i; + panels_.clear(); + } + + + void + DockArea::on_panel_shown (panel::Panel* panel) + { + REQUIRE(panel); + if(panel->is_shown() || panel->is_iconified()) return; + + removePanel(panel); + } + + + +}}// namespace gui::workspace diff --git a/src/gui/workspace/dock-area.hpp b/src/gui/workspace/dock-area.hpp new file mode 100644 index 000000000..66b8137dd --- /dev/null +++ b/src/gui/workspace/dock-area.hpp @@ -0,0 +1,339 @@ +/* + DOCK-AREA.hpp - maintain a docking area within the WorkspaceWindow + + Copyright (C) Lumiera.org + 2008, Joel Holdsworth + 2018, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file dock-area.hpp + ** Management of dockable panels within each top-level WorkspaceWindow. + ** + ** @todo 2017 need to clarify the intended behaviour of panels + ** ///////////////////////////////////////////////////////////////////////////TICKET #1097 clarify the role and behaviour of Panels + ** @todo will be transformed into a Dock entity as of 6/2018 /////////////////////////////////////////////TICKET #1144 refactor dock handling + ** + ** @see actions.hpp + */ + +#ifndef GUI_WORKSPACE_DOCK_AREA_H +#define GUI_WORKSPACE_DOCK_AREA_H + +#include "gui/panel/panel.hpp" + +#include +#include + + +using namespace gui::panel; ///////////////////////////////////////////////////////////////////////////TICKET #1071 no wildcard includes please! + +namespace gui { +namespace workspace { + + + /** + * A class to manage DockItem objects for WorkspaceWindow. + * @todo this code is smelly and needs some clean-up ///////////////////////////////TICKET #1026 + */ + class DockArea + { + /** reference to the owner workspace window object */ + WorkspaceWindow& workspaceWindow_; + + /** The pointer to GDL dock widget. + * @remarks This value is NULL until setup_dock has been called. + */ + Gdl::Dock dock_; + + /** The pointer to GDL dock bar widget. + * @remarks This value is NULL until setup_dock has been called. + */ + Gdl::DockBar dockBar_; + + /** The pointer to GDL dock layout object. + * @remarks This value is NULL until setup_dock has been called. + */ + Glib::RefPtr dockLayout_; + + /** Pointers to the 4 root place holders. + * @remarks All 4 entries are NULL until setup_dock has been called. + */ + GdlDockPlaceholder *dockPlaceholders_[4]; + + /** list of all panels created */ + std::list panels_; + + /** + * An accumulator for the panel id. + */ + static unsigned short panelID; + + + public: + DockArea (WorkspaceWindow&); + ~DockArea(); + + /** + * Initialises this dock manager and creates the dock and all it's widgets. + * @remarks This function must be called only once as the first call after construction. + */ + void setupDock(); + + /** + * Gets a pointer to the dock object. + * @remarks Note that this must not be called before setup_dock. ///////////////////////////////TICKET #1026 : code smell + */ + Gdl::Dock& getDock(); + + /** + * Gets a pointer to the dock bar. + * @remarks Note that this must not be called before setup_dock. + */ + Gdl::DockBar& getDockBar(); + + /** + * Returns a reference to the owner workspace window. + */ + WorkspaceWindow& getWorkspaceWindow(); ///////////////////////////////TICKET #1026 : code smell, unclear dependency relation + + /** + * Shows a panel given a description index. + * @param description_index The index of the panel type to show. + */ + void showPanel (const int description_index); + + /** + * Switches a panel from one type to another, + * without touching the underlying GdlDockItem. + * @param old_panel The panel which will be transformed to a new type. + * @param description_index The index of the panel description + * that will be instantiated. + */ + void switchPanel (panel::Panel& old_panel, const int description_index); + + /** + * Splits a panel into two panels of the same type. + * @param panel The panel to split. + * @param split_direction The direction to split the panel in. + */ + void splitPanel (panel::Panel& panel, Gtk::Orientation split_direction); + + + public: + /** Gets the number of panel descriptions. */ + static int getPanelDescriptionCount(); + + /** + * Gets a panel description's stock id. + * @param index The index of the panel to retrieve. + * @return Returns the stock id of a panel at this index. + */ + static const gchar* getPanelStockID (const int index); + + /** + * Gets a panel description's title. + * @param index The index of the panel to retrieve. + * @return Returns the title of a panel at this index. + */ + static const char* getPanelTitle (int index); + + + private: + /** Creates the standard panel layout.*/ + void createPanels(); + + /** + * Find the index of a panel description given the class name. + * @param class_name The name of the object class to search for. + * @return Returns the index of the panel description found, or -1 + * if no description was found for this type. + */ + int findPanelDescription (const char* class_name) const; + + /** + * Creates a panel by description index. + * @param index The index of the description to instantiate. + * @return Returns a pointer to the new instantiated panel object. + */ + panel::Panel* createPanel_by_index (const int index); + + /** + * Creates a panel by description index with a given GdlDockItem + * @param index The index of the description to instantiate. + * @param dock_item The GdlDockItem to attach this panel to + * @return Returns a pointer to the new instantiated panel object. + */ + panel::Panel* createPanel_by_index (const int index, Gdl::DockItem& dock_item); + + /** + * Creates a panel by class name. + * @param class_name The name of the object class to create. + * @return Returns a pointer to the new instantiated panel object. + */ + panel::Panel* createPanel_by_name (const char* class_name); + + /** + * Gets the type of a given panel. + * @param panel The Panel to get the type of + * @return Returns the index of the panel description found, or -1 + * if no description was found for this type. + */ + int getPanelType (panel::Panel* const panel) const; + + /** + * Removes a panel from the panel list and deletes it. + * @param panel The panel to remove and delete. + */ + void removePanel (panel::Panel* const panel); + + /** Removes all panels from the panel list and deletes them.*/ + void clearPanels(); + + + private: + /** + * An event handler for when the panel is shown or hidden. + * @param panel A pointer to the panel that was hidden. + */ + void on_panel_shown (panel::Panel *panel); + + + + private: + + /** + * A class to describe and instantiate Panel types. + */ + class PanelDescription + { + protected: + typedef panel::Panel* (*const CreatePanelProc)(PanelManager&, Gdl::DockItem&); + ///////////////////////////////TICKET #1026 : code smell, why not just using inheritance? + private: + /** reference to the typeID of this class */ + const std::type_info& classInfo_; + + /** localised title that will be shown on the panel. */ + const char* const titleName_; + + /** Stock ID for this type of panel.*/ + const gchar* const stockID_; + + /**pointer to a function that will instantiate the panel object */ + CreatePanelProc createPanelProc_; + + + protected: + /** + * @param classInfo The typeID of the Panel class + * @param title The localised title that will be shown on the panel. + * @param stock_id The Stock ID for this type of panel. + * @param create_panel_proc A pointer to a function that will instantiate the panel object. + */ + PanelDescription (std::type_info const& classInfo + ,const char* title + ,const gchar* stockID + ,CreatePanelProc createPanelProc) + : classInfo_(classInfo) + , titleName_(title) + , stockID_(stockID) + , createPanelProc_(createPanelProc) + { + REQUIRE(titleName_); + } + + + public: + const std::type_info& getClassInfo() const + { + return classInfo_; + } + + const char* getClassName() const + { + return classInfo_.name(); + } + + /** the localised title that will be shown on the panel */ + const char* getTitle() const + { + ENSURE(titleName_); + return titleName_; + } + + const gchar* getStockID() const + { + ENSURE(stockID_); + return stockID_; + } + + + /** + * Creates an instance of this panel. + * @param panel_manager The owner panel manager. + * @param dock_item The GdlDockItem that will host this panel. + * @return Returns a pointer to the panel object. + */ + panel::Panel* + create (PanelManager& panelManager, Gdl::DockItem& dockItem) const + { + REQUIRE(createPanelProc_); + return createPanelProc_ (panelManager, dockItem); + } + }; + + + + /** + * A helper class that will create PanelDescription objects. + * @param P The type of panel::Panel that the PanelDescription will describe. + */ + template + class Panel + : public PanelDescription + { + public: + Panel() + : PanelDescription (typeid(P) + ,P::getTitle() + ,P::getStockID() + ,Panel::createPanel) + { } + + private: + /**helper function to create a panel of type P + * @param panel_manager The owner panel manager. + * @param dock_item The Gdl::DockItem that will host this panel. + * @return pointer to the created panel object. + */ + static panel::Panel* + createPanel (PanelManager& panelManager, Gdl::DockItem& dockItem) + { + return new P(panelManager, dockItem); + } + }; + + + /** The list of panel descriptions */ + static const PanelDescription panelDescriptionList[]; + }; + + +}}// namespace gui::workspace +#endif /*GUI_WORKSPACE_DOCK_AREA_H*/ diff --git a/src/gui/workspace/panel-manager.cpp b/src/gui/workspace/panel-manager.cpp index 9d2cd1481..9df74186c 100644 --- a/src/gui/workspace/panel-manager.cpp +++ b/src/gui/workspace/panel-manager.cpp @@ -23,7 +23,8 @@ /** @file panel-manager.cpp ** Implementation of dockable UI panels, implemented with the - ** help of lib GDL (»Gnome Docking Libraray«, formerly aka »Gnome Design Library«) + ** help of lib GDL (»Gnome Docking Library«, formerly aka »Gnome Design Library«) + ** @deprecated shall be transformed into a Dock entity as of 6/2018 */ diff --git a/src/gui/workspace/panel-manager.hpp b/src/gui/workspace/panel-manager.hpp index 465dfbb88..3608618a5 100644 --- a/src/gui/workspace/panel-manager.hpp +++ b/src/gui/workspace/panel-manager.hpp @@ -26,6 +26,7 @@ ** ** @todo 2017 need to clarify the intended behaviour of panels ** ///////////////////////////////////////////////////////////////////////////TICKET #1097 clarify the role and behaviour of Panels + ** @deprecated shall be transformed into a Dock entity as of 6/2018 //////////////////////////////////////TICKET #1144 refactor dock handling ** ** @see actions.hpp */ diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 052bd25a5..02e09ecac 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3168,7 +3168,7 @@ In order to build a sensible plan for our timeline structure, we need to investi }}} -
+
//Management of dockable content panes//
 Within each top level application window, the usable screen real estate can be split and arranged into a number of standard view building blocks. Each of this //panels// follows a specific preconfigured basic layout -- these are hard coded, yet customisable in detail. There is a finite list of such panel types available:
 * the [[timeline display|GuiTimelineView]]
@@ -3182,6 +3182,13 @@ Please note the distinction between UI component view and panel; in many cases t
 !Placing and addressing of embedded contents
 A specific problem arises insofar other parts of the UI need to create, address and control some UI entities, which at the same time exist as part of a docking panel. This is a problem of crosscutting concerns: UI control and interaction structure should not be mingled with the generic concern to maintain a component as part of a screen layout. Unfortunately, the design of standard UI toolkit sets is incredibly naive when it comes to interaction design, and the only available alternative is to rely on frameworks, which come with a hard-wired philosophy.
 As a result, we're forced to build our UI from components which fall short on the distinction between //ownership and control.//
+
+!Hierarchy and organisation
+Each top-level window //holds a single dock,// which in turn might hold several docking panels, in a layout arranged by the user. However, the library ''GDL'', which Lumiera uses to implement docking functionality, introduce the notion of a //dock master.// This is an invisible control component, which maintains a common layout of panels, possibly located within several separated docks. The role of the master can be re-assigned; typically it is automatically attained by the first dock created, and several docks will share the same master, if and only if they are created as dependent, which means to create the second dock by referring to the first one (there is a dedicated function in GDL to achieve that). Only docks managed by the same master may participate in drag-n-drop actions beyond the scope of a single dock. Moreover, all iconified panels of a given master are represented as icons within a single //dock bar.//
+
+!!!Panel Locator
+In accordance with this structure, we introduce a central component, the {{{PanelLocator}}} -- to keep track of all {{{DockArea}}} elements within the UI. The latter are responsible for managing the docking panels //within a specific// top-level {{{WorkspaceWindow}}}.
+
 
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 29edcfc5d..9ffe81bcf 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -2831,6 +2831,21 @@ + + + + + + +

+ Aufgabe: docking panels global +

+ + +
+ + +
@@ -11026,7 +11041,7 @@ - + @@ -11250,13 +11265,13 @@ - + - + @@ -11471,11 +11486,11 @@ - + - + - + @@ -11497,7 +11512,7 @@ - + @@ -11665,6 +11680,9 @@ + + + @@ -11692,7 +11710,7 @@ - + @@ -11720,8 +11738,8 @@ - - + + @@ -11748,6 +11766,9 @@ + + + @@ -11778,7 +11799,7 @@ - + @@ -11924,7 +11945,7 @@ - + @@ -11970,7 +11991,7 @@ - + @@ -12069,7 +12090,7 @@ - + @@ -12116,7 +12137,7 @@ - + @@ -12136,7 +12157,7 @@ - + @@ -12154,7 +12175,7 @@ - + @@ -12171,7 +12192,7 @@ - + @@ -12192,7 +12213,7 @@ - + @@ -12251,7 +12272,7 @@ - + @@ -12380,13 +12401,13 @@ - + - - + + @@ -12394,8 +12415,8 @@ - - + + @@ -13787,7 +13808,9 @@ - + + + @@ -13796,6 +13819,30 @@ + + + + + + + + + + + + + + + + + + + + + + + +