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