Skip to content

Commit

Permalink
#5122: Add ortho context menu entries to select/deselect by filter
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed Mar 29, 2020
1 parent 5ecb33e commit 78dd0d8
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 2 deletions.
1 change: 1 addition & 0 deletions include/iorthocontextmenu.h
Expand Up @@ -17,6 +17,7 @@ class IOrthoContextMenu :
SECTION_CREATE, // Create Entity, Create Speaker, etc.
SECTION_ACTION, // Make Visportals
SECTION_SELECTION_GROUPS, // Selection Groups
SECTION_FILTER, // Filter selectors
SECTION_LAYER, // Layer operations
SECTION_USER = 100,
};
Expand Down
2 changes: 2 additions & 0 deletions radiant/Makefile.am
Expand Up @@ -277,6 +277,8 @@ darkradiant_SOURCES = main.cpp \
ui/common/CommandEntry.cpp \
ui/common/EntityChooser.cpp \
ui/common/MapPreview.cpp \
ui/filters/FilterContextMenu.cpp \
ui/filters/FilterOrthoContextMenuItem.cpp \
ui/menu/FiltersMenu.cpp \
ui/prefdialog/GameSetupDialog.cpp \
ui/prefdialog/GameSetupPage.cpp \
Expand Down
4 changes: 2 additions & 2 deletions radiant/filters/BasicFilterSystem.cpp
Expand Up @@ -141,10 +141,10 @@ void BasicFilterSystem::initialiseModule(const ApplicationContext& ctx)
GlobalEventManager().addCommand("ActivateAllFilters", "ActivateAllFilters");
GlobalEventManager().addCommand("DeactivateAllFilters", "DeactivateAllFilters");

GlobalCommandSystem().addCommand("SelectObjectsByFilter",
GlobalCommandSystem().addCommand(SELECT_OBJECTS_BY_FILTER_CMD,
std::bind(&BasicFilterSystem::selectObjectsByFilterCmd, this, std::placeholders::_1), { cmd::ARGTYPE_STRING });

GlobalCommandSystem().addCommand("DeselectObjectsByFilter",
GlobalCommandSystem().addCommand(DESELECT_OBJECTS_BY_FILTER_CMD,
std::bind(&BasicFilterSystem::deselectObjectsByFilterCmd, this, std::placeholders::_1), { cmd::ARGTYPE_STRING });
}

Expand Down
3 changes: 3 additions & 0 deletions radiant/filters/BasicFilterSystem.h
Expand Up @@ -15,6 +15,9 @@
namespace filters
{

const char* const SELECT_OBJECTS_BY_FILTER_CMD = "SelectObjectsByFilter";
const char* const DESELECT_OBJECTS_BY_FILTER_CMD = "DeselectObjectsByFilter";

/** FilterSystem implementation class.
*/
class BasicFilterSystem :
Expand Down
19 changes: 19 additions & 0 deletions radiant/ui/UserInterfaceModule.cpp
Expand Up @@ -2,6 +2,7 @@

#include "i18n.h"
#include "ilayer.h"
#include "ifilter.h"
#include "iorthocontextmenu.h"
#include "ieventmanager.h"

Expand Down Expand Up @@ -32,6 +33,7 @@
#include "ui/entitylist/EntityList.h"
#include "textool/TexTool.h"
#include "modelexport/ExportAsModelDialog.h"
#include "ui/filters/FilterOrthoContextMenuItem.h"

namespace ui
{
Expand All @@ -44,6 +46,9 @@ namespace
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 char* const SELECT_BY_FILTER_TEXT = N_("Select by Filter...");
const char* const DESELECT_BY_FILTER_TEXT = N_("Deselect by Filter...");
}

const std::string& UserInterfaceModule::getName() const
Expand All @@ -61,6 +66,7 @@ const StringSet& UserInterfaceModule::getDependencies() const
_dependencies.insert(MODULE_LAYERS);
_dependencies.insert(MODULE_ORTHOCONTEXTMENU);
_dependencies.insert(MODULE_UIMANAGER);
_dependencies.insert(MODULE_FILTERSYSTEM);
}

return _dependencies;
Expand Down Expand Up @@ -105,6 +111,19 @@ void UserInterfaceModule::initialiseModule(const ApplicationContext& ctx)

GlobalRadiant().signal_radiantStarted().connect(
sigc::ptr_fun(LayerControlDialog::onRadiantStartup));

// Add the filter actions
GlobalOrthoContextMenu().addItem(
std::make_shared<FilterOrthoContextMenuItem>(_(SELECT_BY_FILTER_TEXT),
FilterOrthoContextMenuItem::SelectByFilter),
IOrthoContextMenu::SECTION_FILTER
);

GlobalOrthoContextMenu().addItem(
std::make_shared<FilterOrthoContextMenuItem>(_(DESELECT_BY_FILTER_TEXT),
FilterOrthoContextMenuItem::DeselectByFilter),
IOrthoContextMenu::SECTION_FILTER
);
}

