1 /* 2 Copyright (C) 2010-2014 Kristian Duske 3 4 This file is part of TrenchBroom. 5 6 TrenchBroom is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 TrenchBroom is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "Menu.h" 21 22 #include "PreferenceManager.h" 23 #include "View/ActionContext.h" 24 #include "View/CommandIds.h" 25 26 #include <wx/menu.h> 27 #include <wx/menuitem.h> 28 29 #include <algorithm> 30 31 namespace TrenchBroom { 32 namespace View { MenuItem(const Type type,MenuItemParent * parent)33 MenuItem::MenuItem(const Type type, MenuItemParent* parent) : 34 m_type(type), 35 m_parent(parent) {} 36 ~MenuItem()37 MenuItem::~MenuItem() {} 38 type() const39 MenuItem::Type MenuItem::type() const { 40 return m_type; 41 } 42 parent() const43 const MenuItemParent* MenuItem::parent() const { 44 return m_parent; 45 } 46 appendToMenu(wxMenu * menu,const bool withShortcuts) const47 void MenuItem::appendToMenu(wxMenu* menu, const bool withShortcuts) const { 48 doAppendToMenu(menu, withShortcuts); 49 } 50 appendToMenu(wxMenuBar * menu,const bool withShortcuts) const51 void MenuItem::appendToMenu(wxMenuBar* menu, const bool withShortcuts) const { 52 doAppendToMenu(menu, withShortcuts); 53 } 54 findActionMenuItem(const int id) const55 const ActionMenuItem* MenuItem::findActionMenuItem(const int id) const { 56 return doFindActionMenuItem(id); 57 } 58 getShortcutEntries(KeyboardShortcutEntry::List & entries)59 void MenuItem::getShortcutEntries(KeyboardShortcutEntry::List& entries) { 60 doGetShortcutEntries(entries); 61 } 62 resetShortcuts()63 void MenuItem::resetShortcuts() { 64 doResetShortcuts(); 65 } 66 doAppendToMenu(wxMenuBar * menu,const bool withShortcuts) const67 void MenuItem::doAppendToMenu(wxMenuBar* menu, const bool withShortcuts) const {} 68 doFindActionMenuItem(const int id) const69 const ActionMenuItem* MenuItem::doFindActionMenuItem(const int id) const { 70 return NULL; 71 } 72 doGetShortcutEntries(KeyboardShortcutEntry::List & entries)73 void MenuItem::doGetShortcutEntries(KeyboardShortcutEntry::List& entries) {} 74 doResetShortcuts()75 void MenuItem::doResetShortcuts() {} 76 SeparatorItem(MenuItemParent * parent)77 SeparatorItem::SeparatorItem(MenuItemParent* parent) : 78 MenuItem(Type_Separator, parent) {} 79 doAppendToMenu(wxMenu * menu,const bool withShortcuts) const80 void SeparatorItem::doAppendToMenu(wxMenu* menu, const bool withShortcuts) const { 81 menu->AppendSeparator(); 82 } 83 LabeledMenuItem(const Type type,MenuItemParent * parent)84 LabeledMenuItem::LabeledMenuItem(const Type type, MenuItemParent* parent) : 85 MenuItem(type, parent) {} 86 ~LabeledMenuItem()87 LabeledMenuItem::~LabeledMenuItem() {} 88 id() const89 int LabeledMenuItem::id() const { 90 return doGetId(); 91 } 92 label() const93 const String& LabeledMenuItem::label() const { 94 return doGetLabel(); 95 } 96 ActionMenuItem(const Type type,MenuItemParent * parent,const int id,const String & label,const KeyboardShortcut & defaultShortcut,const bool modifiable)97 ActionMenuItem::ActionMenuItem(const Type type, MenuItemParent* parent, const int id, const String& label, const KeyboardShortcut& defaultShortcut, const bool modifiable) : 98 LabeledMenuItem(type, parent), 99 m_action(id, label, modifiable), 100 m_preference(IO::Path("Menu") + path(label), defaultShortcut) { 101 assert(type == Type_Action || type == Type_Check); 102 } 103 ~ActionMenuItem()104 ActionMenuItem::~ActionMenuItem() {} 105 menuString(const wxString & suffix,const bool withShortcuts) const106 wxString ActionMenuItem::menuString(const wxString& suffix, const bool withShortcuts) const { 107 wxString caption; 108 caption << label(); 109 if (!suffix.empty()) 110 caption << " " << suffix; 111 if (!m_action.modifiable() || withShortcuts) 112 return shortcut().shortcutMenuItemString(caption); 113 else 114 return caption; 115 } 116 path(const String & label) const117 IO::Path ActionMenuItem::path(const String& label) const { 118 IO::Path path(label); 119 120 const MenuItemParent* p = parent(); 121 while (p != NULL) { 122 if (!p->label().empty()) 123 path = IO::Path(p->label()) + path; 124 p = p->parent(); 125 } 126 127 return path; 128 } 129 doAppendToMenu(wxMenu * menu,const bool withShortcuts) const130 void ActionMenuItem::doAppendToMenu(wxMenu* menu, const bool withShortcuts) const { 131 if (type() == Type_Action) 132 menu->Append(id(), menuString("", withShortcuts)); 133 else 134 menu->AppendCheckItem(id(), menuString("", withShortcuts)); 135 } 136 doFindActionMenuItem(int id) const137 const ActionMenuItem* ActionMenuItem::doFindActionMenuItem(int id) const { 138 if (id == m_action.id()) 139 return this; 140 return NULL; 141 } 142 doGetShortcutEntries(KeyboardShortcutEntry::List & entries)143 void ActionMenuItem::doGetShortcutEntries(KeyboardShortcutEntry::List& entries) { 144 entries.push_back(this); 145 } 146 doResetShortcuts()147 void ActionMenuItem::doResetShortcuts() { 148 PreferenceManager& prefs = PreferenceManager::instance(); 149 prefs.resetToDefault(m_preference); 150 } 151 doGetId() const152 int ActionMenuItem::doGetId() const { 153 return m_action.id(); 154 } 155 doGetLabel() const156 const String& ActionMenuItem::doGetLabel() const { 157 return m_action.name(); 158 } 159 doGetActionContext() const160 int ActionMenuItem::doGetActionContext() const { 161 return ActionContext_Any; 162 } 163 doGetModifiable() const164 bool ActionMenuItem::doGetModifiable() const { 165 return m_action.modifiable(); 166 } 167 doGetActionDescription() const168 wxString ActionMenuItem::doGetActionDescription() const { 169 return m_preference.path().asString(" > "); 170 } 171 doGetJsonString() const172 wxString ActionMenuItem::doGetJsonString() const { 173 const IO::Path menuPath = path(label()); 174 175 wxString str; 176 str << "{ path: [\"" << menuPath.asString("\", \"") << "\"], shortcut: " << shortcut().asJsonString() << " }"; 177 return str; 178 } 179 doGetPreference() const180 const Preference<KeyboardShortcut>& ActionMenuItem::doGetPreference() const { 181 return m_preference; 182 } 183 doGetShortcut() const184 const KeyboardShortcut& ActionMenuItem::doGetShortcut() const { 185 PreferenceManager& prefs = PreferenceManager::instance(); 186 return prefs.get(m_preference); 187 } 188 doUpdateShortcut(const KeyboardShortcut & shortcut)189 void ActionMenuItem::doUpdateShortcut(const KeyboardShortcut& shortcut) { 190 assert(m_action.modifiable()); 191 192 PreferenceManager& prefs = PreferenceManager::instance(); 193 prefs.set(m_preference, shortcut); 194 } 195 doGetAcceleratorEntry(const ActionView view) const196 wxAcceleratorEntry ActionMenuItem::doGetAcceleratorEntry(const ActionView view) const { 197 return shortcut().acceleratorEntry(id()); 198 } 199 MenuItemParent(const Type type,MenuItemParent * parent,const int id,const String & label)200 MenuItemParent::MenuItemParent(const Type type, MenuItemParent* parent, const int id, const String& label) : 201 LabeledMenuItem(type, parent), 202 m_id(id), 203 m_label(label) {} 204 ~MenuItemParent()205 MenuItemParent::~MenuItemParent() { 206 VectorUtils::clearAndDelete(m_items); 207 } 208 addItem(MenuItem * item)209 void MenuItemParent::addItem(MenuItem* item) { 210 m_items.push_back(item); 211 } 212 items() const213 const MenuItemParent::List& MenuItemParent::items() const { 214 return m_items; 215 } 216 items()217 MenuItemParent::List& MenuItemParent::items() { 218 return m_items; 219 } 220 doAppendToMenu(wxMenu * menu,const bool withShortcuts) const221 void MenuItemParent::doAppendToMenu(wxMenu* menu, const bool withShortcuts) const { 222 wxMenu* subMenu = buildMenu(withShortcuts); 223 224 wxMenuItem* subMenuItem = new wxMenuItem(subMenu, id(), label()); 225 subMenuItem->SetSubMenu(subMenu); 226 menu->Append(subMenuItem); 227 } 228 doAppendToMenu(wxMenuBar * menu,const bool withShortcuts) const229 void MenuItemParent::doAppendToMenu(wxMenuBar* menu, const bool withShortcuts) const { 230 wxMenu* subMenu = buildMenu(withShortcuts); 231 menu->Append(subMenu, label()); 232 } 233 buildMenu(const bool withShortcuts) const234 wxMenu* MenuItemParent::buildMenu(const bool withShortcuts) const { 235 wxMenu* subMenu = new wxMenu(); 236 237 MenuItem::List::const_iterator it, end; 238 for (it = m_items.begin(), end = m_items.end(); it != end; ++it) { 239 const MenuItem* item = *it; 240 item->appendToMenu(subMenu, withShortcuts); 241 } 242 243 return subMenu; 244 } 245 doFindActionMenuItem(int id) const246 const ActionMenuItem* MenuItemParent::doFindActionMenuItem(int id) const { 247 MenuItem::List::const_iterator it, end; 248 for (it = m_items.begin(), end = m_items.end(); it != end; ++it) { 249 const MenuItem* item = *it; 250 const ActionMenuItem* foundItem = item->findActionMenuItem(id); 251 if (foundItem != NULL) 252 return foundItem; 253 } 254 return NULL; 255 } 256 doGetShortcutEntries(KeyboardShortcutEntry::List & entries)257 void MenuItemParent::doGetShortcutEntries(KeyboardShortcutEntry::List& entries) { 258 MenuItem::List::const_iterator it, end; 259 for (it = m_items.begin(), end = m_items.end(); it != end; ++it) { 260 MenuItem* item = *it; 261 item->getShortcutEntries(entries); 262 } 263 } 264 doResetShortcuts()265 void MenuItemParent::doResetShortcuts() { 266 MenuItem::List::const_iterator it, end; 267 for (it = m_items.begin(), end = m_items.end(); it != end; ++it) { 268 MenuItem* item = *it; 269 item->resetShortcuts(); 270 } 271 } 272 doGetId() const273 int MenuItemParent::doGetId() const { 274 return m_id; 275 } 276 doGetLabel() const277 const String& MenuItemParent::doGetLabel() const { 278 return m_label; 279 } 280 Menu(MenuItemParent * parent,const int id,const String & label)281 Menu::Menu(MenuItemParent* parent, const int id, const String& label) : 282 MenuItemParent(Type_Menu, parent, id, label) {} 283 Menu(const String & label)284 Menu::Menu(const String& label) : 285 MenuItemParent(Type_Menu, NULL, wxID_ANY, label) {} 286 ~Menu()287 Menu::~Menu() {} 288 addModifiableActionItem(const int id,const String & label,const KeyboardShortcut & defaultShortcut)289 MenuItem* Menu::addModifiableActionItem(const int id, const String& label, const KeyboardShortcut& defaultShortcut) { 290 return addActionItem(id, label, defaultShortcut, true); 291 } 292 addUnmodifiableActionItem(const int id,const String & label,const KeyboardShortcut & defaultShortcut)293 MenuItem* Menu::addUnmodifiableActionItem(const int id, const String& label, const KeyboardShortcut& defaultShortcut) { 294 return addActionItem(id, label, defaultShortcut, false); 295 } 296 addModifiableCheckItem(const int id,const String & label,const KeyboardShortcut & defaultShortcut)297 MenuItem* Menu::addModifiableCheckItem(const int id, const String& label, const KeyboardShortcut& defaultShortcut) { 298 return addCheckItem(id, label, defaultShortcut, true); 299 } 300 addUnmodifiableCheckItem(const int id,const String & label,const KeyboardShortcut & defaultShortcut)301 MenuItem* Menu::addUnmodifiableCheckItem(const int id, const String& label, const KeyboardShortcut& defaultShortcut) { 302 return addCheckItem(id, label, defaultShortcut, false); 303 } 304 addSeparator()305 void Menu::addSeparator() { 306 MenuItem* item(new SeparatorItem(this)); 307 addItem(item); 308 } 309 addMenu(const String & label)310 Menu* Menu::addMenu(const String& label) { 311 return addMenu(wxID_ANY, label); 312 } 313 addMenu(int id,const String & label)314 Menu* Menu::addMenu(int id, const String& label) { 315 Menu* menu = new Menu(this, id, label); 316 addItem(menu); 317 return menu; 318 } 319 addActionItem(const int id,const String & label,const KeyboardShortcut & defaultShortcut,const bool modifiable)320 MenuItem* Menu::addActionItem(const int id, const String& label, const KeyboardShortcut& defaultShortcut, const bool modifiable) { 321 MenuItem* item = new ActionMenuItem(MenuItem::Type_Action, this, id, label, defaultShortcut, modifiable); 322 addItem(item); 323 return item; 324 } 325 addCheckItem(const int id,const String & label,const KeyboardShortcut & defaultShortcut,const bool modifiable)326 MenuItem* Menu::addCheckItem(const int id, const String& label, const KeyboardShortcut& defaultShortcut, const bool modifiable) { 327 MenuItem* item = new ActionMenuItem(MenuItem::Type_Check, this, id, label, defaultShortcut, modifiable); 328 addItem(item); 329 return item; 330 } 331 MenuBar()332 MenuBar::MenuBar() {} ~MenuBar()333 MenuBar::~MenuBar() { 334 VectorUtils::clearAndDelete(m_menus); 335 } 336 findActionMenuItem(int id) const337 const ActionMenuItem* MenuBar::findActionMenuItem(int id) const { 338 MenuList::const_iterator it, end; 339 for (it = m_menus.begin(), end = m_menus.end(); it != end; ++it) { 340 const Menu* menu = *it; 341 const ActionMenuItem* item = menu->findActionMenuItem(id); 342 if (item != NULL) 343 return item; 344 } 345 return NULL; 346 } 347 resetShortcuts()348 void MenuBar::resetShortcuts() { 349 MenuList::const_iterator it, end; 350 for (it = m_menus.begin(), end = m_menus.end(); it != end; ++it) { 351 Menu* menu = *it; 352 menu->resetShortcuts(); 353 } 354 } 355 addMenu(const String & label)356 Menu* MenuBar::addMenu(const String& label) { 357 Menu* menu = new Menu(label); 358 m_menus.push_back(menu); 359 return menu; 360 } 361 createMenuBar(const bool withShortcuts)362 wxMenuBar* MenuBar::createMenuBar(const bool withShortcuts) { 363 wxMenuBar* menuBar = new wxMenuBar(); 364 MenuList::const_iterator it, end; 365 for (it = m_menus.begin(), end = m_menus.end(); it != end; ++it) { 366 const Menu* menu = *it; 367 menu->appendToMenu(menuBar, withShortcuts); 368 } 369 return menuBar; 370 } 371 getShortcutEntries(KeyboardShortcutEntry::List & entries) const372 void MenuBar::getShortcutEntries(KeyboardShortcutEntry::List& entries) const { 373 MenuList::const_iterator it, end; 374 for (it = m_menus.begin(), end = m_menus.end(); it != end; ++it) { 375 Menu* menu = *it; 376 menu->getShortcutEntries(entries); 377 } 378 } 379 } 380 } 381