diff --git a/include/ifiltermenu.h b/include/ifiltermenu.h index 91bc4610c3..6365c5c8d1 100644 --- a/include/ifiltermenu.h +++ b/include/ifiltermenu.h @@ -12,10 +12,7 @@ namespace ui * A class representing a Filters submenu, with a proper * getWidget() method for packing it into a parent container. * - * Upon construction, the menu will be registered in the - * global MenuManager. The destructor will remove it from there, - * so the client needs to prevent this class from getting - * out of scope too soon. + * It's the caller's responsibility to delete the object. * * Use the GlobalUIManager() interface to acquire * a new instance of this filter menu. @@ -28,6 +25,7 @@ class IFilterMenu // Constructs and returns the widget of a full filters menu // including submenu and the items. This can be packed into an // existing menu bar or toolitem right away. + // Caller is responsible of deleting the menu! virtual wxMenu* getMenuWidget() = 0; }; typedef std::shared_ptr IFilterMenuPtr; diff --git a/include/ilayer.h b/include/ilayer.h index b2c258f935..393eb23071 100644 --- a/include/ilayer.h +++ b/include/ilayer.h @@ -4,6 +4,7 @@ #include #include #include "imodule.h" +#include namespace scene { @@ -178,6 +179,17 @@ class ILayerSystem : * should be de-selected. */ virtual void setSelected(int layerID, bool selected) = 0; + + /** + * A signal for client code to get notified about layer creation, + * addition, removal. + */ + virtual sigc::signal signal_layersChanged() = 0; + + /** + * + */ + virtual sigc::signal signal_layerVisibilityChanged() = 0; }; } // namespace scene diff --git a/include/imap.h b/include/imap.h index 62da4166ff..999bdddcc5 100644 --- a/include/imap.h +++ b/include/imap.h @@ -66,6 +66,7 @@ class IMap : MapLoaded, // emitted when the current map is done loading MapUnloading, // emitted just before a map is unloaded from memory MapUnloaded, // emitted after a map has been unloaded + MapSaved, // emitted right after a map has been saved }; typedef sigc::signal MapEventSignal; diff --git a/include/imediabrowser.h b/include/imediabrowser.h new file mode 100644 index 0000000000..bbfcc98415 --- /dev/null +++ b/include/imediabrowser.h @@ -0,0 +1,57 @@ +#pragma once + +#include "imodule.h" +#include + +namespace ui +{ + +/** + * Interface to the MediaBrowser, which is displaying the + * available materials, accessible as a page in the + * GroupDialog's notebook. + */ +class IMediaBrowser : + public RegisterableModule +{ +public: + virtual ~IMediaBrowser() + {} + + /** + * Returns the texture name of the currently selected item. + * Will return an empty string if a folder is selected or + * nothing is selected at all. + */ + virtual std::string getSelection() = 0; + + /** Set the given path as the current selection, highlighting it + * in the tree view. + * + * @param selection + * The fullname of the item to select, or the empty string if there + * should be no selection. + */ + virtual void setSelection(const std::string& selection) = 0; + + // The tab name as registred in the GroupDialog + const char* const getGroupDialogTabName() const + { + return "mediabrowser"; + } +}; + +} + +const char* const MODULE_MEDIABROWSER = "MediaBrowser"; + +inline ui::IMediaBrowser& GlobalMediaBrowser() +{ + // Cache the reference locally + static ui::IMediaBrowser& _mediaBrowser( + *std::static_pointer_cast( + module::GlobalModuleRegistry().getModule(MODULE_MEDIABROWSER) + ) + ); + return _mediaBrowser; +} diff --git a/include/iuimanager.h b/include/iuimanager.h index 196a79341f..4da60d5254 100644 --- a/include/iuimanager.h +++ b/include/iuimanager.h @@ -5,8 +5,8 @@ // Forward declarations class wxWindow; -class wxObject; class wxToolBar; +class wxMenuBar; class IColourSchemeManager { public: @@ -32,8 +32,6 @@ namespace ui }; } // namespace ui -/** greebo: Implementation documentation: see MenuManager.h. - */ class IMenuManager { public: @@ -41,13 +39,10 @@ class IMenuManager */ virtual ~IMenuManager() {} - /** greebo: Retrieves the menuitem widget specified by the path. - * - * Example: get("main/file/open") delivers the widget for the "Open..." command. - * - * @returns: the widget, or NULL, if no the path hasn't been found. + /** + * Returns the constructed menu bar, ready for packing into a parent container. */ - virtual wxObject* get(const std::string& path) = 0; + virtual wxMenuBar* getMenuBar(const std::string& name) = 0; /** greebo: Shows/hides the menuitem under the given path. * @@ -64,11 +59,8 @@ class IMenuManager * @caption: the display string of the menu item (incl. mnemonic) * @icon: the icon filename (can be empty) * @eventname: the event name (e.g. "ToggleShowSizeInfo") - * - * @returns: the menu item wxObject, which might be a wxMenuItem, or - * a wxMenu or wxMenuBar pointer. */ - virtual wxObject* add(const std::string& insertPath, + virtual void add(const std::string& insertPath, const std::string& name, ui::eMenuItemType type, const std::string& caption, @@ -82,17 +74,17 @@ class IMenuManager * @caption: the display string including mnemonic * @icon: the image file name relative to "bitmaps/", can be empty. * @eventName: the event name this item is associated with (can be empty). - * - * @returns: the menu item wxObject, which might be a wxMenuItem, or - * a wxMenu or wxMenuBar pointer. */ - virtual wxObject* insert(const std::string& insertPath, + virtual void insert(const std::string& insertPath, const std::string& name, ui::eMenuItemType type, const std::string& caption, const std::string& icon, const std::string& eventName) = 0; + // Returns true if the given path exists + virtual bool exists(const std::string& path) = 0; + /** * Removes an entire path from the menus. */ diff --git a/include/precompiled_interfaces.h b/include/precompiled_interfaces.h index 30463ed774..288990f527 100644 --- a/include/precompiled_interfaces.h +++ b/include/precompiled_interfaces.h @@ -50,6 +50,7 @@ #include "imapresource.h" #include "imd5anim.h" #include "imd5model.h" +#include "imediabrowser.h" #include "imenu.h" #include "imodel.h" #include "imodelcache.h" diff --git a/install/user.xml b/install/user.xml index 6c4e6ea4d7..ef34a277b3 100644 --- a/install/user.xml +++ b/install/user.xml @@ -109,9 +109,6 @@ - - - diff --git a/libs/wxutil/menu/PopupMenu.cpp b/libs/wxutil/menu/PopupMenu.cpp index 796d8e9d0a..d2e0b37afa 100644 --- a/libs/wxutil/menu/PopupMenu.cpp +++ b/libs/wxutil/menu/PopupMenu.cpp @@ -84,4 +84,12 @@ void PopupMenu::_onItemClick(wxCommandEvent& ev) } } +void PopupMenu::foreachMenuItem(const std::function& functor) +{ + for (const ui::IMenuItemPtr& item : _menuItems) + { + functor(item); + } +} + } // namespace diff --git a/libs/wxutil/menu/PopupMenu.h b/libs/wxutil/menu/PopupMenu.h index c6783bd53f..6eddb8390a 100644 --- a/libs/wxutil/menu/PopupMenu.h +++ b/libs/wxutil/menu/PopupMenu.h @@ -77,6 +77,9 @@ class PopupMenu : * displayed. */ virtual void show(wxWindow* parent); + +protected: + virtual void foreachMenuItem(const std::function& functor); }; typedef std::shared_ptr PopupMenuPtr; diff --git a/libs/wxutil/preview/RenderPreview.cpp b/libs/wxutil/preview/RenderPreview.cpp index acbde4e579..2232cc868b 100644 --- a/libs/wxutil/preview/RenderPreview.cpp +++ b/libs/wxutil/preview/RenderPreview.cpp @@ -48,7 +48,8 @@ RenderPreview::RenderPreview(wxWindow* parent, bool enableAnimation) : _timer(this), _previewWidth(0), _previewHeight(0), - _filtersMenu(GlobalUIManager().createFilterMenu()) + _filtersMenu(GlobalUIManager().createFilterMenu()), + _filterTool(nullptr) { Connect(wxEVT_TIMER, wxTimerEventHandler(RenderPreview::_onFrame), NULL, this); @@ -96,6 +97,8 @@ void RenderPreview::setupToolbars(bool enableAnimation) wxToolBarToolBase* filterTool = filterToolbar->AddTool(wxID_ANY, _("Filters"), wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "iconFilter16.png"), _("Filters"), wxITEM_DROPDOWN); + + // By setting it as dropdown menu the toolitem will take ownership and delete the menu on destruction filterToolbar->SetDropdownMenu(filterTool->GetId(), filterSubmenu); filterToolbar->Realize(); @@ -134,7 +137,7 @@ void RenderPreview::connectToolbarSignals() RenderPreview::~RenderPreview() { - _timer.Stop(); + _timer.Stop(); } void RenderPreview::updateActiveRenderModeButton() diff --git a/libs/wxutil/preview/RenderPreview.h b/libs/wxutil/preview/RenderPreview.h index 43e4d2a095..facb916ee2 100644 --- a/libs/wxutil/preview/RenderPreview.h +++ b/libs/wxutil/preview/RenderPreview.h @@ -16,6 +16,8 @@ #include "render/SceneRenderWalker.h" #include "render/NopVolumeTest.h" +class wxToolBarToolBase; + namespace wxutil { @@ -117,6 +119,7 @@ class RenderPreview : // The filters menu ui::IFilterMenuPtr _filtersMenu; + wxToolBarToolBase* _filterTool; protected: const scene::GraphPtr& getScene(); diff --git a/plugins/uimanager/FilterMenu.cpp b/plugins/uimanager/FilterMenu.cpp index 477e812e0d..baae1d575a 100644 --- a/plugins/uimanager/FilterMenu.cpp +++ b/plugins/uimanager/FilterMenu.cpp @@ -1,60 +1,56 @@ #include "FilterMenu.h" #include "iuimanager.h" -#include "string/convert.h" -#include "i18n.h" +#include "ieventmanager.h" #include +#include "wxutil/menu/IconTextMenuItem.h" + namespace ui { namespace { - // These are used for the general-purpose Filter Menu: - const char* const FILTERS_MENU_BAR = "filters"; - const char* const FILTERS_MENU_FOLDER = "allfilters"; - const char* const FILTERS_MENU_CAPTION = N_("_Filters"); - const char* const MENU_ICON = "iconFilter16.png"; } -std::size_t FilterMenu::_counter = 0; - -FilterMenu::FilterMenu() +FilterMenu::FilterMenu() : + _menu(new wxutil::PopupMenu) { - IMenuManager& menuManager = GlobalUIManager().getMenuManager(); - - // Create a unique name for the menu - _path = FILTERS_MENU_BAR + string::to_string(_counter++); - - // Menu not yet constructed, do it now - _menu = dynamic_cast(menuManager.add("", _path, - menuFolder, _(FILTERS_MENU_CAPTION), "", "")); - assert(_menu != NULL); - - _targetPath = _path; - // Visit the filters in the FilterSystem to populate the menu GlobalFilterSystem().forEachFilter(*this); } FilterMenu::~FilterMenu() { - GlobalUIManager().getMenuManager().remove(_path); + for (auto i : _filterItems) + { + IEventPtr event = GlobalEventManager().findEvent(i.first); + + if (event) + { + event->disconnectMenuItem(i.second); + } + } + + _menu = nullptr; } -// Visitor function void FilterMenu::visit(const std::string& filterName) { - // Get the menu manager - IMenuManager& menuManager = GlobalUIManager().getMenuManager(); + wxMenuItem* item = _menu->Append(new wxutil::IconTextMenuItem(filterName, MENU_ICON)); + item->SetCheckable(true); std::string eventName = GlobalFilterSystem().getFilterEventName(filterName); - // Create the menu item - menuManager.add(_targetPath, _targetPath + "_" + filterName, - menuItem, filterName, - MENU_ICON, eventName); + IEventPtr event = GlobalEventManager().findEvent(eventName); + + if (event) + { + event->connectMenuItem(item); + } + + _filterItems.insert(std::make_pair(eventName, item)); } wxMenu* FilterMenu::getMenuWidget() diff --git a/plugins/uimanager/FilterMenu.h b/plugins/uimanager/FilterMenu.h index c56d72f1ca..e4010d8199 100644 --- a/plugins/uimanager/FilterMenu.h +++ b/plugins/uimanager/FilterMenu.h @@ -1,7 +1,9 @@ #pragma once +#include #include "ifiltermenu.h" #include "ifilter.h" +#include "wxutil/menu/PopupMenu.h" namespace ui { @@ -10,26 +12,19 @@ namespace ui * registers the relevant menuitems on demand. * * Construct a FiltersMenu instance to generate a new Filter Menu which - * can be packed into a parent container widget using the GtkWidget* operator. + * can be packed into a parent container widget using the getMenuWidget(). */ class FilterMenu : public IFilterMenu, public IFilterVisitor { private: - wxMenu* _menu; + std::map _filterItems; - // Static counter to create unique menu bar widgets - static std::size_t _counter; - - // The path of this menu - std::string _path; - - // The target path used for population - std::string _targetPath; + wxutil::PopupMenu* _menu; public: - // Constructs the filters submenu including menu bar + // Constructs the filter items FilterMenu(); ~FilterMenu(); diff --git a/plugins/uimanager/Makefile.am b/plugins/uimanager/Makefile.am index 7ff482836c..80144dd081 100644 --- a/plugins/uimanager/Makefile.am +++ b/plugins/uimanager/Makefile.am @@ -15,19 +15,23 @@ uimanager_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \ $(top_builddir)/libs/math/libmath.la \ $(top_builddir)/libs/scene/libscenegraph.la \ $(top_builddir)/libs/xmlutil/libxmlutil.la -uimanager_la_SOURCES = MenuItem.cpp \ - GroupDialog.cpp \ +uimanager_la_SOURCES = GroupDialog.cpp \ animationpreview/MD5AnimationChooser.cpp \ animationpreview/MD5AnimationViewer.cpp \ animationpreview/AnimationPreview.cpp \ colourscheme/ColourScheme.cpp \ colourscheme/ColourSchemeEditor.cpp \ colourscheme/ColourSchemeManager.cpp \ + menu/MenuBar.cpp \ + menu/MenuElement.cpp \ + menu/MenuFolder.cpp \ + menu/MenuItem.cpp \ + menu/MenuManager.cpp \ + menu/MenuSeparator.cpp \ SoundChooser.cpp \ SoundShaderPreview.cpp \ StatusBarManager.cpp \ DialogManager.cpp \ ToolbarManager.cpp \ UIManager.cpp \ - FilterMenu.cpp \ - MenuManager.cpp + FilterMenu.cpp diff --git a/plugins/uimanager/Makefile.in b/plugins/uimanager/Makefile.in index 28252bdad4..6de51055b6 100644 --- a/plugins/uimanager/Makefile.in +++ b/plugins/uimanager/Makefile.in @@ -131,15 +131,17 @@ uimanager_la_DEPENDENCIES = $(top_builddir)/libs/wxutil/libwxutil.la \ $(top_builddir)/libs/scene/libscenegraph.la \ $(top_builddir)/libs/xmlutil/libxmlutil.la am__dirstamp = $(am__leading_dot)dirstamp -am_uimanager_la_OBJECTS = MenuItem.lo GroupDialog.lo \ +am_uimanager_la_OBJECTS = GroupDialog.lo \ animationpreview/MD5AnimationChooser.lo \ animationpreview/MD5AnimationViewer.lo \ animationpreview/AnimationPreview.lo \ colourscheme/ColourScheme.lo \ colourscheme/ColourSchemeEditor.lo \ - colourscheme/ColourSchemeManager.lo SoundChooser.lo \ + colourscheme/ColourSchemeManager.lo menu/MenuBar.lo \ + menu/MenuElement.lo menu/MenuFolder.lo menu/MenuItem.lo \ + menu/MenuManager.lo menu/MenuSeparator.lo SoundChooser.lo \ SoundShaderPreview.lo StatusBarManager.lo DialogManager.lo \ - ToolbarManager.lo UIManager.lo FilterMenu.lo MenuManager.lo + ToolbarManager.lo UIManager.lo FilterMenu.lo uimanager_la_OBJECTS = $(am_uimanager_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -426,22 +428,26 @@ uimanager_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \ $(top_builddir)/libs/scene/libscenegraph.la \ $(top_builddir)/libs/xmlutil/libxmlutil.la -uimanager_la_SOURCES = MenuItem.cpp \ - GroupDialog.cpp \ +uimanager_la_SOURCES = GroupDialog.cpp \ animationpreview/MD5AnimationChooser.cpp \ animationpreview/MD5AnimationViewer.cpp \ animationpreview/AnimationPreview.cpp \ colourscheme/ColourScheme.cpp \ colourscheme/ColourSchemeEditor.cpp \ colourscheme/ColourSchemeManager.cpp \ + menu/MenuBar.cpp \ + menu/MenuElement.cpp \ + menu/MenuFolder.cpp \ + menu/MenuItem.cpp \ + menu/MenuManager.cpp \ + menu/MenuSeparator.cpp \ SoundChooser.cpp \ SoundShaderPreview.cpp \ StatusBarManager.cpp \ DialogManager.cpp \ ToolbarManager.cpp \ UIManager.cpp \ - FilterMenu.cpp \ - MenuManager.cpp + FilterMenu.cpp all: all-am @@ -539,6 +545,22 @@ colourscheme/ColourSchemeEditor.lo: colourscheme/$(am__dirstamp) \ colourscheme/$(DEPDIR)/$(am__dirstamp) colourscheme/ColourSchemeManager.lo: colourscheme/$(am__dirstamp) \ colourscheme/$(DEPDIR)/$(am__dirstamp) +menu/$(am__dirstamp): + @$(MKDIR_P) menu + @: > menu/$(am__dirstamp) +menu/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) menu/$(DEPDIR) + @: > menu/$(DEPDIR)/$(am__dirstamp) +menu/MenuBar.lo: menu/$(am__dirstamp) menu/$(DEPDIR)/$(am__dirstamp) +menu/MenuElement.lo: menu/$(am__dirstamp) \ + menu/$(DEPDIR)/$(am__dirstamp) +menu/MenuFolder.lo: menu/$(am__dirstamp) \ + menu/$(DEPDIR)/$(am__dirstamp) +menu/MenuItem.lo: menu/$(am__dirstamp) menu/$(DEPDIR)/$(am__dirstamp) +menu/MenuManager.lo: menu/$(am__dirstamp) \ + menu/$(DEPDIR)/$(am__dirstamp) +menu/MenuSeparator.lo: menu/$(am__dirstamp) \ + menu/$(DEPDIR)/$(am__dirstamp) uimanager.la: $(uimanager_la_OBJECTS) $(uimanager_la_DEPENDENCIES) $(EXTRA_uimanager_la_DEPENDENCIES) $(AM_V_CXXLD)$(uimanager_la_LINK) -rpath $(modulesdir) $(uimanager_la_OBJECTS) $(uimanager_la_LIBADD) $(LIBS) @@ -549,6 +571,8 @@ mostlyclean-compile: -rm -f animationpreview/*.lo -rm -f colourscheme/*.$(OBJEXT) -rm -f colourscheme/*.lo + -rm -f menu/*.$(OBJEXT) + -rm -f menu/*.lo distclean-compile: -rm -f *.tab.c @@ -556,8 +580,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DialogManager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FilterMenu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GroupDialog.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MenuItem.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MenuManager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SoundChooser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SoundShaderPreview.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StatusBarManager.Plo@am__quote@ @@ -569,6 +591,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@colourscheme/$(DEPDIR)/ColourScheme.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@colourscheme/$(DEPDIR)/ColourSchemeEditor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@colourscheme/$(DEPDIR)/ColourSchemeManager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@menu/$(DEPDIR)/MenuBar.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@menu/$(DEPDIR)/MenuElement.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@menu/$(DEPDIR)/MenuFolder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@menu/$(DEPDIR)/MenuItem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@menu/$(DEPDIR)/MenuManager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@menu/$(DEPDIR)/MenuSeparator.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @@ -601,6 +629,7 @@ clean-libtool: -rm -rf .libs _libs -rm -rf animationpreview/.libs animationpreview/_libs -rm -rf colourscheme/.libs colourscheme/_libs + -rm -rf menu/.libs menu/_libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique @@ -721,6 +750,8 @@ distclean-generic: -rm -f animationpreview/$(am__dirstamp) -rm -f colourscheme/$(DEPDIR)/$(am__dirstamp) -rm -f colourscheme/$(am__dirstamp) + -rm -f menu/$(DEPDIR)/$(am__dirstamp) + -rm -f menu/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -731,7 +762,7 @@ clean-am: clean-generic clean-libtool clean-modulesLTLIBRARIES \ mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) animationpreview/$(DEPDIR) colourscheme/$(DEPDIR) + -rm -rf ./$(DEPDIR) animationpreview/$(DEPDIR) colourscheme/$(DEPDIR) menu/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -777,7 +808,7 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) animationpreview/$(DEPDIR) colourscheme/$(DEPDIR) + -rm -rf ./$(DEPDIR) animationpreview/$(DEPDIR) colourscheme/$(DEPDIR) menu/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/plugins/uimanager/MenuItem.cpp b/plugins/uimanager/MenuItem.cpp deleted file mode 100644 index 4cd4427c46..0000000000 --- a/plugins/uimanager/MenuItem.cpp +++ /dev/null @@ -1,452 +0,0 @@ -#include "MenuItem.h" - -#include "i18n.h" -#include "itextstream.h" -#include "iradiant.h" -#include "ieventmanager.h" -#include -#include -#include - -#include -#include - -#include - -#include "LocalBitmapArtProvider.h" - -namespace ui -{ - -namespace -{ - typedef std::vector StringVector; -} - -int MenuItem::_nextMenuItemId = 100; - -MenuItem::MenuItem(const MenuItemPtr& parent) : - _parent(MenuItemWeakPtr(parent)), - _widget(NULL), - _type(menuNothing), - _constructed(false) -{ - if (parent == NULL) { - _type = menuRoot; - } - else if (parent->isRoot()) { - _type = menuBar; - } -} - -MenuItem::~MenuItem() -{ - disconnectEvent(); -} - -std::string MenuItem::getName() const { - return _name; -} - -void MenuItem::setName(const std::string& name) { - _name = name; -} - -bool MenuItem::isRoot() const { - return (_type == menuRoot); -} - -MenuItemPtr MenuItem::parent() const { - return _parent.lock(); -} - -void MenuItem::setParent(const MenuItemPtr& parent) { - _parent = parent; -} - -void MenuItem::setCaption(const std::string& caption) { - _caption = caption; -} - -std::string MenuItem::getCaption() const { - return _caption; -} - -void MenuItem::setIcon(const std::string& icon) { - _icon = icon; -} - -bool MenuItem::isEmpty() const { - return (_type != menuItem); -} - -eMenuItemType MenuItem::getType() const { - return _type; -} - -void MenuItem::setType(eMenuItemType type) { - _type = type; -} - -std::size_t MenuItem::numChildren() const { - return _children.size(); -} - -void MenuItem::addChild(const MenuItemPtr& newChild) { - _children.push_back(newChild); -} - -void MenuItem::removeChild(const MenuItemPtr& child) -{ - for (MenuItemList::iterator i = _children.begin(); i != _children.end(); ++i) - { - if (*i == child) - { - _children.erase(i); - return; - } - } -} - -void MenuItem::removeAllChildren() -{ - _children.clear(); -} - -std::string MenuItem::getEvent() const -{ - return _event; -} - -void MenuItem::setEvent(const std::string& eventName) -{ - _event = eventName; -} - -int MenuItem::getMenuPosition(const MenuItemPtr& child) -{ - if (!_constructed) - { - construct(); - } - - // Check if this is the right item type for this operation - if (_type == menuFolder) - { - // A menufolder is a menuitem with a contained submenu, retrieve it - wxMenu* container = dynamic_cast(_widget); - - // Get the list of child widgets - wxMenuItemList& children = container->GetMenuItems(); - - // The child Widget for comparison - wxObject* childWidget = child->getWidget(); - - int position = 0; - for (wxMenuItemList::const_iterator i = children.begin(); i != children.end(); ++i, ++position) - { - // Get the widget pointer from the current list item - wxMenuItem* item = *i; - - if (item == childWidget || - (child->getType() == menuFolder && item->GetSubMenu() == childWidget)) - { - return position; - } - } - } - else if (_type == menuBar) - { - wxMenuBar* container = dynamic_cast(_widget); - - if (container == NULL) - { - rWarning() << "Cannot find menu position, cannot cast to wxMenuBar." << std::endl; - return -1; - } - - // The child Widget for comparison - wxObject* childWidget = child->getWidget(); - - // Iterate over all registered menus - for (int position = 0; position < container->GetMenuCount(); ++position) - { - // Get the widget pointer from the current list item - if (container->GetMenu(position) == childWidget) - { - return position; - } - } - } - - return -1; // not found or wrong type -} - -wxObject* MenuItem::getWidget() -{ - // Check for toggle, allocate the Gtk::Widget* - if (!_constructed) - { - construct(); - } - - return _widget; -} - -MenuItemPtr MenuItem::find(const std::string& menuPath) -{ - // Split the path and analyse it - StringVector parts; - boost::algorithm::split(parts, menuPath, boost::algorithm::is_any_of("/")); - - // Any path items at all? - if (!parts.empty()) { - MenuItemPtr child; - - // Path is not empty, try to find the first item among the item's children - for (std::size_t i = 0; i < _children.size(); i++) { - if (_children[i]->getName() == parts[0]) { - child = _children[i]; - break; - } - } - - // The topmost name seems to be part of the children, pass the call - if (child != NULL) { - // Is this the end of the path (no more items)? - if (parts.size() == 1) { - // Yes, return the found item - return child; - } - else { - // No, pass the query down the hierarchy - std::string childPath(""); - for (std::size_t i = 1; i < parts.size(); i++) { - childPath += (childPath != "") ? "/" : ""; - childPath += parts[i]; - } - return child->find(childPath); - } - } - } - - // Nothing found, return NULL pointer - return MenuItemPtr(); -} - -void MenuItem::parseNode(xml::Node& node, const MenuItemPtr& thisItem) { - std::string nodeName = node.getName(); - - setName(node.getAttributeValue("name")); - - // Put the caption through gettext before passing it to setCaption - setCaption(_(node.getAttributeValue("caption").c_str())); - - if (nodeName == "menuItem") { - _type = menuItem; - // Get the EventPtr according to the event - setEvent(node.getAttributeValue("command")); - setIcon(node.getAttributeValue("icon")); - } - else if (nodeName == "menuSeparator") { - _type = menuSeparator; - } - else if (nodeName == "subMenu") { - _type = menuFolder; - - xml::NodeList subNodes = node.getChildren(); - for (std::size_t i = 0; i < subNodes.size(); i++) { - if (subNodes[i].getName() != "text" && subNodes[i].getName() != "comment") { - // Allocate a new child menuitem with a pointer to - MenuItemPtr newChild = MenuItemPtr(new MenuItem(thisItem)); - // Let the child parse the subnode - newChild->parseNode(subNodes[i], newChild); - - // Add the child to the list - _children.push_back(newChild); - } - } - } - else if (nodeName == "menu") { - _type = menuBar; - - xml::NodeList subNodes = node.getChildren(); - for (std::size_t i = 0; i < subNodes.size(); i++) { - if (subNodes[i].getName() != "text" && subNodes[i].getName() != "comment") { - // Allocate a new child menuitem with a pointer to - MenuItemPtr newChild = MenuItemPtr(new MenuItem(thisItem)); - // Let the child parse the subnode - newChild->parseNode(subNodes[i], newChild); - - // Add the child to the list - _children.push_back(newChild); - } - } - } - else { - _type = menuNothing; - rError() << "MenuItem: Unknown node found: " << nodeName << std::endl; - } -} - -void MenuItem::construct() -{ - if (_type == menuBar) - { - wxMenuBar* menuBar = new wxMenuBar; - _widget = menuBar; - - for (std::size_t i = 0; i < _children.size(); i++) - { - // Cast each children onto wxMenu and append it to the menu - wxMenu* menu = dynamic_cast(_children[i]->getWidget()); - - if (menuItem != NULL) - { - menuBar->Append(menu, _children[i]->getCaption()); - } - else - { - rError() << "MenuItem::construct: Cannot cast child to wxMenu" << std::endl; - } - } - } - else if (_type == menuSeparator) - { - _widget = NULL; // separator is handled when adding to the parent menu itself - } - else if (_type == menuFolder) - { - // Create the menuitem, don't pass a title to the constructor, - // otherwise the caption gets immediately added as first child - wxMenu* menu = new wxMenu; - _widget = menu; - - for (std::size_t i = 0; i < _children.size(); i++) - { - if (_children[i]->getType() == menuSeparator) - { - _children[i]->_widget = menu->AppendSeparator(); - continue; - } - - wxMenuItem* menuItem = dynamic_cast(_children[i]->getWidget()); - - if (menuItem != NULL) - { - menu->Append(menuItem); - - if (_children[i]->getEvent().empty()) - { - // No event attached to this menu item, disable it - menu->Enable(menuItem->GetId(), false); - continue; - } - - // Now is the time to connect the event, the item has a valid menu parent at this point - IEventPtr event = GlobalEventManager().findEvent(_children[i]->getEvent()); - - if (event != NULL) - { - event->connectMenuItem(menuItem); - } - - continue; - } - - wxMenu* subMenu = dynamic_cast(_children[i]->getWidget()); - - if (subMenu != NULL) - { - menu->AppendSubMenu(subMenu, _children[i]->getCaption()); - continue; - } - } - } - else if (_type == menuItem) - { - if (!_event.empty()) - { - // Try to lookup the event name - IEventPtr event = GlobalEventManager().findEvent(_event); - - if (!event->empty()) - { - // Retrieve an accelerator string formatted for a menu - const std::string accelText = - GlobalEventManager().getAcceleratorStr(event, true); - - // Create a new menuitem - // greebo: Accelerators seem to globally catch the key events, add a space to fool wxWidgets - wxMenuItem* item = new wxMenuItem(NULL, _nextMenuItemId++, _caption + "\t " + accelText); - _widget = item; - - if (!_icon.empty()) - { - item->SetBitmap(wxArtProvider::GetBitmap(LocalBitmapArtProvider::ArtIdPrefix() + _icon)); - } - - item->SetCheckable(event->isToggle()); - } - else - { - rWarning() << "MenuItem: Cannot find associated event: " << _event << std::endl; - } - } - else - { - // Create an empty, desensitised menuitem, will be disabled once attached to the parent menu - wxMenuItem* item = new wxMenuItem(NULL, _nextMenuItemId++, _caption.empty() ? "-" : _caption); - - _widget = item; - } - } - else if (_type == menuRoot) - { - // Cannot instantiate root MenuItem, ignore - } - - _constructed = true; -} - -void MenuItem::connectEvent() -{ - if (!_event.empty() && _type == menuItem) - { - // Try to lookup the event name - IEventPtr event = GlobalEventManager().findEvent(_event); - wxMenuItem* menuItem = dynamic_cast(_widget); - - if (event != NULL && menuItem != NULL) - { - event->connectMenuItem(menuItem); - } - } -} - -void MenuItem::disconnectEvent() -{ - if (!_event.empty() && _type == menuItem) - { - IEventPtr ev = GlobalEventManager().findEvent(_event); - wxMenuItem* item = dynamic_cast(_widget); - - // Tell the eventmanager to disconnect the widget in any case - // even if has been destroyed already. - if (ev != NULL && item != NULL) - { - ev->disconnectMenuItem(item); - } - } -} - -void MenuItem::setWidget(wxObject* object) -{ - // Disconnect the old widget before setting a new one - disconnectEvent(); - - _widget = object; - _constructed = true; -} - -} // namespace ui diff --git a/plugins/uimanager/MenuItem.h b/plugins/uimanager/MenuItem.h deleted file mode 100644 index 34c778b998..0000000000 --- a/plugins/uimanager/MenuItem.h +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once - -#include "xmlutil/Node.h" -#include "iuimanager.h" -#include -#include - -/** greebo: This is a representation of a general menu item/element. - * - * The possible menuitem types are defined in iuimanager.h. - * Each menu item can have a list of sub-menuitems (this applies to the - * types eMenuBar and eFolder). - * - * Use the MenuManager class to access these menuitems. - */ -namespace ui -{ - -class MenuItem; -typedef std::shared_ptr MenuItemPtr; -typedef std::weak_ptr MenuItemWeakPtr; - -class MenuItem -{ - // The parent of this MenuItem (weak reference to avoid circular ownership) - MenuItemWeakPtr _parent; - - // The name of this node - std::string _name; - - // The caption (display string) incl. the mnemonic - std::string _caption; - - // The icon name - std::string _icon; - - // The associated event - std::string _event; - - wxObject* _widget; - - // The children of this MenuItem - typedef std::vector MenuItemList; - MenuItemList _children; - - eMenuItemType _type; - - // Stays false until the widgets are actually created. - bool _constructed; - - static int _nextMenuItemId; - -public: - // Constructor, needs a name and a parent specified - MenuItem(const MenuItemPtr& parent); - - // Destructor disconnects the widget from the event - ~MenuItem(); - - // The name of this MenuItem - std::string getName() const; - void setName(const std::string& name); - - void setIcon(const std::string& icon); - - // Returns TRUE if this item has no parent item - bool isRoot() const; - - // Returns the pointer to the parent (is NULL for the root item) - MenuItemPtr parent() const; - void setParent(const MenuItemPtr& parent); - - /** greebo: Adds the given menuitem to the list of children. - * - * Note: the new child is NOT reparented, the calling function must to this. - */ - void addChild(const MenuItemPtr& newChild); - - /** - * Removes the given child from this menu item. - */ - void removeChild(const MenuItemPtr& child); - - // Removes all child nodes - void removeAllChildren(); - - /** greebo: Tries to find the GtkMenu position index of the given child. - */ - int getMenuPosition(const MenuItemPtr& child); - - // Returns the type of this item node - eMenuItemType getType() const; - void setType(eMenuItemType type); - - // Gets/sets the caption of this item - void setCaption(const std::string& caption); - std::string getCaption() const; - - // Returns TRUE if this has no actual event assigned - bool isEmpty() const; - - // Returns the number of child items - std::size_t numChildren() const; - - // Return / set the event name - std::string getEvent() const; - void setEvent(const std::string& eventName); - - void connectEvent(); - void disconnectEvent(); - - // Use this to get the corresponding wx menu widget out of this item. - wxObject* getWidget(); - void setWidget(wxObject* object); - - // Tries to (recursively) locate the menuitem by looking up the path - MenuItemPtr find(const std::string& menuPath); - - /** greebo: Takes the given xml::Node and populates this item recursively. - * Pass the MenuItemPtr to this object when calling it, so that - * the item can pass it to its children. - * At least I don't know of any other method right now to - * create a "self" shared_ptr from itself. - */ - void parseNode(xml::Node& node, const MenuItemPtr& thisItem); - -private: - /** greebo: This constructs the actual widgets. This is invoked as soon - * as the first getWidget of this object is requested. - */ - void construct(); -}; - -} // namespace ui diff --git a/plugins/uimanager/UIManager.cpp b/plugins/uimanager/UIManager.cpp index 201b98d782..67882b25b8 100644 --- a/plugins/uimanager/UIManager.cpp +++ b/plugins/uimanager/UIManager.cpp @@ -128,10 +128,7 @@ void UIManager::initialiseModule(const ApplicationContext& ctx) void UIManager::shutdownModule() { - // Free the XmlResources instance - // (I intermittently hit debug heap breakpoints when wx is - // deleting XRC XML resource memory, so let's attempt to fix that this way.) - //delete wxXmlResource::Set(nullptr); + _menuManager.clear(); } } // namespace ui diff --git a/plugins/uimanager/UIManager.h b/plugins/uimanager/UIManager.h index 4c3aa2d39c..1e5cd042ab 100644 --- a/plugins/uimanager/UIManager.h +++ b/plugins/uimanager/UIManager.h @@ -5,7 +5,7 @@ #include "iuimanager.h" #include "idialogmanager.h" -#include "MenuManager.h" +#include "menu/MenuManager.h" #include "ToolbarManager.h" #include "StatusBarManager.h" #include "DialogManager.h" diff --git a/plugins/uimanager/menu/MenuBar.cpp b/plugins/uimanager/menu/MenuBar.cpp new file mode 100644 index 0000000000..ea81ec87ed --- /dev/null +++ b/plugins/uimanager/menu/MenuBar.cpp @@ -0,0 +1,92 @@ +#include "MenuBar.h" + +#include +#include +#include + +#include +#include "MenuFolder.h" + +namespace ui +{ + +MenuBar::MenuBar() : + _menuBar(nullptr) +{} + +wxMenuBar* MenuBar::getMenuBar() +{ + if (_menuBar == nullptr) + { + construct(); + } + + return _menuBar; +} + +void MenuBar::construct() +{ + _needsRefresh = false; + + if (_menuBar != nullptr) + { + MenuElement::constructChildren(); + return; + } + + _menuBar = new wxMenuBar; + + // Set up the listener ensuring that the opened menu is up to date + _menuBar->Bind(wxEVT_MENU_OPEN, sigc::mem_fun(this, &MenuBar::onMenuOpen)); + + MenuElement::constructChildren(); +} + +void MenuBar::deconstruct() +{ + MenuElement::deconstructChildren(); + + if (_menuBar != nullptr) + { + // Any parent frame needs to know about the destruction beforehand + // deleting the wxMenuBar object doesn't notify its parent + if (_menuBar->GetFrame()) + { + _menuBar->GetFrame()->SetMenuBar(nullptr); + } + + delete _menuBar; + _menuBar = nullptr; + } +} + +MenuElementPtr MenuBar::findMenu(wxMenu* menu) +{ + for (const MenuElementPtr& candidate : _children) + { + if (!std::dynamic_pointer_cast(candidate)) continue; + + if (std::static_pointer_cast(candidate)->getMenu() == menu) + { + return candidate; + } + } + + return MenuElementPtr(); +} + +void MenuBar::onMenuOpen(wxMenuEvent& ev) +{ + // Block redraws for the moment being + wxWindowUpdateLocker noUpdates(_menuBar); + + MenuElementPtr menu = findMenu(ev.GetMenu()); + + if (menu && menu->needsRefresh() && std::dynamic_pointer_cast(menu)) + { + // Rebuild this entire menu + std::static_pointer_cast(menu)->refresh(); + } +} + +} diff --git a/plugins/uimanager/menu/MenuBar.h b/plugins/uimanager/menu/MenuBar.h new file mode 100644 index 0000000000..672e2ea645 --- /dev/null +++ b/plugins/uimanager/menu/MenuBar.h @@ -0,0 +1,30 @@ +#pragma once + +#include "MenuElement.h" +#include + +namespace ui +{ + +class MenuBar : + public MenuElement +{ +private: + wxMenuBar* _menuBar; + +public: + MenuBar(); + + virtual wxMenuBar* getMenuBar(); + +protected: + virtual void construct() override; + virtual void deconstruct() override; + +private: + MenuElementPtr findMenu(wxMenu* menu); + void onMenuOpen(wxMenuEvent& ev); +}; + +} + diff --git a/plugins/uimanager/menu/MenuElement.cpp b/plugins/uimanager/menu/MenuElement.cpp new file mode 100644 index 0000000000..4fac467302 --- /dev/null +++ b/plugins/uimanager/menu/MenuElement.cpp @@ -0,0 +1,331 @@ +#include "MenuElement.h" + +#include "i18n.h" +#include "itextstream.h" +#include "ieventmanager.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "MenuBar.h" +#include "MenuFolder.h" +#include "MenuItem.h" +#include "MenuSeparator.h" + +namespace ui +{ + +int MenuElement::_nextMenuItemId = 100; + +MenuElement::MenuElement(const MenuElementPtr& parent) : + _parent(parent ? MenuElementWeakPtr(parent) : MenuElementWeakPtr()), + _type(menuNothing), + _isVisible(true), + _needsRefresh(false) +{} + +MenuElement::~MenuElement() +{} + +std::string MenuElement::getName() const +{ + return _name; +} + +void MenuElement::setName(const std::string& name) +{ + _name = name; +} + +bool MenuElement::isRoot() const +{ + return (_type == menuRoot); +} + +MenuElementPtr MenuElement::getParent() const +{ + return _parent.lock(); +} + +void MenuElement::setParent(const MenuElementPtr& parent) +{ + _parent = parent; +} + +void MenuElement::setCaption(const std::string& caption) +{ + _caption = caption; +} + +std::string MenuElement::getCaption() const +{ + return _caption; +} + +void MenuElement::setIcon(const std::string& icon) +{ + _icon = icon; +} + +bool MenuElement::isEmpty() const +{ + return (_type != menuItem); +} + +eMenuItemType MenuElement::getType() const +{ + return _type; +} + +void MenuElement::setType(eMenuItemType type) +{ + _type = type; +} + +std::size_t MenuElement::numChildren() const +{ + return _children.size(); +} + +bool MenuElement::isVisible() const +{ + return _isVisible; +} + +void MenuElement::setIsVisible(bool visible) +{ + _isVisible = visible; +} + +void MenuElement::addChild(const MenuElementPtr& newChild) +{ + addChild(newChild, std::numeric_limits::max()); +} + +void MenuElement::addChild(const MenuElementPtr& newChild, int pos) +{ + newChild->setParent(shared_from_this()); + + if (pos >= _children.size() || pos == std::numeric_limits::max()) + { + _children.push_back(newChild); + } + else + { + _children.insert(_children.begin() + pos, newChild); + } +} + +void MenuElement::removeChild(const MenuElementPtr& child) +{ + for (MenuElementList::iterator i = _children.begin(); i != _children.end(); ++i) + { + if (*i == child) + { + // Deconstruct the child before removal + child->deconstruct(); + + // Release from parent and remove from the list + child->setParent(MenuElementPtr()); + _children.erase(i); + break; + } + } +} + +void MenuElement::removeAllChildren() +{ + for (const MenuElementPtr& child : _children) + { + child->setParent(MenuElementPtr()); + } + + _children.clear(); +} + +std::string MenuElement::getEvent() const +{ + return _event; +} + +void MenuElement::setEvent(const std::string& eventName) +{ + _event = eventName; +} + +int MenuElement::getMenuPosition(const MenuElementPtr& child) +{ + return static_cast(std::distance(_children.begin(), + std::find(_children.begin(), _children.end(), child))); +} + +MenuElementPtr MenuElement::find(const std::string& menuPath) +{ + // Split the path and analyse it + std::list parts; + boost::algorithm::split(parts, menuPath, boost::algorithm::is_any_of("/")); + + // Any path items at all? + if (parts.empty()) return MenuElementPtr(); + + // Path is not empty, try to find the first item among the item's children + for (const MenuElementPtr& candidate : _children) + { + if (candidate->getName() == parts.front()) + { + // Remove the first part, it has been processed + parts.pop_front(); + + // Is this the end of the path (no more items)? + if (parts.empty()) + { + // Yes, return the found item + return candidate; + } + + // No, pass the query down the hierarchy + std::string childPath = boost::algorithm::join(parts, "/"); + + return candidate->find(childPath); + } + } + + // Nothing found + return MenuElementPtr(); +} + +bool MenuElement::needsRefresh() +{ + return _needsRefresh; +} + +void MenuElement::setNeedsRefresh(bool needsRefresh) +{ + _needsRefresh = needsRefresh; +} + +MenuElementPtr MenuElement::CreateFromNode(const xml::Node& node) +{ + MenuElementPtr item; + + std::string nodeName = node.getName(); + + if (nodeName == "menuItem") + { + item = std::make_shared(); + + item->setType(menuItem); + + // Get the EventPtr according to the event + item->setEvent(node.getAttributeValue("command")); + item->setIcon(node.getAttributeValue("icon")); + } + else if (nodeName == "menuSeparator") + { + item = std::make_shared(); + } + else if (nodeName == "subMenu") + { + item = std::make_shared(); + } + else if (nodeName == "menu") + { + item = std::make_shared(); + } + else + { + rError() << "MenuElement: Unknown node found: " << node.getName() << std::endl; + return item; + } + + item->setName(node.getAttributeValue("name")); + + // Put the caption through gettext before passing it to setCaption + item->setCaption(_(node.getAttributeValue("caption").c_str())); + + // Parse subnodes + xml::NodeList childNodes = node.getChildren(); + + for (const xml::Node& childNode : childNodes) + { + if (childNode.getName() == "text" || childNode.getName() == "comment") + { + continue; + } + + // Allocate a new child item + MenuElementPtr childItem = CreateFromNode(childNode); + + // Add the child to the list + if (childItem) + { + item->addChild(childItem); + } + } + + return item; +} + +MenuElementPtr MenuElement::CreateForType(eMenuItemType type) +{ + MenuElementPtr item; + + switch (type) + { + case menuItem: + item = std::make_shared(); + break; + case menuBar: + item = std::make_shared(); + break; + case menuFolder: + item = std::make_shared(); + break; + case menuSeparator: + item = std::make_shared(); + break; + default: + rError() << "MenuElement: Cannot create node for type " << type << std::endl; + }; + + item->setType(type); + + return item; +} + +void MenuElement::setNeedsRefreshRecursively(bool needsRefresh) +{ + setNeedsRefresh(needsRefresh); + + for (const MenuElementPtr& child : _children) + { + child->setNeedsRefreshRecursively(needsRefresh); + } +} + +void MenuElement::constructChildren() +{ + for (const MenuElementPtr& child : _children) + { + child->construct(); + } +} + +void MenuElement::deconstructChildren() +{ + for (const MenuElementPtr& child : _children) + { + child->deconstruct(); + } +} + +} // namespace ui diff --git a/plugins/uimanager/menu/MenuElement.h b/plugins/uimanager/menu/MenuElement.h new file mode 100644 index 0000000000..bec5dea0fe --- /dev/null +++ b/plugins/uimanager/menu/MenuElement.h @@ -0,0 +1,157 @@ +#pragma once + +#include "xmlutil/Node.h" +#include "iuimanager.h" +#include +#include + +/** greebo: This is a representation of a general menu item/element. + * + * The possible menuitem types are defined in iuimanager.h. + * Each menu item can have a list of sub-MenuElements (this applies to the + * types eMenuBar and eFolder). + * + * Use the MenuManager class to access these MenuElements. + */ +namespace ui +{ + +class MenuElement; +typedef std::shared_ptr MenuElementPtr; +typedef std::weak_ptr MenuElementWeakPtr; + +class MenuElement : + public std::enable_shared_from_this +{ +protected: + // The parent of this MenuElement (weak reference to avoid circular ownership) + MenuElementWeakPtr _parent; + + // The name of this node + std::string _name; + + // The caption (display string) incl. the mnemonic + std::string _caption; + + // The icon name + std::string _icon; + + // The associated event name + std::string _event; + + // The children of this MenuElement + typedef std::vector MenuElementList; + MenuElementList _children; + + eMenuItemType _type; + + bool _isVisible; + + // Checked if anything in this item or below has changed + // and needs reconstruction the next time the menu is opened + bool _needsRefresh; + + static int _nextMenuItemId; + +public: + // Constructor, needs a name and a parent specified + MenuElement(const MenuElementPtr& parent = MenuElementPtr()); + + // Destructor disconnects the widget from the event + ~MenuElement(); + + // The name of this MenuElement + std::string getName() const; + void setName(const std::string& name); + + void setIcon(const std::string& icon); + + // Returns TRUE if this item has no parent item + bool isRoot() const; + + // Returns the pointer to the parent (is NULL for the root item) + MenuElementPtr getParent() const; + void setParent(const MenuElementPtr& parent); + + bool isVisible() const; + void setIsVisible(bool visible); + + /** + * greebo: Adds the given MenuElement at the end of the child list. + */ + void addChild(const MenuElementPtr& newChild); + + /** + * greebo: Adds the given MenuElement to the list of children. + * @pos: the position this element is inserted at. + */ + void addChild(const MenuElementPtr& newChild, int pos); + + /** + * Removes the given child from this menu item. + */ + void removeChild(const MenuElementPtr& child); + + // Removes all child nodes + void removeAllChildren(); + + /** greebo: Tries to find the GtkMenu position index of the given child. + */ + int getMenuPosition(const MenuElementPtr& child); + + // Returns the type of this item node + eMenuItemType getType() const; + void setType(eMenuItemType type); + + // Gets/sets the caption of this item + void setCaption(const std::string& caption); + std::string getCaption() const; + + // Returns TRUE if this has no actual event assigned + bool isEmpty() const; + + // Returns the number of child items + std::size_t numChildren() const; + + // Return / set the event name + std::string getEvent() const; + void setEvent(const std::string& eventName); + + bool needsRefresh(); + void setNeedsRefresh(bool needsRefresh); + + // Tries to (recursively) locate the MenuElement by looking up the path + MenuElementPtr find(const std::string& menuPath); + + /** + * Parses the given XML node recursively and creates all items from the + * information it finds. Returns the constructed MenuElement. + */ + static MenuElementPtr CreateFromNode(const xml::Node& node); + + /** + * Constructs a menu element for the given type + */ + static MenuElementPtr CreateForType(eMenuItemType type); + +protected: + void setNeedsRefreshRecursively(bool needsRefresh); + + // Instantiates this all current child elements recursively as wxObjects + // to be overridden by subclasses + virtual void construct() = 0; + + // Destroys the wxWidget instantiation of this element and all children + // (which also nullifies the widget pointers). Next time + // the getWidget() method is called the widgets will be reconstructed. + // Subclasses should override this + virtual void deconstruct() = 0; + + // This default implementaton here does as exepected: constructs all children + virtual void constructChildren(); + + // This default implementation passes the deconstruct() call to all children + virtual void deconstructChildren(); +}; + +} // namespace ui diff --git a/plugins/uimanager/menu/MenuFolder.cpp b/plugins/uimanager/menu/MenuFolder.cpp new file mode 100644 index 0000000000..9d4a30823a --- /dev/null +++ b/plugins/uimanager/menu/MenuFolder.cpp @@ -0,0 +1,119 @@ +#include "MenuFolder.h" + +#include "itextstream.h" +#include +#include + +#include "MenuBar.h" +#include "MenuFolder.h" + +namespace ui +{ + +MenuFolder::MenuFolder() : + _menu(nullptr), + _parentItem(nullptr) +{} + +wxMenu* MenuFolder::getMenu() +{ + if (_menu == nullptr) + { + construct(); + } + + return _menu; +} + +void MenuFolder::refresh() +{ + deconstructChildren(); + constructChildren(); + + setNeedsRefreshRecursively(false); +} + +void MenuFolder::construct() +{ + if (_menu != nullptr) + { + MenuElement::constructChildren(); + return; + } + + if (isVisible()) + { + // Get the parent menu + MenuElementPtr parent = getParent(); + + if (!parent) + { + rWarning() << "Cannot construct menu without a parent " << std::endl; + return; + } + + _menu = new wxMenu(); + + if (std::dynamic_pointer_cast(parent)) + { + wxMenuBar* bar = std::static_pointer_cast(parent)->getMenuBar(); + bar->Append(_menu, getCaption()); + } + else if (std::dynamic_pointer_cast(parent)) + { + wxMenu* parentMenu = std::static_pointer_cast(parent)->getMenu(); + _parentItem = parentMenu->AppendSubMenu(_menu, getCaption()); + } + } + + MenuElement::constructChildren(); +} + +void MenuFolder::deconstruct() +{ + // Destruct children first + MenuElement::deconstructChildren(); + + if (_parentItem != nullptr) + { + if (_parentItem->GetMenu() != nullptr) + { + // This is a submenu, remove the item we're attached to first + _parentItem->GetMenu()->Delete(_parentItem); + } + else + { + // Unattached parent item, delete it + delete _parentItem; + } + + _parentItem = nullptr; + } + + if (_menu != nullptr) + { + wxMenuBar* menuBar = _menu->GetMenuBar(); + + // Check if we're attached to a menu folder + if (menuBar != nullptr) + { + for (std::size_t i = 0; i < menuBar->GetMenuCount(); ++i) + { + if (menuBar->GetMenu(i) == _menu) + { + // Detach the menu from its bar, caller is responsible of deleting it + menuBar->Remove(i); + break; + } + } + } + } + + // Regardless what happened before, we need to delete the menu ourselves + // (Any parent item didn't delete the _menu in its destructor + // as wxWidgets nullified the submenu member before deleting it) + delete _menu; + _menu = nullptr; +} + +} diff --git a/plugins/uimanager/menu/MenuFolder.h b/plugins/uimanager/menu/MenuFolder.h new file mode 100644 index 0000000000..61a06c7da0 --- /dev/null +++ b/plugins/uimanager/menu/MenuFolder.h @@ -0,0 +1,33 @@ +#pragma once + +#include "MenuElement.h" +#include +#include + +namespace ui +{ + +class MenuFolder : + public MenuElement +{ +private: + wxMenu* _menu; + + // If this is a submenu, we have a parent menu item + wxMenuItem* _parentItem; + +public: + MenuFolder(); + + virtual wxMenu* getMenu(); + + // Empties this menu and rebuilds the wxWidget objects + // Clears the needsRefresh flag on this object and all children + void refresh(); + +protected: + virtual void construct() override; + virtual void deconstruct() override; +}; + +} diff --git a/plugins/uimanager/menu/MenuItem.cpp b/plugins/uimanager/menu/MenuItem.cpp new file mode 100644 index 0000000000..615f5a9bb2 --- /dev/null +++ b/plugins/uimanager/menu/MenuItem.cpp @@ -0,0 +1,120 @@ +#include "MenuItem.h" + +#include "itextstream.h" +#include "ieventmanager.h" + +#include "../LocalBitmapArtProvider.h" +#include "MenuFolder.h" + +namespace ui +{ + +MenuItem::MenuItem() : + _menuItem(nullptr) +{} + +wxMenuItem* MenuItem::getMenuItem() +{ + if (_menuItem == nullptr) + { + construct(); + } + + return _menuItem; +} + +void MenuItem::construct() +{ + if (_menuItem != nullptr) + { + MenuElement::constructChildren(); + return; + } + + if (!isVisible()) + { + MenuElement::constructChildren(); + return; + } + + // Get the parent menu + MenuElementPtr parent = getParent(); + + if (!parent || !std::dynamic_pointer_cast(parent)) + { + rWarning() << "Cannot construct item without a parent menu" << std::endl; + return; + } + + wxMenu* menu = std::static_pointer_cast(parent)->getMenu(); + + std::string caption = _caption; + + // Try to lookup the event name + IEventPtr event = GlobalEventManager().findEvent(_event); + + if (!event->empty()) + { + // Retrieve an accelerator string formatted for a menu + // greebo: Accelerators seem to globally catch the key events, add a space to fool wxWidgets + caption = _caption + "\t " + GlobalEventManager().getAcceleratorStr(event, true); + } + else + { + rWarning() << "MenuElement: Cannot find associated event: " << _event << std::endl; + } + + // Create a new MenuElement + _menuItem = new wxMenuItem(nullptr, _nextMenuItemId++, caption); + + if (!_icon.empty()) + { + _menuItem->SetBitmap(wxArtProvider::GetBitmap(LocalBitmapArtProvider::ArtIdPrefix() + _icon)); + } + + _menuItem->SetCheckable(event && event->isToggle()); + + menu->Append(_menuItem); + + if (event) + { + event->connectMenuItem(_menuItem); + } + else + { + // No event attached to this menu item, disable it + menu->Enable(_menuItem->GetId(), false); + } + + MenuElement::constructChildren(); +} + +void MenuItem::deconstruct() +{ + // Destruct children first + MenuElement::deconstructChildren(); + + if (_menuItem != nullptr) + { + // Try to lookup the event name + if (!_event.empty()) + { + IEventPtr event = GlobalEventManager().findEvent(_event); + + if (event) + { + event->disconnectMenuItem(_menuItem); + } + } + + if (_menuItem->GetMenu() != nullptr) + { + _menuItem->GetMenu()->Remove(_menuItem); + } + + delete _menuItem; + _menuItem = nullptr; + } +} + +} diff --git a/plugins/uimanager/menu/MenuItem.h b/plugins/uimanager/menu/MenuItem.h new file mode 100644 index 0000000000..230a289ec3 --- /dev/null +++ b/plugins/uimanager/menu/MenuItem.h @@ -0,0 +1,26 @@ +#pragma once + +#include "MenuElement.h" + +#include + +namespace ui +{ + +class MenuItem : + public MenuElement +{ +private: + wxMenuItem* _menuItem; + +public: + MenuItem(); + + virtual wxMenuItem* getMenuItem(); + +protected: + virtual void construct() override; + virtual void deconstruct() override; +}; + +} diff --git a/plugins/uimanager/MenuManager.cpp b/plugins/uimanager/menu/MenuManager.cpp similarity index 64% rename from plugins/uimanager/MenuManager.cpp rename to plugins/uimanager/menu/MenuManager.cpp index 524cd57abe..3280780d7a 100644 --- a/plugins/uimanager/MenuManager.cpp +++ b/plugins/uimanager/menu/MenuManager.cpp @@ -3,12 +3,9 @@ #include "itextstream.h" #include "iregistry.h" -#include -#include - -#include -#include -#include +#include "MenuBar.h" +#include "MenuFolder.h" +#include "MenuRootElement.h" namespace ui { @@ -16,106 +13,124 @@ namespace ui namespace { // The menu root key in the registry - const std::string RKEY_MENU_ROOT = "user/ui/menu"; - const std::string TYPE_ITEM = "item"; - typedef std::vector StringVector; + const char* const RKEY_MENU_ROOT = "user/ui/menu"; } MenuManager::MenuManager() : - _root(new MenuItem(MenuItemPtr())) // Allocate the root item (type is set automatically) + _root(new MenuRootElement()) {} -void MenuManager::clear() { - _root = MenuItemPtr(); +void MenuManager::clear() +{ + _root.reset(); } -void MenuManager::loadFromRegistry() { - xml::NodeList menuNodes = GlobalRegistry().findXPath(RKEY_MENU_ROOT); - - if (!menuNodes.empty()) { - for (std::size_t i = 0; i < menuNodes.size(); i++) { - std::string name = menuNodes[i].getAttributeValue("name"); - - // Allocate a new MenuItem with root as parent - MenuItemPtr menubar = MenuItemPtr(new MenuItem(_root)); - menubar->setName(name); +void MenuManager::loadFromRegistry() +{ + // Clear existing elements first + _root.reset(new MenuRootElement()); - // Populate the root menuitem using the current node - menubar->parseNode(menuNodes[i], menubar); + xml::NodeList menuNodes = GlobalRegistry().findXPath(RKEY_MENU_ROOT); - // Add the menubar as child of the root (child is already parented to _root) + if (!menuNodes.empty()) + { + for (const xml::Node& menuNode : menuNodes) + { + MenuElementPtr menubar = MenuElement::CreateFromNode(menuNode); + + // Add the menubar as child of the root _root->addChild(menubar); } } - else { - rError() << "MenuManager: Could not find menu root in registry.\n"; + else + { + rError() << "MenuManager: Could not find menu root in registry." << std::endl; } } void MenuManager::setVisibility(const std::string& path, bool visible) { // Sanity check for empty menu - if (_root == NULL) return; + if (!_root) return; - MenuItemPtr foundMenu = _root->find(path); + MenuElementPtr element = _root->find(path); - if (foundMenu != NULL) + if (element) { - // Get the Widget* and set the visibility - wxMenuItem* menuitem = dynamic_cast(foundMenu->getWidget()); + element->setIsVisible(visible); - if (menuitem == NULL) - { - return; - } + // The corresponding top level menu needs reconstruction + MenuElementPtr parentMenu = findTopLevelMenu(element); - if (visible) + if (parentMenu) { - menuitem->Enable(true); - } - else { - menuitem->Enable(false); + parentMenu->setNeedsRefresh(true); } } - else { - rError() << "MenuManager: Warning: Menu " << path << " not found!\n"; - } } -wxObject* MenuManager::get(const std::string& path) +wxMenuBar* MenuManager::getMenuBar(const std::string& name) { - // Sanity check for empty menu - if (_root == NULL) return NULL; - - MenuItemPtr foundMenu = _root->find(path); + if (!_root) return nullptr; // root has already been removed - if (foundMenu != NULL) + MenuElementPtr menuBar = _root->find(name); + + if (menuBar) { - return foundMenu->getWidget(); - } - else - { - //rError() << "MenuManager: Warning: Menu " << path.c_str() << " not found!\n"; - return NULL; + assert(std::dynamic_pointer_cast(menuBar)); + + return std::static_pointer_cast(menuBar)->getMenuBar(); } + + rError() << "MenuManager: Warning: Menubar with name " << name << " not found!" << std::endl; + return nullptr; } -wxObject* MenuManager::add(const std::string& insertPath, +void MenuManager::add(const std::string& insertPath, const std::string& name, eMenuItemType type, const std::string& caption, const std::string& icon, const std::string& eventName) { + if (!_root) return; // root has already been removed + + MenuElementPtr parent = _root->find(insertPath); + + if (!parent) + { + rWarning() << "Cannot insert element at non-existent parent " << insertPath << std::endl; + return; + } + + MenuElementPtr element = MenuElement::CreateForType(type); + + element->setName(name); + element->setCaption(caption); + element->setIcon(icon); + element->setEvent(eventName); + + parent->addChild(element); + + // The corresponding top level menu needs reconstruction + MenuElementPtr parentMenu = findTopLevelMenu(element); + + if (parentMenu) + { + parentMenu->setNeedsRefresh(true); + } + + // TODO +#if 0 // Sanity check for empty menu if (_root == NULL) return NULL; - MenuItemPtr found = _root->find(insertPath); + MenuElementPtr found = _root->find(insertPath); if (found != NULL) { // Allocate a new MenuItem - MenuItemPtr newItem = MenuItemPtr(new MenuItem(found)); + MenuElementPtr newItem = MenuElementPtr(new MenuElement(found)); newItem->setName(name); newItem->setCaption(caption); @@ -183,7 +198,7 @@ wxObject* MenuManager::add(const std::string& insertPath, else if (insertPath.empty()) { // We have a new top-level menu item, create it as child of root - MenuItemPtr newItem = MenuItemPtr(new MenuItem(_root)); + MenuElementPtr newItem = MenuElementPtr(new MenuElement(_root)); newItem->setName(name); newItem->setCaption(caption); @@ -201,19 +216,53 @@ wxObject* MenuManager::add(const std::string& insertPath, } return NULL; +#endif } -wxObject* MenuManager::insert(const std::string& insertPath, +void MenuManager::insert(const std::string& insertPath, const std::string& name, eMenuItemType type, const std::string& caption, const std::string& icon, const std::string& eventName) { + if (!_root) return; // root has already been removed + + MenuElementPtr insertBefore = _root->find(insertPath); + + if (!insertBefore || !insertBefore->getParent()) + { + rWarning() << "Cannot insert before non-existent item or item doesn't have a parent" + << insertPath << std::endl; + return; + } + + MenuElementPtr element = MenuElement::CreateForType(type); + + element->setName(name); + element->setCaption(caption); + element->setIcon(icon); + element->setEvent(eventName); + + // Get the Menu position of the child widget + int position = insertBefore->getParent()->getMenuPosition(insertBefore); + + insertBefore->getParent()->addChild(element, position); + + // The corresponding top level menu needs reconstruction + MenuElementPtr parentMenu = findTopLevelMenu(element); + + if (parentMenu) + { + parentMenu->setNeedsRefresh(true); + } + + // TODO +#if 0 // Sanity check for empty menu if (_root == NULL) return NULL; - MenuItemPtr found = _root->find(insertPath); + MenuElementPtr found = _root->find(insertPath); if (found != NULL) { @@ -223,7 +272,7 @@ wxObject* MenuManager::insert(const std::string& insertPath, int position = found->parent()->getMenuPosition(found); // Allocate a new MenuItem - MenuItemPtr newItem = MenuItemPtr(new MenuItem(found->parent())); + MenuElementPtr newItem = MenuElementPtr(new MenuElement(found->parent())); found->parent()->addChild(newItem); // Load the properties into the new child @@ -322,18 +371,53 @@ wxObject* MenuManager::insert(const std::string& insertPath, rError() << "MenuManager: Could not find insertPath: " << insertPath << std::endl; return NULL; } +#endif +} + +bool MenuManager::exists(const std::string& path) +{ + if (!_root) return false; // root has already been removed + + return _root->find(path) != nullptr; } void MenuManager::remove(const std::string& path) { + if (!_root) return; // root has already been removed + + MenuElementPtr element = _root->find(path); + + if (!element) + { + return; // no warning, some code just tries to remove stuff unconditionally + } + + if (!element->getParent()) + { + rWarning() << "Cannot remove item without a parent " << path << std::endl; + return; + } + + element->getParent()->removeChild(element); + + // The corresponding top level menu needs reconstruction + MenuElementPtr parentMenu = findTopLevelMenu(element); + + if (parentMenu) + { + parentMenu->setNeedsRefresh(true); + } + + // TODO +#if 0 // Sanity check for empty menu if (_root == NULL) return; - MenuItemPtr item = _root->find(path); + MenuElementPtr item = _root->find(path); if (item == NULL) return; // nothing to do - MenuItemPtr parent = item->parent(); + MenuElementPtr parent = item->parent(); if (parent == NULL) return; // no parent ? @@ -399,6 +483,28 @@ void MenuManager::remove(const std::string& path) // Remove the found item from the parent menu item parent->removeChild(item); +#endif +} + +MenuElementPtr MenuManager::findTopLevelMenu(const MenuElementPtr& element) +{ + MenuElementPtr candidate = element; + + while (candidate) + { + MenuElementPtr parent = candidate->getParent(); + + if (candidate && + std::dynamic_pointer_cast(candidate) && + std::dynamic_pointer_cast(parent)) + { + return candidate; + } + + candidate = parent; + } + + return MenuElementPtr(); } } // namespace ui diff --git a/plugins/uimanager/MenuManager.h b/plugins/uimanager/menu/MenuManager.h similarity index 53% rename from plugins/uimanager/MenuManager.h rename to plugins/uimanager/menu/MenuManager.h index 21be9dabed..f2ab5fcf34 100644 --- a/plugins/uimanager/MenuManager.h +++ b/plugins/uimanager/menu/MenuManager.h @@ -2,10 +2,10 @@ #include #include "iuimanager.h" -#include "MenuItem.h" -/** greebo: The MenuManager takes care of adding and inserting the - * menuitems at the given paths. +/** + * greebo: The MenuManager takes care of adding and inserting the + * menuitems at the given paths. * * A valid menupath is for example: "main/file/exit" and consists of: * @@ -14,36 +14,28 @@ * The first part of the path is the name of the menubar you want to access. * (the MenuManager can of course keep track of several menubars). * - * Use the add() and insert() commands to create menuitems, the GtkWidget* - * pointers are delivered as return value. - * + * Use the add() and insert() commands to create menuitems. */ -namespace ui { +namespace ui +{ + +class MenuElement; +typedef std::shared_ptr MenuElementPtr; class MenuManager : public IMenuManager { - // The root item containing the menubars as children - MenuItemPtr _root; +private: + // All menubars are child of this root item + MenuElementPtr _root; public: // Constructor, initialises the menu from the registry MenuManager(); - /** greebo: Retrieves the menuitem widget specified by the path. - * - * Example: get("main/file/open") delivers the widget for the "Open..." command. - * - * @returns: the widget, or NULL, if no the path hasn't been found. - */ - wxObject* get(const std::string& path); + wxMenuBar* getMenuBar(const std::string& name) override; - /** greebo: Shows/hides the menuitem under the given path. - * - * @path: the path to the item (e.g. "main/view/cameraview") - * @visible: FALSE, if the widget should be hidden, TRUE otherwise - */ - void setVisibility(const std::string& path, bool visible); + void setVisibility(const std::string& path, bool visible) override; /** greebo: Adds a new item as child under the given path. * @@ -54,12 +46,12 @@ class MenuManager : * @icon: the icon filename (can be empty) * @eventname: the event name (e.g. "ToggleShowSizeInfo") */ - wxObject* add(const std::string& insertPath, - const std::string& name, - ui::eMenuItemType type, - const std::string& caption, - const std::string& icon, - const std::string& eventName); + void add(const std::string& insertPath, + const std::string& name, + eMenuItemType type, + const std::string& caption, + const std::string& icon, + const std::string& eventName) override; /** greebo: Inserts a new menuItem as sibling _before_ the given insertPath. * @@ -69,17 +61,19 @@ class MenuManager : * @icon: the image file name relative to "bitmaps/", can be empty. * @eventName: the event name this item is associated with (can be empty). */ - wxObject* insert(const std::string& insertPath, - const std::string& name, - ui::eMenuItemType type, - const std::string& caption, - const std::string& icon, - const std::string& eventName); + void insert(const std::string& insertPath, + const std::string& name, + eMenuItemType type, + const std::string& caption, + const std::string& icon, + const std::string& eventName) override; + + bool exists(const std::string& path) override; /** * Removes an entire menu subtree. */ - void remove(const std::string& path); + void remove(const std::string& path) override; /** greebo: Loads all the menu items from the registry, called upon initialisation. */ @@ -89,6 +83,10 @@ class MenuManager : * Clears all references to GtkWidgets etc. */ void clear(); + +private: + // Returns the top level menu (== MenuFolder) this element is part of + MenuElementPtr findTopLevelMenu(const MenuElementPtr& element); }; } // namespace ui diff --git a/plugins/uimanager/menu/MenuRootElement.h b/plugins/uimanager/menu/MenuRootElement.h new file mode 100644 index 0000000000..9399baaec8 --- /dev/null +++ b/plugins/uimanager/menu/MenuRootElement.h @@ -0,0 +1,30 @@ +#pragma once + +#include "MenuElement.h" + +namespace ui +{ + +class MenuRootElement : + public MenuElement +{ +public: + MenuRootElement() + { + setType(menuRoot); + } + + ~MenuRootElement() + { + deconstructChildren(); + } + +protected: + void construct() override + {} + + void deconstruct() override + {} +}; + +} diff --git a/plugins/uimanager/menu/MenuSeparator.cpp b/plugins/uimanager/menu/MenuSeparator.cpp new file mode 100644 index 0000000000..d649c1b577 --- /dev/null +++ b/plugins/uimanager/menu/MenuSeparator.cpp @@ -0,0 +1,71 @@ +#include "MenuSeparator.h" + +#include "itextstream.h" +#include + +#include "MenuFolder.h" + +namespace ui +{ + +MenuSeparator::MenuSeparator() : + _separator(nullptr) +{} + +wxMenuItem* MenuSeparator::getMenuItem() +{ + if (_separator == nullptr) + { + construct(); + } + + return _separator; +} + +void MenuSeparator::construct() +{ + _needsRefresh = false; + + if (_separator != nullptr) + { + MenuElement::constructChildren(); + return; + } + + if (isVisible()) + { + // Get the parent menu + MenuElementPtr parent = getParent(); + + if (!parent || !std::dynamic_pointer_cast(parent)) + { + rWarning() << "Cannot construct separator without a parent menu" << std::endl; + return; + } + + wxMenu* menu = std::static_pointer_cast(parent)->getMenu(); + + _separator = menu->AppendSeparator(); + } + + MenuElement::constructChildren(); +} + +void MenuSeparator::deconstruct() +{ + // Destruct children first + MenuElement::deconstructChildren(); + + if (_separator != nullptr) + { + if (_separator->GetMenu() != nullptr) + { + _separator->GetMenu()->Remove(_separator); + } + + delete _separator; + _separator = nullptr; + } +} + +} diff --git a/plugins/uimanager/menu/MenuSeparator.h b/plugins/uimanager/menu/MenuSeparator.h new file mode 100644 index 0000000000..b36c51f52c --- /dev/null +++ b/plugins/uimanager/menu/MenuSeparator.h @@ -0,0 +1,25 @@ +#pragma once + +#include "MenuElement.h" +#include + +namespace ui +{ + +class MenuSeparator : + public MenuElement +{ +private: + wxMenuItem* _separator; + +public: + MenuSeparator(); + + virtual wxMenuItem* getMenuItem(); + +protected: + virtual void construct() override; + virtual void deconstruct() override; +}; + +} diff --git a/plugins/vfspk3/Doom3FileSystem.cpp b/plugins/vfspk3/Doom3FileSystem.cpp index 7b5a56c1a1..ed75bd1413 100644 --- a/plugins/vfspk3/Doom3FileSystem.cpp +++ b/plugins/vfspk3/Doom3FileSystem.cpp @@ -358,3 +358,8 @@ void Doom3FileSystem::initialiseModule(const ApplicationContext& ctx) initialise(); } + +void Doom3FileSystem::shutdownModule() +{ + shutdown(); +} diff --git a/plugins/vfspk3/Doom3FileSystem.h b/plugins/vfspk3/Doom3FileSystem.h index 3636addc7a..6ef5c62599 100644 --- a/plugins/vfspk3/Doom3FileSystem.h +++ b/plugins/vfspk3/Doom3FileSystem.h @@ -61,9 +61,10 @@ class Doom3FileSystem : virtual void removeObserver(Observer& observer); // RegisterableModule implementation - virtual const std::string& getName() const; - virtual const StringSet& getDependencies() const; - virtual void initialiseModule(const ApplicationContext& ctx); + const std::string& getName() const override; + const StringSet& getDependencies() const override; + void initialiseModule(const ApplicationContext& ctx) override; + void shutdownModule() override; private: void initPakFile(ArchiveLoader& archiveModule, const std::string& filename); diff --git a/radiant/Makefile.am b/radiant/Makefile.am index 4007f9b4dd..3b1d4717ad 100644 --- a/radiant/Makefile.am +++ b/radiant/Makefile.am @@ -65,6 +65,7 @@ darkradiant_SOURCES = main.cpp \ render/RenderSystemFactory.cpp \ render/View.cpp \ render/debug/SpacePartitionRenderer.cpp \ + ui/UserInterfaceModule.cpp \ ui/entitychooser/EntityClassChooser.cpp \ ui/entitychooser/EntityClassTreePopulator.cpp \ ui/prefabselector/PrefabPopulator.cpp \ diff --git a/radiant/Makefile.in b/radiant/Makefile.in index c1690a70b1..1aa50e0950 100644 --- a/radiant/Makefile.in +++ b/radiant/Makefile.in @@ -143,6 +143,7 @@ am_darkradiant_OBJECTS = darkradiant-main.$(OBJEXT) \ render/darkradiant-RenderSystemFactory.$(OBJEXT) \ render/darkradiant-View.$(OBJEXT) \ render/debug/darkradiant-SpacePartitionRenderer.$(OBJEXT) \ + ui/darkradiant-UserInterfaceModule.$(OBJEXT) \ ui/entitychooser/darkradiant-EntityClassChooser.$(OBJEXT) \ ui/entitychooser/darkradiant-EntityClassTreePopulator.$(OBJEXT) \ ui/prefabselector/darkradiant-PrefabPopulator.$(OBJEXT) \ @@ -883,6 +884,7 @@ darkradiant_SOURCES = main.cpp \ render/RenderSystemFactory.cpp \ render/View.cpp \ render/debug/SpacePartitionRenderer.cpp \ + ui/UserInterfaceModule.cpp \ ui/entitychooser/EntityClassChooser.cpp \ ui/entitychooser/EntityClassTreePopulator.cpp \ ui/prefabselector/PrefabPopulator.cpp \ @@ -1309,6 +1311,14 @@ render/debug/$(DEPDIR)/$(am__dirstamp): render/debug/darkradiant-SpacePartitionRenderer.$(OBJEXT): \ render/debug/$(am__dirstamp) \ render/debug/$(DEPDIR)/$(am__dirstamp) +ui/$(am__dirstamp): + @$(MKDIR_P) ui + @: > ui/$(am__dirstamp) +ui/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ui/$(DEPDIR) + @: > ui/$(DEPDIR)/$(am__dirstamp) +ui/darkradiant-UserInterfaceModule.$(OBJEXT): ui/$(am__dirstamp) \ + ui/$(DEPDIR)/$(am__dirstamp) ui/entitychooser/$(am__dirstamp): @$(MKDIR_P) ui/entitychooser @: > ui/entitychooser/$(am__dirstamp) @@ -2145,6 +2155,7 @@ mostlyclean-compile: -rm -f test/*.$(OBJEXT) -rm -f textool/*.$(OBJEXT) -rm -f textool/item/*.$(OBJEXT) + -rm -f ui/*.$(OBJEXT) -rm -f ui/aas/*.$(OBJEXT) -rm -f ui/about/*.$(OBJEXT) -rm -f ui/brush/*.$(OBJEXT) @@ -2323,6 +2334,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@textool/item/$(DEPDIR)/darkradiant-FaceVertexItem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@textool/item/$(DEPDIR)/darkradiant-PatchItem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@textool/item/$(DEPDIR)/darkradiant-PatchVertexItem.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ui/$(DEPDIR)/darkradiant-UserInterfaceModule.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ui/aas/$(DEPDIR)/darkradiant-AasControl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ui/aas/$(DEPDIR)/darkradiant-AasControlDialog.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ui/about/$(DEPDIR)/darkradiant-AboutDialog.Po@am__quote@ @@ -2995,6 +3007,20 @@ render/debug/darkradiant-SpacePartitionRenderer.obj: render/debug/SpacePartition @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o render/debug/darkradiant-SpacePartitionRenderer.obj `if test -f 'render/debug/SpacePartitionRenderer.cpp'; then $(CYGPATH_W) 'render/debug/SpacePartitionRenderer.cpp'; else $(CYGPATH_W) '$(srcdir)/render/debug/SpacePartitionRenderer.cpp'; fi` +ui/darkradiant-UserInterfaceModule.o: ui/UserInterfaceModule.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/darkradiant-UserInterfaceModule.o -MD -MP -MF ui/$(DEPDIR)/darkradiant-UserInterfaceModule.Tpo -c -o ui/darkradiant-UserInterfaceModule.o `test -f 'ui/UserInterfaceModule.cpp' || echo '$(srcdir)/'`ui/UserInterfaceModule.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) ui/$(DEPDIR)/darkradiant-UserInterfaceModule.Tpo ui/$(DEPDIR)/darkradiant-UserInterfaceModule.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ui/UserInterfaceModule.cpp' object='ui/darkradiant-UserInterfaceModule.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/darkradiant-UserInterfaceModule.o `test -f 'ui/UserInterfaceModule.cpp' || echo '$(srcdir)/'`ui/UserInterfaceModule.cpp + +ui/darkradiant-UserInterfaceModule.obj: ui/UserInterfaceModule.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/darkradiant-UserInterfaceModule.obj -MD -MP -MF ui/$(DEPDIR)/darkradiant-UserInterfaceModule.Tpo -c -o ui/darkradiant-UserInterfaceModule.obj `if test -f 'ui/UserInterfaceModule.cpp'; then $(CYGPATH_W) 'ui/UserInterfaceModule.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/UserInterfaceModule.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) ui/$(DEPDIR)/darkradiant-UserInterfaceModule.Tpo ui/$(DEPDIR)/darkradiant-UserInterfaceModule.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ui/UserInterfaceModule.cpp' object='ui/darkradiant-UserInterfaceModule.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ui/darkradiant-UserInterfaceModule.obj `if test -f 'ui/UserInterfaceModule.cpp'; then $(CYGPATH_W) 'ui/UserInterfaceModule.cpp'; else $(CYGPATH_W) '$(srcdir)/ui/UserInterfaceModule.cpp'; fi` + ui/entitychooser/darkradiant-EntityClassChooser.o: ui/entitychooser/EntityClassChooser.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(darkradiant_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ui/entitychooser/darkradiant-EntityClassChooser.o -MD -MP -MF ui/entitychooser/$(DEPDIR)/darkradiant-EntityClassChooser.Tpo -c -o ui/entitychooser/darkradiant-EntityClassChooser.o `test -f 'ui/entitychooser/EntityClassChooser.cpp' || echo '$(srcdir)/'`ui/entitychooser/EntityClassChooser.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) ui/entitychooser/$(DEPDIR)/darkradiant-EntityClassChooser.Tpo ui/entitychooser/$(DEPDIR)/darkradiant-EntityClassChooser.Po @@ -5990,6 +6016,8 @@ distclean-generic: -rm -f textool/$(am__dirstamp) -rm -f textool/item/$(DEPDIR)/$(am__dirstamp) -rm -f textool/item/$(am__dirstamp) + -rm -f ui/$(DEPDIR)/$(am__dirstamp) + -rm -f ui/$(am__dirstamp) -rm -f ui/aas/$(DEPDIR)/$(am__dirstamp) -rm -f ui/aas/$(am__dirstamp) -rm -f ui/about/$(DEPDIR)/$(am__dirstamp) @@ -6060,7 +6088,7 @@ clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) map/infofile/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEPDIR) selection/group/$(DEPDIR) selection/manipulators/$(DEPDIR) selection/selectionset/$(DEPDIR) selection/shaderclipboard/$(DEPDIR) settings/$(DEPDIR) test/$(DEPDIR) textool/$(DEPDIR) textool/item/$(DEPDIR) ui/aas/$(DEPDIR) ui/about/$(DEPDIR) ui/brush/$(DEPDIR) ui/commandlist/$(DEPDIR) ui/common/$(DEPDIR) ui/einspector/$(DEPDIR) ui/entitychooser/$(DEPDIR) ui/filterdialog/$(DEPDIR) ui/findshader/$(DEPDIR) ui/layers/$(DEPDIR) ui/lightinspector/$(DEPDIR) ui/mainframe/$(DEPDIR) ui/mapinfo/$(DEPDIR) ui/mediabrowser/$(DEPDIR) ui/menu/$(DEPDIR) ui/modelselector/$(DEPDIR) ui/mousetool/$(DEPDIR) ui/mru/$(DEPDIR) ui/ortho/$(DEPDIR) ui/overlay/$(DEPDIR) ui/particles/$(DEPDIR) ui/patch/$(DEPDIR) ui/prefabselector/$(DEPDIR) ui/prefdialog/$(DEPDIR) ui/splash/$(DEPDIR) ui/surfaceinspector/$(DEPDIR) ui/texturebrowser/$(DEPDIR) ui/transform/$(DEPDIR) xyview/$(DEPDIR) xyview/tools/$(DEPDIR) + -rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) map/infofile/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEPDIR) selection/group/$(DEPDIR) selection/manipulators/$(DEPDIR) selection/selectionset/$(DEPDIR) selection/shaderclipboard/$(DEPDIR) settings/$(DEPDIR) test/$(DEPDIR) textool/$(DEPDIR) textool/item/$(DEPDIR) ui/$(DEPDIR) ui/aas/$(DEPDIR) ui/about/$(DEPDIR) ui/brush/$(DEPDIR) ui/commandlist/$(DEPDIR) ui/common/$(DEPDIR) ui/einspector/$(DEPDIR) ui/entitychooser/$(DEPDIR) ui/filterdialog/$(DEPDIR) ui/findshader/$(DEPDIR) ui/layers/$(DEPDIR) ui/lightinspector/$(DEPDIR) ui/mainframe/$(DEPDIR) ui/mapinfo/$(DEPDIR) ui/mediabrowser/$(DEPDIR) ui/menu/$(DEPDIR) ui/modelselector/$(DEPDIR) ui/mousetool/$(DEPDIR) ui/mru/$(DEPDIR) ui/ortho/$(DEPDIR) ui/overlay/$(DEPDIR) ui/particles/$(DEPDIR) ui/patch/$(DEPDIR) ui/prefabselector/$(DEPDIR) ui/prefdialog/$(DEPDIR) ui/splash/$(DEPDIR) ui/surfaceinspector/$(DEPDIR) ui/texturebrowser/$(DEPDIR) ui/transform/$(DEPDIR) xyview/$(DEPDIR) xyview/tools/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -6106,7 +6134,7 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) map/infofile/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEPDIR) selection/group/$(DEPDIR) selection/manipulators/$(DEPDIR) selection/selectionset/$(DEPDIR) selection/shaderclipboard/$(DEPDIR) settings/$(DEPDIR) test/$(DEPDIR) textool/$(DEPDIR) textool/item/$(DEPDIR) ui/aas/$(DEPDIR) ui/about/$(DEPDIR) ui/brush/$(DEPDIR) ui/commandlist/$(DEPDIR) ui/common/$(DEPDIR) ui/einspector/$(DEPDIR) ui/entitychooser/$(DEPDIR) ui/filterdialog/$(DEPDIR) ui/findshader/$(DEPDIR) ui/layers/$(DEPDIR) ui/lightinspector/$(DEPDIR) ui/mainframe/$(DEPDIR) ui/mapinfo/$(DEPDIR) ui/mediabrowser/$(DEPDIR) ui/menu/$(DEPDIR) ui/modelselector/$(DEPDIR) ui/mousetool/$(DEPDIR) ui/mru/$(DEPDIR) ui/ortho/$(DEPDIR) ui/overlay/$(DEPDIR) ui/particles/$(DEPDIR) ui/patch/$(DEPDIR) ui/prefabselector/$(DEPDIR) ui/prefdialog/$(DEPDIR) ui/splash/$(DEPDIR) ui/surfaceinspector/$(DEPDIR) ui/texturebrowser/$(DEPDIR) ui/transform/$(DEPDIR) xyview/$(DEPDIR) xyview/tools/$(DEPDIR) + -rm -rf ./$(DEPDIR) brush/$(DEPDIR) brush/csg/$(DEPDIR) brush/export/$(DEPDIR) camera/$(DEPDIR) clipper/$(DEPDIR) layers/$(DEPDIR) log/$(DEPDIR) map/$(DEPDIR) map/algorithm/$(DEPDIR) map/infofile/$(DEPDIR) modulesystem/$(DEPDIR) namespace/$(DEPDIR) patch/$(DEPDIR) patch/algorithm/$(DEPDIR) referencecache/$(DEPDIR) render/$(DEPDIR) render/backend/$(DEPDIR) render/backend/glprogram/$(DEPDIR) render/debug/$(DEPDIR) selection/$(DEPDIR) selection/algorithm/$(DEPDIR) selection/clipboard/$(DEPDIR) selection/group/$(DEPDIR) selection/manipulators/$(DEPDIR) selection/selectionset/$(DEPDIR) selection/shaderclipboard/$(DEPDIR) settings/$(DEPDIR) test/$(DEPDIR) textool/$(DEPDIR) textool/item/$(DEPDIR) ui/$(DEPDIR) ui/aas/$(DEPDIR) ui/about/$(DEPDIR) ui/brush/$(DEPDIR) ui/commandlist/$(DEPDIR) ui/common/$(DEPDIR) ui/einspector/$(DEPDIR) ui/entitychooser/$(DEPDIR) ui/filterdialog/$(DEPDIR) ui/findshader/$(DEPDIR) ui/layers/$(DEPDIR) ui/lightinspector/$(DEPDIR) ui/mainframe/$(DEPDIR) ui/mapinfo/$(DEPDIR) ui/mediabrowser/$(DEPDIR) ui/menu/$(DEPDIR) ui/modelselector/$(DEPDIR) ui/mousetool/$(DEPDIR) ui/mru/$(DEPDIR) ui/ortho/$(DEPDIR) ui/overlay/$(DEPDIR) ui/particles/$(DEPDIR) ui/patch/$(DEPDIR) ui/prefabselector/$(DEPDIR) ui/prefdialog/$(DEPDIR) ui/splash/$(DEPDIR) ui/surfaceinspector/$(DEPDIR) ui/texturebrowser/$(DEPDIR) ui/transform/$(DEPDIR) xyview/$(DEPDIR) xyview/tools/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/radiant/RadiantModule.cpp b/radiant/RadiantModule.cpp index a56c71bc4a..54e2ea80a8 100644 --- a/radiant/RadiantModule.cpp +++ b/radiant/RadiantModule.cpp @@ -4,51 +4,23 @@ #include #include -#include "ifiletypes.h" #include "iregistry.h" #include "icommandsystem.h" #include "itextstream.h" -#include "ifilesystem.h" #include "iuimanager.h" -#include "ieclass.h" -#include "ipreferencesystem.h" #include "ieventmanager.h" -#include "iclipper.h" #include "i18n.h" #include "imainframe.h" #include "scene/Node.h" -#include "map/PointFile.h" -#include "ui/texturebrowser/TextureBrowser.h" -#include "ui/mediabrowser/MediaBrowser.h" #include "ui/mainframe/ScreenUpdateBlocker.h" -#include "textool/TexTool.h" -#include "ui/overlay/OverlayDialog.h" -#include "ui/prefdialog/PrefDialog.h" -#include "ui/splash/Splash.h" -#include "wxutil/FileChooser.h" #include "ui/mru/MRU.h" -#include "map/Map.h" -#include "wxutil/MultiMonitor.h" -#include "brush/csg/CSG.h" #include "modulesystem/StaticModule.h" #include "selection/algorithm/General.h" +#include "brush/csg/CSG.h" -#include "log/Console.h" -#include "ui/lightinspector/LightInspector.h" -#include "ui/patch/PatchInspector.h" -#include "ui/surfaceinspector/SurfaceInspector.h" -#include "ui/transform/TransformDialog.h" -#include "ui/mapinfo/MapInfoDialog.h" -#include "ui/layers/LayerControlDialog.h" -#include "ui/commandlist/CommandList.h" -#include "ui/findshader/FindShader.h" -#include "ui/filterdialog/FilterDialog.h" -#include "ui/mousetool/ToolMappingDialog.h" -#include "ui/about/AboutDialog.h" -#include "map/FindMapElements.h" #include "ui/modelselector/ModelSelector.h" #include "EventRateLimiter.h" #include "selection/shaderclipboard/ShaderClipboard.h" @@ -184,11 +156,7 @@ const StringSet& RadiantModule::getDependencies() const { _dependencies.insert(MODULE_COMMANDSYSTEM); _dependencies.insert(MODULE_XMLREGISTRY); - _dependencies.insert(MODULE_PREFERENCESYSTEM); _dependencies.insert(MODULE_EVENTMANAGER); - _dependencies.insert(MODULE_SELECTIONSYSTEM); - _dependencies.insert(MODULE_RENDERSYSTEM); - _dependencies.insert(MODULE_CLIPPER); } return _dependencies; @@ -196,24 +164,14 @@ const StringSet& RadiantModule::getDependencies() const void RadiantModule::initialiseModule(const ApplicationContext& ctx) { - rMessage() << "RadiantModule::initialiseModule called." << std::endl; + rMessage() << getName() << "::initialiseModule called." << std::endl; // Reset the node id count scene::Node::resetIds(); - map::PointFile::Instance().registerCommands(); - - registerUICommands(); - - ui::TexTool::registerCommands(); - ui::MediaBrowser::registerCommandsAndPreferences(); - - selection::algorithm::registerCommands(); + selection::algorithm::registerCommands(); brush::algorithm::registerCommands(); - GlobalCommandSystem().addCommand("Exit", exitCmd); - GlobalEventManager().addCommand("Exit", "Exit"); - // Subscribe for the post-module init event module::GlobalModuleRegistry().signal_allModulesInitialised().connect( sigc::mem_fun(this, &RadiantModule::postModuleInitialisation)); @@ -221,39 +179,23 @@ void RadiantModule::initialiseModule(const ApplicationContext& ctx) void RadiantModule::shutdownModule() { - rMessage() << "RadiantModule::shutdownModule called." << std::endl; - - GlobalFileSystem().shutdown(); - - map::PointFile::Instance().destroy(); + rMessage() << getName() << "::shutdownModule called." << std::endl; + _radiantStarted.clear(); _radiantShutdown.clear(); } void RadiantModule::postModuleInitialisation() { - // Create the empty Settings node and set the title to empty. - GlobalPreferenceSystem().getPage(_("Game")); - - IPreferencePage& settingsPage = GlobalPreferenceSystem().getPage(_("Settings")); - settingsPage.setTitle(""); - // Construct the MRU commands and menu structure, load the recently used files GlobalMRU().initialise(); - wxutil::MultiMonitor::printMonitorInfo(); - - // Initialise the mediabrowser - ui::MediaBrowser::init(); - // Initialise the mainframe GlobalMainFrame().construct(); // Initialise the shaderclipboard GlobalShaderClipboard().clear(); - ui::LayerControlDialog::init(); - // Broadcast the startup event broadcastStartupEvent(); @@ -271,60 +213,6 @@ void RadiantModule::postModuleInitialisation() rMessage() << "Startup complete at " << ctime(&localtime) << std::endl; } -void RadiantModule::registerUICommands() -{ - GlobalCommandSystem().addCommand("ProjectSettings", ui::PrefDialog::ShowProjectSettings); - GlobalCommandSystem().addCommand("Preferences", ui::PrefDialog::ShowPrefDialog); - - GlobalCommandSystem().addCommand("ToggleConsole", ui::Console::toggle); - GlobalCommandSystem().addCommand("ToggleLightInspector", ui::LightInspector::toggleInspector); - GlobalCommandSystem().addCommand("SurfaceInspector", ui::SurfaceInspector::toggle); - GlobalCommandSystem().addCommand("PatchInspector", ui::PatchInspector::toggle); - GlobalCommandSystem().addCommand("OverlayDialog", ui::OverlayDialog::toggle); - GlobalCommandSystem().addCommand("TransformDialog", ui::TransformDialog::toggle); - - GlobalCommandSystem().addCommand("FindBrush", DoFind); - - GlobalCommandSystem().addCommand("MapInfo", ui::MapInfoDialog::ShowDialog); - GlobalCommandSystem().addCommand("EditFiltersDialog", ui::FilterDialog::ShowDialog); - GlobalCommandSystem().addCommand("MouseToolMappingDialog", ui::ToolMappingDialog::ShowDialog); - - GlobalCommandSystem().addCommand("FindReplaceTextures", ui::FindAndReplaceShader::ShowDialog); - GlobalCommandSystem().addCommand("ShowCommandList", ui::CommandList::ShowDialog); - GlobalCommandSystem().addCommand("About", ui::AboutDialog::showDialog); - - // ----------------------- Bind Events --------------------------------------- - - GlobalEventManager().addCommand("ProjectSettings", "ProjectSettings"); - - GlobalEventManager().addCommand("Preferences", "Preferences"); - - GlobalEventManager().addCommand("ToggleConsole", "ToggleConsole"); - - GlobalEventManager().addCommand("ToggleLightInspector", "ToggleLightInspector"); - GlobalEventManager().addCommand("SurfaceInspector", "SurfaceInspector"); - GlobalEventManager().addCommand("PatchInspector", "PatchInspector"); - GlobalEventManager().addCommand("OverlayDialog", "OverlayDialog"); - GlobalEventManager().addCommand("TransformDialog", "TransformDialog"); - - GlobalEventManager().addCommand("FindBrush", "FindBrush"); - - GlobalEventManager().addCommand("MapInfo", "MapInfo"); - GlobalEventManager().addCommand("EditFiltersDialog", "EditFiltersDialog"); - GlobalEventManager().addCommand("MouseToolMappingDialog", "MouseToolMappingDialog"); - - GlobalEventManager().addCommand("FindReplaceTextures", "FindReplaceTextures"); - GlobalEventManager().addCommand("ShowCommandList", "ShowCommandList"); - GlobalEventManager().addCommand("About", "About"); -} - -void RadiantModule::exitCmd(const cmd::ArgumentList& args) -{ - // Just tell the main application window to close, which will invoke - // appropriate event handlers. - GlobalMainFrame().getWxTopLevelWindow()->Close(false /* don't force */); -} - // Define the static Radiant module module::StaticModule radiantCoreModule; diff --git a/radiant/RadiantModule.h b/radiant/RadiantModule.h index 8335852d6a..21cef8591a 100644 --- a/radiant/RadiantModule.h +++ b/radiant/RadiantModule.h @@ -1,8 +1,6 @@ #pragma once #include "iradiant.h" -#include "icommandsystem.h" - #include namespace radiant @@ -46,11 +44,6 @@ class RadiantModule : // Stuff to be done after the modules have been loaded void postModuleInitialisation(); - - void registerUICommands(); - - // Target method bound to the "Exit" command - static void exitCmd(const cmd::ArgumentList& args); }; typedef std::shared_ptr RadiantModulePtr; diff --git a/radiant/camera/GlobalCamera.cpp b/radiant/camera/GlobalCamera.cpp index 621f0ea704..5a2432f6c9 100644 --- a/radiant/camera/GlobalCamera.cpp +++ b/radiant/camera/GlobalCamera.cpp @@ -3,6 +3,7 @@ #include "imousetoolmanager.h" #include "ieventmanager.h" #include "iselection.h" +#include "iuimanager.h" #include "itextstream.h" #include "xmlutil/Node.h" @@ -574,6 +575,7 @@ const StringSet& GlobalCameraManager::getDependencies() const _dependencies.insert(MODULE_RENDERSYSTEM); _dependencies.insert(MODULE_COMMANDSYSTEM); _dependencies.insert(MODULE_MOUSETOOLMANAGER); + _dependencies.insert(MODULE_UIMANAGER); } return _dependencies; diff --git a/radiant/layers/LayerSystem.cpp b/radiant/layers/LayerSystem.cpp index 97dc6cfff7..9d5411ba77 100644 --- a/radiant/layers/LayerSystem.cpp +++ b/radiant/layers/LayerSystem.cpp @@ -21,10 +21,6 @@ #include "wxutil/dialog/Dialog.h" #include "wxutil/dialog/MessageBox.h" #include "wxutil/EntryAbortedException.h" -#include "wxutil/menu/CommandMenuItem.h" - -#include "ui/layers/LayerControlDialog.h" -#include "ui/layers/LayerOrthoContextMenuItem.h" #include @@ -34,14 +30,6 @@ namespace scene namespace { const char* const DEFAULT_LAYER_NAME = N_("Default"); - - const char* const LAYER_ICON = "layers.png"; - const char* const CREATE_LAYER_TEXT = N_("Create Layer..."); - - const char* const ADD_TO_LAYER_TEXT = N_("Add to Layer..."); - const char* const MOVE_TO_LAYER_TEXT = N_("Move to Layer..."); - const char* const REMOVE_FROM_LAYER_TEXT = N_("Remove from Layer..."); - const int DEFAULT_LAYER = 0; } @@ -151,7 +139,8 @@ void LayerSystem::reset() _layerVisibility[DEFAULT_LAYER] = true; // Update the LayerControlDialog - ui::LayerControlDialog::Instance().refresh(); + _layersChangedSignal.emit(); + _layerVisibilityChangedSignal.emit(); } bool LayerSystem::renameLayer(int layerID, const std::string& newLayerName) @@ -281,7 +270,8 @@ void LayerSystem::updateSceneGraphVisibility() { GlobalSceneGraph().root()->traverseChildren(walker); } -void LayerSystem::onLayerVisibilityChanged() { +void LayerSystem::onLayerVisibilityChanged() +{ // Update all nodes updateSceneGraphVisibility(); @@ -289,7 +279,7 @@ void LayerSystem::onLayerVisibilityChanged() { SceneChangeNotify(); // Update the LayerControlDialog - ui::LayerControlDialog::Instance().update(); + _layerVisibilityChangedSignal.emit(); } void LayerSystem::addSelectionToLayer(int layerID) { @@ -404,6 +394,16 @@ void LayerSystem::setSelected(int layerID, bool selected) { } } +sigc::signal LayerSystem::signal_layersChanged() +{ + return _layersChangedSignal; +} + +sigc::signal LayerSystem::signal_layerVisibilityChanged() +{ + return _layerVisibilityChangedSignal; +} + int LayerSystem::getLayerID(const std::string& name) const { for (LayerMap::const_iterator i = _layers.begin(); i != _layers.end(); i++) { if (i->second == name) { @@ -453,7 +453,8 @@ int LayerSystem::getLowestUnusedLayerID() { } // RegisterableModule implementation -const std::string& LayerSystem::getName() const { +const std::string& LayerSystem::getName() const +{ static std::string _name(MODULE_LAYERSYSTEM); return _name; } @@ -466,8 +467,6 @@ const StringSet& LayerSystem::getDependencies() const { _dependencies.insert(MODULE_EVENTMANAGER); _dependencies.insert(MODULE_COMMANDSYSTEM); - _dependencies.insert(MODULE_UIMANAGER); - _dependencies.insert(MODULE_ORTHOCONTEXTMENU); _dependencies.insert(MODULE_MAPINFOFILEMANAGER); } @@ -482,10 +481,9 @@ void LayerSystem::initialiseModule(const ApplicationContext& ctx) createLayer(_(DEFAULT_LAYER_NAME)); // Add command targets for the first 10 layer IDs here - for (int i = 0; i < 10; i++) { - _commandTargets.push_back( - LayerCommandTargetPtr(new LayerCommandTarget(i)) - ); + for (int i = 0; i < 10; i++) + { + _commandTargets.push_back(std::make_shared(i)); } // Register the "create layer" command @@ -494,35 +492,10 @@ void LayerSystem::initialiseModule(const ApplicationContext& ctx) cmd::ARGTYPE_STRING|cmd::ARGTYPE_OPTIONAL); IEventPtr ev = GlobalEventManager().addCommand("CreateNewLayer", "CreateNewLayer"); - GlobalCommandSystem().addCommand("ToggleLayerControlDialog", ui::LayerControlDialog::toggle); - GlobalEventManager().addCommand("ToggleLayerControlDialog", "ToggleLayerControlDialog"); - GlobalMapModule().signal_mapEvent().connect( sigc::mem_fun(*this, &LayerSystem::onMapEvent) ); - // Create a new menu item connected to the CreateNewLayer command - wxutil::CommandMenuItemPtr menuItem(new wxutil::CommandMenuItem( - new wxutil::IconTextMenuItem(_(CREATE_LAYER_TEXT), LAYER_ICON), - "CreateNewLayer") - ); - - GlobalOrthoContextMenu().addItem(menuItem, ui::IOrthoContextMenu::SECTION_LAYER); - - // Add the ortho context menu items - ui::LayerOrthoContextMenuItemPtr addMenu(new ui::LayerOrthoContextMenuItem( - _(ADD_TO_LAYER_TEXT), ui::LayerOrthoContextMenuItem::AddToLayer)); - - ui::LayerOrthoContextMenuItemPtr moveMenu(new ui::LayerOrthoContextMenuItem( - _(MOVE_TO_LAYER_TEXT), ui::LayerOrthoContextMenuItem::MoveToLayer)); - - ui::LayerOrthoContextMenuItemPtr removeMenu(new ui::LayerOrthoContextMenuItem( - _(REMOVE_FROM_LAYER_TEXT), ui::LayerOrthoContextMenuItem::RemoveFromLayer)); - - GlobalOrthoContextMenu().addItem(addMenu, ui::IOrthoContextMenu::SECTION_LAYER); - GlobalOrthoContextMenu().addItem(moveMenu, ui::IOrthoContextMenu::SECTION_LAYER); - GlobalOrthoContextMenu().addItem(removeMenu, ui::IOrthoContextMenu::SECTION_LAYER); - GlobalMapInfoFileManager().registerInfoFileModule( std::make_shared() ); @@ -567,9 +540,10 @@ void LayerSystem::createLayerCmd(const cmd::ArgumentList& args) // Attempt to create the layer, this will return -1 if the operation fails int layerID = createLayer(layerName); - if (layerID != -1) { + if (layerID != -1) + { // Success, break the loop - ui::LayerControlDialog::Instance().refresh(); + _layersChangedSignal.emit(); break; } else { diff --git a/radiant/layers/LayerSystem.h b/radiant/layers/LayerSystem.h index e50fc8a696..a6dfbe6b0c 100644 --- a/radiant/layers/LayerSystem.h +++ b/radiant/layers/LayerSystem.h @@ -36,6 +36,9 @@ class LayerSystem : // The ID of the active layer int _activeLayer; + sigc::signal _layersChangedSignal; + sigc::signal _layerVisibilityChangedSignal; + public: LayerSystem(); @@ -121,6 +124,9 @@ class LayerSystem : // Selects/unselects an entire layer void setSelected(int layerID, bool selected); + sigc::signal signal_layersChanged() override; + sigc::signal signal_layerVisibilityChanged() override; + // RegisterableModule implementation const std::string& getName() const; const StringSet& getDependencies() const; diff --git a/radiant/map/Map.cpp b/radiant/map/Map.cpp index f776c80631..65e1943413 100644 --- a/radiant/map/Map.cpp +++ b/radiant/map/Map.cpp @@ -32,7 +32,6 @@ #include "scene/BasicRootNode.h" #include "map/MapFileManager.h" #include "map/MapPositionManager.h" -#include "map/PointFile.h" #include "map/RootNode.h" #include "map/MapResource.h" #include "map/algorithm/Clone.h" @@ -446,7 +445,7 @@ bool Map::save(const MapFormatPtr& mapFormat) // Store the map positions into the worldspawn spawnargs GlobalMapPosition().savePositions(); - PointFile::Instance().clear(); + signal_mapEvent().emit(IMap::MapSaved); wxutil::ScopeTimer timer("map save"); diff --git a/radiant/map/PointFile.cpp b/radiant/map/PointFile.cpp index 3d44714e36..6e89f26227 100644 --- a/radiant/map/PointFile.cpp +++ b/radiant/map/PointFile.cpp @@ -18,102 +18,72 @@ #include "xyview/GlobalXYWnd.h" #include +#include "modulesystem/StaticModule.h" + #ifdef MessageBox #undef MessageBox #endif -namespace map { +namespace map +{ // Constructor PointFile::PointFile() : - _curPos(_points.begin()), - _displayList(0) -{ - _renderstate = GlobalRenderSystem().capture("$POINTFILE"); - GlobalRenderSystem().attachRenderable(*this); - - GlobalMap().signal_mapEvent().connect(sigc::mem_fun(*this, &PointFile::onMapEvent)); -} - -void PointFile::destroy() { - GlobalRenderSystem().detachRenderable(*this); - _renderstate = ShaderPtr(); -} + _points(GL_LINE_STRIP), + _curPos(0) +{} void PointFile::onMapEvent(IMap::MapEvent ev) { - if (ev == IMap::MapUnloading) + if (ev == IMap::MapUnloading || ev == IMap::MapSaved) { clear(); } } -// Static accessor method -PointFile& PointFile::Instance() { - static PointFile _instance; - return _instance; -} - -// Query whether the point path is currently visible -bool PointFile::isVisible() const { - return _displayList != 0; +bool PointFile::isVisible() const +{ + return !_points.empty(); } -/* - * Toggle the status of the pointfile rendering. If the pointfile must be - * shown, the file is parsed automatically. - */ -void PointFile::show(bool show) { - +void PointFile::show(bool show) +{ // Update the status if required - if(show && _displayList == 0) { + if (show) + { // Parse the pointfile from disk parse(); - if (_points.size() > 0) { - generateDisplayList(); - } } - else if(!show && _displayList != 0) { - glDeleteLists (_displayList, 1); - _displayList = 0; + else + { _points.clear(); } // Regardless whether hide or show, we reset the current position - _curPos = _points.begin(); + _curPos = 0; // Redraw the scene SceneChangeNotify(); } -/* - * OpenGL render function (back-end). - */ -void PointFile::render(const RenderInfo& info) const { - glCallList(_displayList); -} - -/* - * Solid renderable submission function (front-end) - */ -void PointFile::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const { - if(isVisible()) { +void PointFile::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const +{ + if (isVisible()) + { collector.SetState(_renderstate, RenderableCollector::eWireframeOnly); collector.SetState(_renderstate, RenderableCollector::eFullMaterials); - collector.addRenderable(*this, Matrix4::getIdentity()); + collector.addRenderable(_points, Matrix4::getIdentity()); } } -/* - * Wireframe renderable submission function (front-end). - */ -void PointFile::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const { +void PointFile::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const +{ renderSolid(collector, volume); } // Parse the current pointfile and read the vectors into the point list -void PointFile::parse() { - +void PointFile::parse() +{ // Pointfile is the same as the map file but with a .lin extension // instead of .map std::string mapName = GlobalMap().getMapName(); @@ -121,7 +91,9 @@ void PointFile::parse() { // Open the pointfile and get its input stream if possible std::ifstream inFile(pfName.c_str()); - if (!inFile) { + + if (!inFile) + { wxutil::Messagebox::ShowError( (boost::format(_("Could not open pointfile: %s")) % pfName).str()); return; @@ -129,52 +101,36 @@ void PointFile::parse() { // Pointfile is a list of float vectors, one per line, with components // separated by spaces. - while (inFile.good()) { + while (inFile.good()) + { float x, y, z; inFile >> x; inFile >> y; inFile >> z; - _points.push_back(Vector3(x, y, z)); + _points.push_back(VertexCb(Vertex3f(x, y, z), Colour4b(255,0,0,1))); } } -// create the display list at the end -void PointFile::generateDisplayList() { - _displayList = glGenLists(1); - - glNewList (_displayList, GL_COMPILE); - - glBegin(GL_LINE_STRIP); - for (VectorList::iterator i = _points.begin(); - i != _points.end(); - ++i) - { - glVertex3dv(*i); - } - glEnd(); - glLineWidth (1); - - glEndList(); -} - -// Static shader -ShaderPtr PointFile::_renderstate; - // advance camera to previous point -void PointFile::advance(bool forward) { - if (!isVisible()) { +void PointFile::advance(bool forward) +{ + if (!isVisible()) + { return; } - if (forward) { - if (_curPos+2 == _points.end()) { + if (forward) + { + if (_curPos + 2 >= _points.size()) + { rMessage() << "End of pointfile" << std::endl; return; } _curPos++; } - else { - // Backward movement - if (_curPos == _points.begin()) { + else // Backward movement + { + if (_curPos == 0) + { rMessage() << "Start of pointfile" << std::endl; return; } @@ -183,47 +139,99 @@ void PointFile::advance(bool forward) { } ui::CamWndPtr cam = GlobalCamera().getActiveCamWnd(); - if (cam == NULL) return; - ui::CamWnd& camwnd = *cam; - camwnd.setCameraOrigin(*_curPos); - GlobalXYWnd().getActiveXY()->setOrigin(*_curPos); + if (!cam) return; + + cam->setCameraOrigin(_points[_curPos].vertex); + + GlobalXYWnd().getActiveXY()->setOrigin(_points[_curPos].vertex); + { - Vector3 dir((*(_curPos+1) - camwnd.getCameraOrigin()).getNormalised()); - Vector3 angles(camwnd.getCameraAngles()); - angles[ui::CAMERA_YAW] = static_cast(radians_to_degrees(atan2(dir[1], dir[0]))); - angles[ui::CAMERA_PITCH] = static_cast(radians_to_degrees(asin(dir[2]))); - camwnd.setCameraAngles(angles); + Vector3 dir((_points[_curPos+1].vertex - cam->getCameraOrigin()).getNormalised()); + Vector3 angles(cam->getCameraAngles()); + + angles[ui::CAMERA_YAW] = radians_to_degrees(atan2(dir[1], dir[0])); + angles[ui::CAMERA_PITCH] = radians_to_degrees(asin(dir[2])); + + cam->setCameraAngles(angles); } // Redraw the scene SceneChangeNotify(); } -void PointFile::nextLeakSpot(const cmd::ArgumentList& args) { - Instance().advance(true); +void PointFile::nextLeakSpot(const cmd::ArgumentList& args) +{ + advance(true); } -void PointFile::prevLeakSpot(const cmd::ArgumentList& args) { - Instance().advance(false); +void PointFile::prevLeakSpot(const cmd::ArgumentList& args) +{ + advance(false); } -void PointFile::clear() { +void PointFile::clear() +{ show(false); } -void PointFile::toggle(const cmd::ArgumentList& args) { - Instance().show(!Instance().isVisible()); +void PointFile::toggle(const cmd::ArgumentList& args) +{ + show(!isVisible()); } -void PointFile::registerCommands() { - GlobalCommandSystem().addCommand("TogglePointfile", toggle); - GlobalCommandSystem().addCommand("NextLeakSpot", nextLeakSpot); - GlobalCommandSystem().addCommand("PrevLeakSpot", prevLeakSpot); +void PointFile::registerCommands() +{ + GlobalCommandSystem().addCommand("TogglePointfile", sigc::mem_fun(*this, &PointFile::toggle)); + GlobalCommandSystem().addCommand("NextLeakSpot", sigc::mem_fun(*this, &PointFile::nextLeakSpot)); + GlobalCommandSystem().addCommand("PrevLeakSpot", sigc::mem_fun(*this, &PointFile::prevLeakSpot)); GlobalEventManager().addCommand("TogglePointfile", "TogglePointfile"); GlobalEventManager().addCommand("NextLeakSpot", "NextLeakSpot"); GlobalEventManager().addCommand("PrevLeakSpot", "PrevLeakSpot"); } +// RegisterableModule implementation +const std::string& PointFile::getName() const +{ + static std::string _name("PointFile"); + return _name; +} + +const StringSet& PointFile::getDependencies() const +{ + static StringSet _dependencies; + + if (_dependencies.empty()) + { + _dependencies.insert(MODULE_COMMANDSYSTEM); + _dependencies.insert(MODULE_EVENTMANAGER); + _dependencies.insert(MODULE_RENDERSYSTEM); + _dependencies.insert(MODULE_MAP); + } + + return _dependencies; +} + +void PointFile::initialiseModule(const ApplicationContext& ctx) +{ + rMessage() << getName() << "::initialiseModule called" << std::endl; + + registerCommands(); + + _renderstate = GlobalRenderSystem().capture("$POINTFILE"); + + GlobalRenderSystem().attachRenderable(*this); + + GlobalMap().signal_mapEvent().connect(sigc::mem_fun(*this, &PointFile::onMapEvent)); +} + +void PointFile::shutdownModule() +{ + GlobalRenderSystem().detachRenderable(*this); + _renderstate.reset(); +} + +module::StaticModule pointFileModule; + } // namespace map diff --git a/radiant/map/PointFile.h b/radiant/map/PointFile.h index 77b008aabf..77aca5a411 100644 --- a/radiant/map/PointFile.h +++ b/radiant/map/PointFile.h @@ -3,28 +3,27 @@ #include #include "irender.h" #include "imap.h" +#include "imodule.h" #include "icommandsystem.h" #include "irenderable.h" #include "math/Vector3.h" +#include "render.h" namespace map { class PointFile : - public Renderable, - public OpenGLRenderable + public RegisterableModule, + public Renderable { +private: // Vector of point coordinates - typedef std::vector VectorList; - VectorList _points; - + RenderablePointVector _points; + // Holds the current position in the point file chain - VectorList::iterator _curPos; - - // GL display list pointer for rendering the point path - int _displayList; + std::size_t _curPos; - static ShaderPtr _renderstate; + ShaderPtr _renderstate; public: // Constructor @@ -33,30 +32,10 @@ class PointFile : // Destructor virtual ~PointFile() {} - /** greebo: Accessor method containing the singleton instance. - */ - static PointFile& Instance(); - - /** greebo: This releases the shader and detaches this class from - * the shadercache. - */ - void destroy(); - // Query whether the point path is currently visible bool isVisible() const; /* - * Toggle the status of the pointfile rendering. If the pointfile must be - * shown, the file is parsed automatically. - */ - void show(bool show); - - /* - * OpenGL render function (back-end). - */ - void render(const RenderInfo& info) const; - - /* * Solid renderable submission function (front-end) */ void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override; @@ -74,33 +53,41 @@ class PointFile : return Highlight::NoHighlight; } - /** greebo: This sets the camera position to the next/prev leak spot. - * - * @forward: pass true to set to the next leak spot, false for the previous + const std::string& getName() const override; + const StringSet& getDependencies() const override; + void initialiseModule(const ApplicationContext& ctx) override; + void shutdownModule() override; + +private: + // Registers the events to the EventManager + void registerCommands(); + + /* + * Toggle the status of the pointfile rendering. If the pointfile must be + * shown, the file is parsed automatically. */ - void advance(bool forward); + void show(bool show); - /** greebo: Clears the point file vector and hides it, if applicable. + /** + * greebo: Clears the point file vector, which is the same as hiding it. */ void clear(); - // Registers the events to the EventManager - static void registerCommands(); - - // Static command targets, these re-route the call to the static instance - static void nextLeakSpot(const cmd::ArgumentList& args); - static void prevLeakSpot(const cmd::ArgumentList& args); + /** + * greebo: This sets the camera position to the next/prev leak spot. + * @forward: pass true to set to the next leak spot, false for the previous + */ + void advance(bool forward); + // command targets // Toggles visibility of the point file line - static void toggle(const cmd::ArgumentList& args); + void toggle(const cmd::ArgumentList& args); + void nextLeakSpot(const cmd::ArgumentList& args); + void prevLeakSpot(const cmd::ArgumentList& args); -private: // Parse the current pointfile and read the vectors into the point list void parse(); - // Generates the OpenGL displaylist from the point vector - void generateDisplayList(); - void onMapEvent(IMap::MapEvent ev); }; diff --git a/radiant/selection/shaderclipboard/ShaderClipboard.cpp b/radiant/selection/shaderclipboard/ShaderClipboard.cpp index 9897e08cb6..6364240778 100644 --- a/radiant/selection/shaderclipboard/ShaderClipboard.cpp +++ b/radiant/selection/shaderclipboard/ShaderClipboard.cpp @@ -4,7 +4,7 @@ #include "iselectiontest.h" #include "iscenegraph.h" #include "iuimanager.h" -#include "ui/mediabrowser/MediaBrowser.h" +#include "imediabrowser.h" #include "ui/texturebrowser/TextureBrowser.h" #include "ClosestTexturableFinder.h" @@ -69,22 +69,24 @@ Texturable ShaderClipboard::getTexturable(SelectionTest& test) { return returnValue; } -void ShaderClipboard::updateMediaBrowsers() { +void ShaderClipboard::updateMediaBrowsers() +{ // Avoid nasty loopbacks _updatesDisabled = true; // Set the active shader in the Texture window as well GlobalTextureBrowser().setSelectedShader(_source.getShader()); + std::string sourceShader = _source.getShader(); - ui::MediaBrowser::getInstance().setSelection(sourceShader); + GlobalMediaBrowser().setSelection(sourceShader); _updatesDisabled = false; updateStatusText(); } -void ShaderClipboard::updateStatusText() { - +void ShaderClipboard::updateStatusText() +{ std::string statusText; if (!_source.empty()) { diff --git a/radiant/settings/PreferencePage.cpp b/radiant/settings/PreferencePage.cpp index ac0ca4d3c1..bf0cdeed1f 100644 --- a/radiant/settings/PreferencePage.cpp +++ b/radiant/settings/PreferencePage.cpp @@ -117,6 +117,11 @@ void PreferencePage::foreachItem(const std::function(label, registryKey)); diff --git a/radiant/settings/PreferencePage.h b/radiant/settings/PreferencePage.h index 81041eb491..830b0169d2 100644 --- a/radiant/settings/PreferencePage.h +++ b/radiant/settings/PreferencePage.h @@ -69,6 +69,9 @@ class PreferencePage : // Hit the functor with each item on this page void foreachItem(const std::function& functor) const; + // Returns TRUE if this page doesn't hold any items + bool isEmpty() const; + // IPreferencePage implementation void appendCheckBox(const std::string& label, const std::string& registryKey) override; diff --git a/radiant/ui/UserInterfaceModule.cpp b/radiant/ui/UserInterfaceModule.cpp new file mode 100644 index 0000000000..6ff6bd07f7 --- /dev/null +++ b/radiant/ui/UserInterfaceModule.cpp @@ -0,0 +1,161 @@ +#include "UserInterfaceModule.h" + +#include "i18n.h" +#include "ilayer.h" +#include "iorthocontextmenu.h" +#include "ieventmanager.h" + +#include "wxutil/menu/CommandMenuItem.h" +#include "wxutil/MultiMonitor.h" + +#include "modulesystem/StaticModule.h" + +#include "ui/layers/LayerOrthoContextMenuItem.h" +#include "ui/layers/LayerControlDialog.h" +#include "ui/overlay/OverlayDialog.h" +#include "ui/prefdialog/PrefDialog.h" +#include "log/Console.h" +#include "ui/lightinspector/LightInspector.h" +#include "ui/patch/PatchInspector.h" +#include "ui/surfaceinspector/SurfaceInspector.h" +#include "ui/transform/TransformDialog.h" +#include "ui/findshader/FindShader.h" +#include "map/FindMapElements.h" +#include "ui/mapinfo/MapInfoDialog.h" +#include "ui/commandlist/CommandList.h" +#include "ui/filterdialog/FilterDialog.h" +#include "ui/mousetool/ToolMappingDialog.h" +#include "ui/about/AboutDialog.h" +#include "textool/TexTool.h" + +namespace ui +{ + +namespace +{ + const char* const LAYER_ICON = "layers.png"; + const char* const CREATE_LAYER_TEXT = N_("Create Layer..."); + + const char* const ADD_TO_LAYER_TEXT = N_("Add to Layer..."); + const char* const MOVE_TO_LAYER_TEXT = N_("Move to Layer..."); + const char* const REMOVE_FROM_LAYER_TEXT = N_("Remove from Layer..."); +} + +const std::string& UserInterfaceModule::getName() const +{ + static std::string _name("UserInterfaceModule"); + return _name; +} + +const StringSet& UserInterfaceModule::getDependencies() const +{ + static StringSet _dependencies; + + if (_dependencies.empty()) + { + _dependencies.insert(MODULE_LAYERSYSTEM); + _dependencies.insert(MODULE_ORTHOCONTEXTMENU); + _dependencies.insert(MODULE_UIMANAGER); + } + + return _dependencies; +} + +void UserInterfaceModule::initialiseModule(const ApplicationContext& ctx) +{ + rMessage() << getName() << "::initialiseModule called." << std::endl; + + wxutil::MultiMonitor::printMonitorInfo(); + + registerUICommands(); + + // Register LayerControlDialog + GlobalCommandSystem().addCommand("ToggleLayerControlDialog", LayerControlDialog::toggle); + GlobalEventManager().addCommand("ToggleLayerControlDialog", "ToggleLayerControlDialog"); + + // Create a new menu item connected to the CreateNewLayer command + GlobalOrthoContextMenu().addItem(std::make_shared( + new wxutil::IconTextMenuItem(_(CREATE_LAYER_TEXT), LAYER_ICON), "CreateNewLayer"), + IOrthoContextMenu::SECTION_LAYER + ); + + // Add the orthocontext menu's layer actions + GlobalOrthoContextMenu().addItem( + std::make_shared(_(ADD_TO_LAYER_TEXT), + LayerOrthoContextMenuItem::AddToLayer), + IOrthoContextMenu::SECTION_LAYER + ); + + GlobalOrthoContextMenu().addItem( + std::make_shared(_(MOVE_TO_LAYER_TEXT), + LayerOrthoContextMenuItem::MoveToLayer), + IOrthoContextMenu::SECTION_LAYER + ); + + GlobalOrthoContextMenu().addItem( + std::make_shared(_(REMOVE_FROM_LAYER_TEXT), + LayerOrthoContextMenuItem::RemoveFromLayer), + IOrthoContextMenu::SECTION_LAYER + ); + + GlobalRadiant().signal_radiantStarted().connect( + sigc::ptr_fun(LayerControlDialog::onRadiantStartup)); +} + +void UserInterfaceModule::shutdownModule() +{ +} + +void UserInterfaceModule::registerUICommands() +{ + TexTool::registerCommands(); + + GlobalCommandSystem().addCommand("ProjectSettings", PrefDialog::ShowProjectSettings); + GlobalCommandSystem().addCommand("Preferences", PrefDialog::ShowPrefDialog); + + GlobalCommandSystem().addCommand("ToggleConsole", Console::toggle); + GlobalCommandSystem().addCommand("ToggleLightInspector", LightInspector::toggleInspector); + GlobalCommandSystem().addCommand("SurfaceInspector", SurfaceInspector::toggle); + GlobalCommandSystem().addCommand("PatchInspector", PatchInspector::toggle); + GlobalCommandSystem().addCommand("OverlayDialog", OverlayDialog::toggle); + GlobalCommandSystem().addCommand("TransformDialog", TransformDialog::toggle); + + GlobalCommandSystem().addCommand("FindBrush", DoFind); + + GlobalCommandSystem().addCommand("MapInfo", MapInfoDialog::ShowDialog); + GlobalCommandSystem().addCommand("EditFiltersDialog", FilterDialog::ShowDialog); + GlobalCommandSystem().addCommand("MouseToolMappingDialog", ToolMappingDialog::ShowDialog); + + GlobalCommandSystem().addCommand("FindReplaceTextures", FindAndReplaceShader::ShowDialog); + GlobalCommandSystem().addCommand("ShowCommandList", CommandList::ShowDialog); + GlobalCommandSystem().addCommand("About", AboutDialog::showDialog); + + // ----------------------- Bind Events --------------------------------------- + + GlobalEventManager().addCommand("ProjectSettings", "ProjectSettings"); + + GlobalEventManager().addCommand("Preferences", "Preferences"); + + GlobalEventManager().addCommand("ToggleConsole", "ToggleConsole"); + + GlobalEventManager().addCommand("ToggleLightInspector", "ToggleLightInspector"); + GlobalEventManager().addCommand("SurfaceInspector", "SurfaceInspector"); + GlobalEventManager().addCommand("PatchInspector", "PatchInspector"); + GlobalEventManager().addCommand("OverlayDialog", "OverlayDialog"); + GlobalEventManager().addCommand("TransformDialog", "TransformDialog"); + + GlobalEventManager().addCommand("FindBrush", "FindBrush"); + + GlobalEventManager().addCommand("MapInfo", "MapInfo"); + GlobalEventManager().addCommand("EditFiltersDialog", "EditFiltersDialog"); + GlobalEventManager().addCommand("MouseToolMappingDialog", "MouseToolMappingDialog"); + + GlobalEventManager().addCommand("FindReplaceTextures", "FindReplaceTextures"); + GlobalEventManager().addCommand("ShowCommandList", "ShowCommandList"); + GlobalEventManager().addCommand("About", "About"); +} + +// Static module registration +module::StaticModule userInterfaceModule; + +} diff --git a/radiant/ui/UserInterfaceModule.h b/radiant/ui/UserInterfaceModule.h new file mode 100644 index 0000000000..04db7a897c --- /dev/null +++ b/radiant/ui/UserInterfaceModule.h @@ -0,0 +1,30 @@ +#pragma once + +#include "imodule.h" +#include "iorthocontextmenu.h" + +namespace ui +{ + +/** + * Module responsible of registering and intialising the various + * UI classes in DarkRadiant, e.g. the LayerSystem. + * + * Currently many UI classes are spread and initialised all across + * the main binary, so there's still work left to do. + */ +class UserInterfaceModule : + public RegisterableModule +{ +public: + // RegisterableModule + const std::string & getName() const override; + const StringSet & getDependencies() const override; + void initialiseModule(const ApplicationContext & ctx) override; + void shutdownModule() override; + +private: + void registerUICommands(); +}; + +} diff --git a/radiant/ui/layers/LayerControlDialog.cpp b/radiant/ui/layers/LayerControlDialog.cpp index d50dc811fd..2e57ef03ed 100644 --- a/radiant/ui/layers/LayerControlDialog.cpp +++ b/radiant/ui/layers/LayerControlDialog.cpp @@ -201,7 +201,7 @@ void LayerControlDialog::toggle(const cmd::ArgumentList& args) Instance().ToggleVisibility(); } -void LayerControlDialog::init() +void LayerControlDialog::onRadiantStartup() { // Lookup the stored window information in the registry if (GlobalRegistry().getAttribute(RKEY_WINDOW_STATE, "visible") == "1") @@ -241,7 +241,7 @@ LayerControlDialog& LayerControlDialog::Instance() { LayerControlDialogPtr& instancePtr = InstancePtr(); - if (instancePtr == NULL) + if (!instancePtr) { // Not yet instantiated, do it now instancePtr.reset(new LayerControlDialog); @@ -264,12 +264,22 @@ void LayerControlDialog::_preShow() _rescanSelectionOnIdle = true; }); + // Layer creation/addition/removal triggers a refresh + _layersChangedSignal = GlobalLayerSystem().signal_layersChanged().connect( + sigc::mem_fun(this, &LayerControlDialog::refresh)); + + // Visibility change doesn't repopulate the dialog + _layerVisibilityChangedSignal = GlobalLayerSystem().signal_layerVisibilityChanged().connect( + sigc::mem_fun(this, &LayerControlDialog::update)); + // Re-populate the dialog refresh(); } void LayerControlDialog::_postHide() { + _layersChangedSignal.disconnect(); + _layerVisibilityChangedSignal.disconnect(); _selectionChangedSignal.disconnect(); _rescanSelectionOnIdle = false; } diff --git a/radiant/ui/layers/LayerControlDialog.h b/radiant/ui/layers/LayerControlDialog.h index adc7be45c4..57e4d5eb80 100644 --- a/radiant/ui/layers/LayerControlDialog.h +++ b/radiant/ui/layers/LayerControlDialog.h @@ -36,6 +36,8 @@ class LayerControlDialog : bool _rescanSelectionOnIdle; sigc::connection _selectionChangedSignal; + sigc::connection _layersChangedSignal; + sigc::connection _layerVisibilityChangedSignal; public: LayerControlDialog(); @@ -51,8 +53,8 @@ class LayerControlDialog : // Command target (registered in the event manager) static void toggle(const cmd::ArgumentList& args); - // Called during mainframe construction - static void init(); + // Checks if dialog should be shown after startup + static void onRadiantStartup(); static LayerControlDialog& Instance(); diff --git a/radiant/ui/mainframe/LayoutCommand.h b/radiant/ui/mainframe/LayoutCommand.h index 32c4d66a29..7c047528d8 100644 --- a/radiant/ui/mainframe/LayoutCommand.h +++ b/radiant/ui/mainframe/LayoutCommand.h @@ -12,14 +12,16 @@ #include #include -namespace ui { +namespace ui +{ - namespace { - const std::string MENU_LAYOUTS_PARENT = "main/view"; - const std::string MENU_LAYOUTS = "layouts"; - const std::string MENU_LAYOUTS_PATH = MENU_LAYOUTS_PARENT + "/" + MENU_LAYOUTS; - const std::string MENU_LAYOUTS_INSERT_BEFORE = "main/view/camera"; - } +namespace +{ + const std::string MENU_LAYOUTS_PARENT = "main/view"; + const std::string MENU_LAYOUTS = "layouts"; + const std::string MENU_LAYOUTS_PATH = MENU_LAYOUTS_PARENT + "/" + MENU_LAYOUTS; + const std::string MENU_LAYOUTS_INSERT_BEFORE = "main/view/camera"; +} /** * This little class represents a "command target", providing @@ -46,7 +48,7 @@ class LayoutCommand IMenuManager& menuManager = GlobalUIManager().getMenuManager(); // Add a new folder, if not existing yet - if (menuManager.get(MENU_LAYOUTS_PATH) == NULL) + if (!menuManager.exists(MENU_LAYOUTS_PATH)) { menuManager.insert( MENU_LAYOUTS_INSERT_BEFORE, @@ -75,9 +77,9 @@ class LayoutCommand void activateLayout(const cmd::ArgumentList& args) { GlobalMainFrame().setActiveLayoutName(_layoutName); - wxutil::Messagebox::Show("Restart required", - "Restart DarkRadiant to apply changes", - ui::IDialog::MESSAGE_CONFIRM); + wxutil::Messagebox::Show(_("Restart required"), + _("Restart DarkRadiant to apply changes"), + IDialog::MESSAGE_CONFIRM); } }; typedef std::shared_ptr LayoutCommandPtr; diff --git a/radiant/ui/mainframe/MainFrame.cpp b/radiant/ui/mainframe/MainFrame.cpp index 3347637b1c..66dbe07499 100644 --- a/radiant/ui/mainframe/MainFrame.cpp +++ b/radiant/ui/mainframe/MainFrame.cpp @@ -12,7 +12,6 @@ #include "log/Console.h" #include "xyview/GlobalXYWnd.h" -#include "ui/mediabrowser/MediaBrowser.h" #include "camera/GlobalCamera.h" #include "registry/registry.h" @@ -108,6 +107,8 @@ void MainFrame::initialiseModule(const ApplicationContext& ctx) ); GlobalEventManager().addCommand("ToggleFullScreenCamera", "ToggleFullScreenCamera"); + + #ifdef WIN32 HMODULE lib = LoadLibrary(L"dwmapi.dll"); @@ -144,6 +145,16 @@ void MainFrame::shutdownModule() disableScreenUpdates(); } +void MainFrame::exitCmd(const cmd::ArgumentList& args) +{ + // Just tell the main application window to close, which will invoke + // appropriate event handlers. + if (getWxTopLevelWindow() != nullptr) + { + getWxTopLevelWindow()->Close(false /* don't force */); + } +} + void MainFrame::keyChanged() { #ifdef WIN32 @@ -359,21 +370,6 @@ void MainFrame::create() // Create the topmost window first createTopLevelWindow(); - /* Construct the Group Dialog. This is the tabbed window that contains - * a number of pages - usually Entities, Textures and possibly Console. - */ - // Add the Media Browser page - IGroupDialog::PagePtr mediaBrowserPage(new IGroupDialog::Page); - - mediaBrowserPage->name = "mediabrowser"; - mediaBrowserPage->windowLabel = _("Media"); - mediaBrowserPage->page = MediaBrowser::getInstance().getWidget(); - mediaBrowserPage->tabIcon = "folder16.png"; - mediaBrowserPage->tabLabel = _("Media"); - mediaBrowserPage->position = IGroupDialog::Page::Position::MediaBrowser; - - GlobalGroupDialog().addPage(mediaBrowserPage); - // Add the console IGroupDialog::PagePtr consolePage(new IGroupDialog::Page); diff --git a/radiant/ui/mainframe/MainFrame.h b/radiant/ui/mainframe/MainFrame.h index adcf0c1b7c..3afab82797 100644 --- a/radiant/ui/mainframe/MainFrame.h +++ b/radiant/ui/mainframe/MainFrame.h @@ -66,6 +66,8 @@ class MainFrame : private: void create(); + void exitCmd(const cmd::ArgumentList& args); + void removeLayout(); // Save/Restore the window position as saved to the registry diff --git a/radiant/ui/mainframe/TopLevelFrame.cpp b/radiant/ui/mainframe/TopLevelFrame.cpp index d868863bad..9de6941cb6 100644 --- a/radiant/ui/mainframe/TopLevelFrame.cpp +++ b/radiant/ui/mainframe/TopLevelFrame.cpp @@ -23,7 +23,7 @@ TopLevelFrame::TopLevelFrame() : wxMenuBar* menuBar = createMenuBar(); - if (menuBar != NULL) + if (menuBar != nullptr) { SetMenuBar(menuBar); } @@ -83,7 +83,7 @@ wxToolBar* TopLevelFrame::getToolbar(IMainFrame::Toolbar type) { ToolbarMap::const_iterator found = _toolbars.find(type); - return (found != _toolbars.end()) ? found->second.get() : NULL; + return (found != _toolbars.end()) ? found->second.get() : nullptr; } wxMenuBar* TopLevelFrame::createMenuBar() @@ -92,7 +92,7 @@ wxMenuBar* TopLevelFrame::createMenuBar() FiltersMenu::addItemsToMainMenu(); // Return the "main" menubar from the UIManager - return dynamic_cast(GlobalUIManager().getMenuManager().get("main")); + return GlobalUIManager().getMenuManager().getMenuBar("main"); } wxBoxSizer* TopLevelFrame::getMainContainer() diff --git a/radiant/ui/mediabrowser/MediaBrowser.cpp b/radiant/ui/mediabrowser/MediaBrowser.cpp index 497c3a3585..e3c3032a75 100644 --- a/radiant/ui/mediabrowser/MediaBrowser.cpp +++ b/radiant/ui/mediabrowser/MediaBrowser.cpp @@ -33,6 +33,7 @@ #include "ui/common/TexturePreviewCombo.h" #include "debugging/ScopedDebugTimer.h" +#include "modulesystem/StaticModule.h" #include #include @@ -58,7 +59,6 @@ const char* APPLY_TEXTURE_ICON = "textureApplyToSelection16.png"; const char* SHOW_SHADER_DEF_TEXT = N_("Show Shader Definition"); const char* SHOW_SHADER_DEF_ICON = "icon_script.png"; -const std::string RKEY_MEDIA_BROWSER_PRELOAD = "user/ui/mediaBrowser/preLoadMediaTree"; const char* const OTHER_MATERIALS_FOLDER = N_("Other Materials"); const char* const SELECT_ITEMS = N_("Select elements using this shader"); @@ -327,22 +327,22 @@ class MediaBrowser::Populator : // Constructor MediaBrowser::MediaBrowser() : - _tempParent(NULL), - _mainWidget(NULL), - _treeView(NULL), - _treeStore(NULL), - _preview(NULL), + _tempParent(nullptr), + _mainWidget(nullptr), + _treeView(nullptr), + _treeStore(nullptr), + _preview(nullptr), _isPopulated(false) {} void MediaBrowser::construct() { - if (_mainWidget != NULL) + if (_mainWidget != nullptr) { return; } - _tempParent = new wxFrame(NULL, wxID_ANY, ""); + _tempParent = new wxFrame(nullptr, wxID_ANY, ""); _tempParent->Hide(); _treeStore = new wxutil::TreeModel(_columns); @@ -370,11 +370,11 @@ void MediaBrowser::construct() // Connect up the selection changed callback _treeView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, - wxTreeEventHandler(MediaBrowser::_onSelectionChanged), NULL, this); - _treeView->Connect(wxEVT_PAINT, wxPaintEventHandler(MediaBrowser::_onExpose), NULL, this); + wxTreeEventHandler(MediaBrowser::_onSelectionChanged), nullptr, this); + _treeView->Connect(wxEVT_PAINT, wxPaintEventHandler(MediaBrowser::_onExpose), nullptr, this); _treeView->Connect(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, - wxDataViewEventHandler(MediaBrowser::_onContextMenu), NULL, this); + wxDataViewEventHandler(MediaBrowser::_onContextMenu), nullptr, this); // Add the info pane _preview = new TexturePreviewCombo(_mainWidget); @@ -409,18 +409,7 @@ void MediaBrowser::construct() // Connect the finish callback to load the treestore Connect(wxutil::EV_TREEMODEL_POPULATION_FINISHED, - TreeModelPopulationFinishedHandler(MediaBrowser::onTreeStorePopulationFinished), NULL, this); - - GlobalRadiant().signal_radiantShutdown().connect( - sigc::mem_fun(*this, &MediaBrowser::onRadiantShutdown) - ); -} - -wxWindow* MediaBrowser::getWidget() -{ - construct(); - - return _mainWidget; + TreeModelPopulationFinishedHandler(MediaBrowser::onTreeStorePopulationFinished), nullptr, this); } /* Tree query functions */ @@ -436,47 +425,46 @@ bool MediaBrowser::isDirectorySelected() return row[_columns.isFolder].getBool(); } -std::string MediaBrowser::getSelectedName() +void MediaBrowser::onRadiantStartup() { - // Get the selected value - wxDataViewItem item = _treeView->GetSelection(); + // Add the Media Browser page + IGroupDialog::PagePtr mediaBrowserPage(new IGroupDialog::Page); - if (!item.IsOk()) return ""; // nothing selected - - // Cast to TreeModel::Row and get the full name - wxutil::TreeModel::Row row(item, *_treeView->GetModel()); - - return row[_columns.fullName]; -} - -void MediaBrowser::onRadiantShutdown() -{ - _tempParent->Destroy(); + mediaBrowserPage->name = getGroupDialogTabName(); + mediaBrowserPage->windowLabel = _("Media"); + mediaBrowserPage->page = _mainWidget; + mediaBrowserPage->tabIcon = "folder16.png"; + mediaBrowserPage->tabLabel = _("Media"); + mediaBrowserPage->position = IGroupDialog::Page::Position::MediaBrowser; - GlobalMaterialManager().detach(*this); + GlobalGroupDialog().addPage(mediaBrowserPage); - // Delete the singleton instance on shutdown - getInstancePtr().reset(); + if (_tempParent != nullptr) + { + _tempParent->Destroy(); + _tempParent = nullptr; + } } -/** Return the singleton instance. - */ -MediaBrowser& MediaBrowser::getInstance() +std::string MediaBrowser::getSelection() { - MediaBrowserPtr& instancePtr = getInstancePtr(); + if (!_isPopulated) + { + return std::string(); + } + + // Get the selected value + wxDataViewItem item = _treeView->GetSelection(); - if (instancePtr == NULL) + if (!item.IsOk()) { - instancePtr.reset(new MediaBrowser); + return std::string(); // nothing selected } - return *instancePtr; -} + // Cast to TreeModel::Row and get the full name + wxutil::TreeModel::Row row(item, *_treeView->GetModel()); -MediaBrowserPtr& MediaBrowser::getInstancePtr() -{ - static MediaBrowserPtr _instancePtr; - return _instancePtr; + return row[_columns.fullName]; } // Set the selection in the treeview @@ -509,32 +497,6 @@ void MediaBrowser::setSelection(const std::string& selection) } } -void MediaBrowser::reloadMedia() -{ - // Remove all items and clear the "isPopulated" flag - _treeStore->Clear(); - _isPopulated = false; - - // Trigger an "expose" event - _treeView->Refresh(); -} - -void MediaBrowser::init() -{ - // Create the widgets now - getInstance().construct(); - - // Check for pre-loading the textures - if (registry::getValue(RKEY_MEDIA_BROWSER_PRELOAD)) - { - getInstance().populate(); - } - - // Attach to the MaterialManager to get notified on unrealise/realise - // events, in which case we're reloading the media tree - GlobalMaterialManager().attach(getInstance()); -} - void MediaBrowser::realise() { if (!_isPopulated) @@ -585,13 +547,13 @@ void MediaBrowser::onTreeStorePopulationFinished(wxutil::TreeModel::PopulationFi _treeView->AssociateModel(_treeStore.get()); } -/* gtkutil::PopupMenu callbacks */ +/* wxutil::PopupMenu callbacks */ void MediaBrowser::_onLoadInTexView() { // Use a TextureDirectoryLoader functor to search the directory. This // may throw an exception if cancelled by user. - TextureDirectoryLoader loader(getSelectedName()); + TextureDirectoryLoader loader(getSelection()); try { @@ -615,14 +577,14 @@ bool MediaBrowser::_testLoadInTexView() void MediaBrowser::_onApplyToSel() { // Pass shader name to the selection system - selection::algorithm::applyShaderToSelection(getSelectedName()); + selection::algorithm::applyShaderToSelection(getSelection()); } // Check if a single non-directory texture is selected (used by multiple menu // options). bool MediaBrowser::_testSingleTexSel() { - if (!isDirectorySelected() && !getSelectedName().empty()) + if (!isDirectorySelected() && !getSelection().empty()) return true; else return false; @@ -630,7 +592,7 @@ bool MediaBrowser::_testSingleTexSel() void MediaBrowser::_onShowShaderDefinition() { - std::string shaderName = getSelectedName(); + std::string shaderName = getSelection(); // Construct a shader view and pass the shader name ShaderDefinitionView::ShowDialog(shaderName); @@ -638,7 +600,7 @@ void MediaBrowser::_onShowShaderDefinition() void MediaBrowser::_onSelectItems(bool select) { - std::string shaderName = getSelectedName(); + std::string shaderName = getSelection(); if (select) { @@ -671,8 +633,8 @@ void MediaBrowser::handleSelectionChange() // Update the preview if a texture is selected if (!isDirectorySelected()) { - _preview->SetTexture(getSelectedName()); - GlobalShaderClipboard().setSource(getSelectedName()); + _preview->SetTexture(getSelection()); + GlobalShaderClipboard().setSource(getSelection()); } else { @@ -687,19 +649,60 @@ void MediaBrowser::_onSelectionChanged(wxTreeEvent& ev) handleSelectionChange(); } -void MediaBrowser::toggle(const cmd::ArgumentList& args) +void MediaBrowser::togglePage(const cmd::ArgumentList& args) +{ + GlobalGroupDialog().togglePage(getGroupDialogTabName()); +} + +const std::string& MediaBrowser::getName() const +{ + static std::string _name("MediaBrowser"); + return _name; +} + +const StringSet& MediaBrowser::getDependencies() const { - GlobalGroupDialog().togglePage("mediabrowser"); + static StringSet _dependencies; + + if (_dependencies.empty()) + { + _dependencies.insert(MODULE_COMMANDSYSTEM); + _dependencies.insert(MODULE_EVENTMANAGER); + _dependencies.insert(MODULE_SHADERSYSTEM); + _dependencies.insert(MODULE_UIMANAGER); + } + + return _dependencies; } -void MediaBrowser::registerCommandsAndPreferences() +void MediaBrowser::initialiseModule(const ApplicationContext& ctx) { - // Add a page to the given group - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Media Browser")); - page.appendCheckBox(_("Load media tree at startup"), RKEY_MEDIA_BROWSER_PRELOAD); + rMessage() << getName() << "::initialiseModule called." << std::endl; - GlobalCommandSystem().addCommand("ToggleMediaBrowser", toggle); + GlobalCommandSystem().addCommand("ToggleMediaBrowser", sigc::mem_fun(this, &MediaBrowser::togglePage)); GlobalEventManager().addCommand("ToggleMediaBrowser", "ToggleMediaBrowser"); + + // We need to create the liststore and widgets before attaching ourselves + // to the material manager as observer, as the attach() call below + // will invoke a realise() callback, which triggers a population + construct(); + + // The startup event will add this page to the group dialog tab + GlobalRadiant().signal_radiantStarted().connect( + sigc::mem_fun(*this, &MediaBrowser::onRadiantStartup) + ); + + // Attach to the MaterialManager to get notified on unrealise/realise + // events, in which case we're reloading the media tree + GlobalMaterialManager().attach(*this); +} + +void MediaBrowser::shutdownModule() +{ + GlobalMaterialManager().detach(*this); } +// Static module +module::StaticModule mediaBrowserModule; + } // namespace diff --git a/radiant/ui/mediabrowser/MediaBrowser.h b/radiant/ui/mediabrowser/MediaBrowser.h index b95dfe7302..9b44299d03 100644 --- a/radiant/ui/mediabrowser/MediaBrowser.h +++ b/radiant/ui/mediabrowser/MediaBrowser.h @@ -1,5 +1,6 @@ #pragma once +#include "imediabrowser.h" #include "iradiant.h" #include "imodule.h" #include "icommandsystem.h" @@ -20,9 +21,6 @@ namespace ui class TexturePreviewCombo; -class MediaBrowser; -typedef std::shared_ptr MediaBrowserPtr; - /** * \brief Media Browser page of the group dialog. * @@ -30,7 +28,7 @@ typedef std::shared_ptr MediaBrowserPtr; * into the texture window or applying directly to map geometry. */ class MediaBrowser : - public sigc::trackable, + public IMediaBrowser, public wxEvtHandler, public ModuleObserver // to monitor the MaterialManager module { @@ -97,32 +95,20 @@ class MediaBrowser : /* Tree selection query functions */ bool isDirectorySelected(); // is a directory selected - std::string getSelectedName(); // return name of selection // Populates the treeview void populate(); void onTreeStorePopulationFinished(wxutil::TreeModel::PopulationFinishedEvent& ev); - /** Return the singleton instance. - */ - static MediaBrowserPtr& getInstancePtr(); - public: - - /** Return the singleton instance. - */ - static MediaBrowser& getInstance(); - - /** Return the main widget for packing into - * the groupdialog or other parent container. - */ - wxWindow* getWidget(); - - /** Constructor creates GTK widgets. + /** Constructor creates widgets. */ MediaBrowser(); + // Returns the currently selected item, or an empty string if nothing is selected + std::string getSelection() override; + /** Set the given path as the current selection, highlighting it * in the tree view. * @@ -130,35 +116,26 @@ class MediaBrowser : * The fullname of the item to select, or the empty string if there * should be no selection. */ - void setSelection(const std::string& selection); - - /** greebo: Rebuilds the media tree from scratch (call this after - * a "RefreshShaders" command. - */ - void reloadMedia(); - - /** - * greebo: Handles the media tree preload - */ - static void init(); - - /** - * greebo: Registers the preference page and the commands - */ - static void registerCommandsAndPreferences(); + void setSelection(const std::string& selection) override; // ModuleObserver implementation, these are called when the MaterialManager // is emitting realise signals - void unrealise(); - void realise(); + void unrealise() override; + void realise() override; + + const std::string& getName() const override; + const StringSet& getDependencies() const override; + void initialiseModule(const ApplicationContext& ctx) override; + void shutdownModule() override; +private: // Radiant Event Listener - void onRadiantShutdown(); + void onRadiantStartup(); /** - * greebo: Static command target for toggling the mediabrowser tab in the groupdialog. - */ - static void toggle(const cmd::ArgumentList& args); + * greebo: Command target for toggling the mediabrowser tab in the groupdialog. + */ + void togglePage(const cmd::ArgumentList& args); }; } diff --git a/radiant/ui/prefdialog/PrefPage.cpp b/radiant/ui/prefdialog/PrefPage.cpp index 4422d38474..ad8d40e47a 100644 --- a/radiant/ui/prefdialog/PrefPage.cpp +++ b/radiant/ui/prefdialog/PrefPage.cpp @@ -23,10 +23,13 @@ PrefPage::PrefPage(wxWindow* parent, const settings::PreferencePage& settingsPag wxBoxSizer* overallVBox = new wxBoxSizer(wxVERTICAL); GetSizer()->Add(overallVBox, 1, wxEXPAND | wxALL, 12); - // Create the label - wxStaticText* titleLabel = new wxStaticText(this, wxID_ANY, _settingsPage.getTitle()); - titleLabel->SetFont(titleLabel->GetFont().Bold()); - overallVBox->Add(titleLabel, 0, wxBOTTOM, 12); + // Create the label, unless the page is empty + if (!settingsPage.isEmpty()) + { + wxStaticText* titleLabel = new wxStaticText(this, wxID_ANY, _settingsPage.getTitle()); + titleLabel->SetFont(titleLabel->GetFont().Bold()); + overallVBox->Add(titleLabel, 0, wxBOTTOM, 12); + } _table = new wxFlexGridSizer(1, 2, 6, 12); overallVBox->Add(_table, 1, wxEXPAND | wxLEFT, 6); // another 12 pixels to the left diff --git a/radiant/ui/texturebrowser/TextureBrowser.cpp b/radiant/ui/texturebrowser/TextureBrowser.cpp index baf3398120..68a446ccfc 100644 --- a/radiant/ui/texturebrowser/TextureBrowser.cpp +++ b/radiant/ui/texturebrowser/TextureBrowser.cpp @@ -7,6 +7,7 @@ #include "igroupdialog.h" #include "iradiant.h" #include "ipreferencesystem.h" +#include "imediabrowser.h" #include "wxutil/menu/IconTextMenuItem.h" #include "wxutil/GLWidget.h" @@ -18,7 +19,6 @@ #include "shaderlib.h" #include "selection/algorithm/Shader.h" #include "selection/shaderclipboard/ShaderClipboard.h" -#include "ui/mediabrowser/MediaBrowser.h" #include #include @@ -833,8 +833,8 @@ void TextureBrowser::onSeekInMediaBrowser() if (shader != NULL) { // Focus the MediaBrowser selection to the given shader - GlobalGroupDialog().setPage("mediabrowser"); - MediaBrowser::getInstance().setSelection(shader->getName()); + GlobalGroupDialog().setPage(GlobalMediaBrowser().getGroupDialogTabName()); + GlobalMediaBrowser().setSelection(shader->getName()); } } diff --git a/tools/msvc2015/DarkRadiant.vcxproj b/tools/msvc2015/DarkRadiant.vcxproj index 6f56d9e234..eee67e4f8e 100644 --- a/tools/msvc2015/DarkRadiant.vcxproj +++ b/tools/msvc2015/DarkRadiant.vcxproj @@ -553,6 +553,7 @@ + @@ -847,6 +848,7 @@ + diff --git a/tools/msvc2015/DarkRadiant.vcxproj.filters b/tools/msvc2015/DarkRadiant.vcxproj.filters index e29974df14..02406e5dac 100644 --- a/tools/msvc2015/DarkRadiant.vcxproj.filters +++ b/tools/msvc2015/DarkRadiant.vcxproj.filters @@ -892,6 +892,9 @@ src\ui\mapinfo + + src\ui + @@ -1815,6 +1818,9 @@ src\ui\mapinfo + + src\ui + diff --git a/tools/msvc2015/include.vcxproj b/tools/msvc2015/include.vcxproj index 81f70d98dd..5fc19593cb 100644 --- a/tools/msvc2015/include.vcxproj +++ b/tools/msvc2015/include.vcxproj @@ -150,6 +150,7 @@ + diff --git a/tools/msvc2015/uimanager.vcxproj b/tools/msvc2015/uimanager.vcxproj index 79ccc9bee2..b34a68d72a 100644 --- a/tools/msvc2015/uimanager.vcxproj +++ b/tools/msvc2015/uimanager.vcxproj @@ -325,8 +325,12 @@ - - + + + + + + @@ -344,8 +348,13 @@ - - + + + + + + + diff --git a/tools/msvc2015/uimanager.vcxproj.filters b/tools/msvc2015/uimanager.vcxproj.filters index ba150bbe5e..3ec5348060 100644 --- a/tools/msvc2015/uimanager.vcxproj.filters +++ b/tools/msvc2015/uimanager.vcxproj.filters @@ -11,6 +11,9 @@ {76d19dfa-2eef-475e-a743-e99b86aa0d2f} + + {be0c6eeb-cb4b-494a-a99b-7db2abeec7ea} + @@ -22,12 +25,6 @@ src - - src - - - src - src @@ -61,6 +58,24 @@ src\animationpreview + + src\menu + + + src\menu + + + src\menu + + + src\menu + + + src\menu + + + src\menu + @@ -72,12 +87,6 @@ src - - src - - - src - src @@ -114,6 +123,27 @@ src\animationpreview + + src\menu + + + src\menu + + + src\menu + + + src\menu + + + src\menu + + + src\menu + + + src\menu +