void UserInterfaceModule::shutdownModule()
Expand Down
81 changes: 81 additions & 0 deletions radiant/ui/filters/FilterContextMenu.cpp
@@ -0,0 +1,81 @@
#include "FilterContextMenu.h"

#include "iuimanager.h"
#include "imap.h"
#include "wxutil/menu/IconTextMenuItem.h"

namespace ui
{

namespace
{
const std::string FILTER_ICON("iconFilter16.png");
}

FilterContextMenu::FilterContextMenu(OnSelectionFunc& onSelection) :
wxMenu(),
_onSelection(onSelection)
{}

void FilterContextMenu::populate()
{
// Clear all existing items
for (const MenuItemIdToLayerMapping::value_type& i : _menuItemMapping)
{
wxMenu* parentMenu = GetParent();

#ifdef __WXGTK__
// wxMSW and wxGTK are doing it differently (see comments in createMenuItems() below)
parentMenu = this;
#endif

if (parentMenu != nullptr)
{
parentMenu->Unbind(wxEVT_MENU, &FilterContextMenu::onActivate, this, i.first);
}

Delete(i.first);
}

_menuItemMapping.clear();

// Populate the map with all layer names and IDs
GlobalFilterSystem().forEachFilter(*this);
}

void FilterContextMenu::visit(const std::string& filterName)
{
// Create a new menuitem
wxMenuItem* menuItem = new wxutil::IconTextMenuItem(filterName, FILTER_ICON);

// Add it to the parent menu
Append(menuItem);

// remember the filter name for this item
_menuItemMapping[menuItem->GetId()] = filterName;

wxMenu* parentMenu = GetParent();

#ifdef __WXGTK__
// wxMSW and wxGTK are doing it differently (which is always great):
// in MSW the parent menu (of this class) is firing the events, whereas
// in GTK+ the submenu (this class) itself is doing that. So let's do an IFDEF
parentMenu = this;
#endif

// If we're packed to a parent menu (ourselves acting as submenu), connect the event to the parent
if (parentMenu != nullptr)
{
parentMenu->Bind(wxEVT_MENU, &FilterContextMenu::onActivate, this, menuItem->GetId());
}
}

void FilterContextMenu::onActivate(wxCommandEvent& ev)
{
assert(_menuItemMapping.find(ev.GetId()) != _menuItemMapping.end());

// Pass the call to the function
_onSelection(_menuItemMapping[ev.GetId()]);
}

}
43 changes: 43 additions & 0 deletions radiant/ui/filters/FilterContextMenu.h
@@ -0,0 +1,43 @@
#pragma once

#include <functional>
#include <map>
#include <memory>

#include "ifilter.h"

#include <wx/menu.h>

namespace ui
{

class FilterContextMenu :
public wxMenu,
public IFilterVisitor
{
public:
// The function to be called on menu selection, the name of the
// selected filter is passed along.
typedef std::function<void(const std::string& filterName)> OnSelectionFunc;

private:
OnSelectionFunc _onSelection;

typedef std::map<int, std::string> MenuItemIdToLayerMapping;
MenuItemIdToLayerMapping _menuItemMapping;

public:
FilterContextMenu(OnSelectionFunc& onSelection);

// IFilterVisitor implementation
void visit(const std::string& filterName) override;

// Loads filer names into the menu, clears existing items first
void populate();

private:
// wx Callback for menu selections
void onActivate(wxCommandEvent& ev);
};

} // namespace ui
66 changes: 66 additions & 0 deletions radiant/ui/filters/FilterOrthoContextMenuItem.cpp
@@ -0,0 +1,66 @@
#include "FilterOrthoContextMenuItem.h"

#include "ifilter.h"
#include "icommandsystem.h"
#include "filters/BasicFilterSystem.h"

namespace ui
{

namespace
{
const char* const FILTER_ICON = "iconFilter16.png";
}

FilterOrthoContextMenuItem::FilterOrthoContextMenuItem(const std::string& caption,
const FilterContextMenu::OnSelectionFunc& callback) :
wxutil::IconTextMenuItem(caption, FILTER_ICON),
_func(callback)
{
// Re-populate the submenu
_submenu = new FilterContextMenu(_func);

// wxWidgets will take care of deleting the submenu
SetSubMenu(_submenu);
}

FilterOrthoContextMenuItem::~FilterOrthoContextMenuItem()
{
if (GetMenu() != nullptr)
{
// Destroying a menu item doesn't de-register it from the parent menu
// To prevent double-deletions, we de-register the item on our own
GetMenu()->Remove(GetId());
}
}

wxMenuItem* FilterOrthoContextMenuItem::getMenuItem()
{
return this;
}

void FilterOrthoContextMenuItem::execute()
{}

bool FilterOrthoContextMenuItem::isSensitive()
{
return true;
}

void FilterOrthoContextMenuItem::preShow()
{
// Re-populate the submenu
_submenu->populate();
}

void FilterOrthoContextMenuItem::SelectByFilter(const std::string& filterName)
{
GlobalCommandSystem().executeCommand(filters::SELECT_OBJECTS_BY_FILTER_CMD, cmd::Argument(filterName));
}

void FilterOrthoContextMenuItem::DeselectByFilter(const std::string& filterName)
{
GlobalCommandSystem().executeCommand(filters::DESELECT_OBJECTS_BY_FILTER_CMD, cmd::Argument(filterName));
}

} // namespace
44 changes: 44 additions & 0 deletions radiant/ui/filters/FilterOrthoContextMenuItem.h
@@ -0,0 +1,44 @@
#pragma once

#include <memory>

#include "imenu.h"
#include "ifilter.h"

#include "FilterContextMenu.h"
#include "wxutil/menu/IconTextMenuItem.h"

namespace ui
{

// A menu item which can be packed into the OrthoContextMenu
class FilterOrthoContextMenuItem :
public IMenuItem,
public wxutil::IconTextMenuItem
{
private:
// Function object for the submenus
FilterContextMenu::OnSelectionFunc _func;

// The submenu (carrying the layer names)
// will be deallocated by wxWidgets on shutdown
FilterContextMenu* _submenu;

public:
FilterOrthoContextMenuItem(const std::string& caption,
const FilterContextMenu::OnSelectionFunc& callback);

~FilterOrthoContextMenuItem();

// IMenuItem implementation
wxMenuItem* getMenuItem() override;
void execute() override;
bool isSensitive() override;
void preShow() override;

// Gets called by the items in the submenus
static void SelectByFilter(const std::string& filterName);
static void DeselectByFilter(const std::string& filterName);
};

} // namespace
1 change: 1 addition & 0 deletions radiant/ui/ortho/OrthoContextMenu.cpp
Expand Up @@ -587,6 +587,7 @@ void OrthoContextMenu::constructMenu()
addSectionItems(SECTION_CREATE, true); // no spacer for first category
addSectionItems(SECTION_ACTION);
addSectionItems(SECTION_SELECTION_GROUPS);
addSectionItems(SECTION_FILTER);
addSectionItems(SECTION_LAYER);

// Add the rest of the sections
Expand Down
4 changes: 4 additions & 0 deletions tools/msvc/DarkRadiant.vcxproj
Expand Up @@ -823,6 +823,8 @@
<ClCompile Include="..\..\radiant\ui\eclasstree\EClassTreeBuilder.cpp" />
<ClCompile Include="..\..\radiant\ui\entitylist\EntityList.cpp" />
<ClCompile Include="..\..\radiant\ui\entitylist\GraphTreeModel.cpp" />
<ClCompile Include="..\..\radiant\ui\filters\FilterContextMenu.cpp" />
<ClCompile Include="..\..\radiant\ui\filters\FilterOrthoContextMenuItem.cpp" />
<ClCompile Include="..\..\radiant\ui\grid\GridManager.cpp" />
<ClCompile Include="..\..\radiant\ui\mainframe\TopLevelFrame.cpp" />
<ClCompile Include="..\..\radiant\ui\mapinfo\LayerInfoTab.cpp" />
Expand Down Expand Up @@ -1291,6 +1293,8 @@
<ClInclude Include="..\..\radiant\ui\entitylist\GraphTreeModel.h" />
<ClInclude Include="..\..\radiant\ui\entitylist\GraphTreeModelPopulator.h" />
<ClInclude Include="..\..\radiant\ui\entitylist\GraphTreeNode.h" />
<ClInclude Include="..\..\radiant\ui\filters\FilterContextMenu.h" />
<ClInclude Include="..\..\radiant\ui\filters\FilterOrthoContextMenuItem.h" />
<ClInclude Include="..\..\radiant\ui\grid\GridItem.h" />
<ClInclude Include="..\..\radiant\ui\grid\GridManager.h" />
<ClInclude Include="..\..\radiant\ui\mainframe\TopLevelFrame.h" />
Expand Down
15 changes: 15 additions & 0 deletions tools/msvc/DarkRadiant.vcxproj.filters
Expand Up @@ -323,6 +323,9 @@
<Filter Include="src\map\format\portable">
<UniqueIdentifier>{158557a6-7181-4ced-a45b-210fc3e54b34}</UniqueIdentifier>
</Filter>
<Filter Include="src\ui\filters">
<UniqueIdentifier>{b15324e3-378b-43fe-abfd-1ee2d99c5bd7}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\radiant\main.cpp">
Expand Down Expand Up @@ -1597,6 +1600,12 @@
<ClCompile Include="..\..\radiant\selection\selectionset\SelectionSetModule.cpp">
<Filter>src\selection\selectionset</Filter>
</ClCompile>
<ClCompile Include="..\..\radiant\ui\filters\FilterOrthoContextMenuItem.cpp">
<Filter>src\ui\filters</Filter>
</ClCompile>
<ClCompile Include="..\..\radiant\ui\filters\FilterContextMenu.cpp">
<Filter>src\ui\filters</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\radiant\RadiantModule.h">
Expand Down Expand Up @@ -3225,6 +3234,12 @@
<ClInclude Include="..\..\radiant\filters\SetObjectSelectionByFilterWalker.h">
<Filter>src\filters</Filter>
</ClInclude>
<ClInclude Include="..\..\radiant\ui\filters\FilterOrthoContextMenuItem.h">
<Filter>src\ui\filters</Filter>
</ClInclude>
<ClInclude Include="..\..\radiant\ui\filters\FilterContextMenu.h">
<Filter>src\ui\filters</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\radiant\darkradiant.rc" />
Expand Down

0 comments on commit 78dd0d8

Please sign in to comment.