FreeCAD

Форк
0
/
MenuManager.cpp 
438 строк · 13.8 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2005 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  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 Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
# include <QApplication>
26
# include <QMenu>
27
# include <QMenuBar>
28
#endif
29

30
#include "MenuManager.h"
31
#include "Application.h"
32
#include "Command.h"
33
#include "MainWindow.h"
34

35

36
using namespace Gui;
37

38

39
MenuItem::MenuItem() = default;
40

41
MenuItem::MenuItem(MenuItem* item)
42
{
43
    if (item) {
44
        item->appendItem(this);
45
    }
46
}
47

48
MenuItem::~MenuItem()
49
{
50
    clear();
51
}
52

53
void MenuItem::setCommand(const std::string& name)
54
{
55
    _name = name;
56
}
57

58
std::string MenuItem::command() const
59
{
60
    return _name;
61
}
62

63
bool MenuItem::hasItems() const
64
{
65
    return _items.count() > 0;
66
}
67

68
MenuItem* MenuItem::findItem(const std::string& name)
69
{
70
    if (_name == name) {
71
        return this;
72
    }
73
    else {
74
        for (auto& item : _items) {
75
            if (item->_name == name) {
76
                return item;
77
            }
78
        }
79
    }
80

81
    return nullptr;
82
}
83

84
MenuItem* MenuItem::findParentOf(const std::string& name)
85
{
86
    for (auto& item : _items) {
87
        if (item->_name == name) {
88
            return this;
89
        }
90
    }
91

92
    for (auto& item : _items) {
93
        if (item->findParentOf(name)) {
94
            return item;
95
        }
96
    }
97

98
    return nullptr;
99
}
100

101
MenuItem* MenuItem::copy() const
102
{
103
    auto root = new MenuItem;
104
    root->setCommand(command());
105

106
    for (auto& item : _items)
107
    {
108
        root->appendItem(item->copy());
109
    }
110

111
    return root;
112
}
113

114
uint MenuItem::count() const
115
{
116
    return _items.count();
117
}
118

119
void MenuItem::appendItem(MenuItem* item)
120
{
121
    _items.push_back(item);
122
}
123

124
bool MenuItem::insertItem(MenuItem* before, MenuItem* item)
125
{
126
    int pos = _items.indexOf(before);
127
    if (pos != -1) {
128
        _items.insert(pos, item);
129
        return true;
130
    }
131

132
    return false;
133
}
134

135
MenuItem* MenuItem::afterItem(MenuItem* item) const
136
{
137
    int pos = _items.indexOf(item);
138
    if (pos < 0 || pos+1 == _items.size()) {
139
        return nullptr;
140
    }
141
    return _items.at(pos+1);
142
}
143

144
void MenuItem::removeItem(MenuItem* item)
145
{
146
    int pos = _items.indexOf(item);
147
    if (pos != -1) {
148
        _items.removeAt(pos);
149
    }
150
}
151

152
void MenuItem::clear()
153
{
154
    for (auto& item : _items) {
155
        delete item;
156
    }
157
    _items.clear();
158
}
159

160
MenuItem& MenuItem::operator << (const std::string& command)
161
{
162
    auto item = new MenuItem(this);
163
    item->setCommand(command);
164
    return *this;
165
}
166

167
MenuItem& MenuItem::operator << (MenuItem* item)
168
{
169
    appendItem(item);
170
    return *this;
171
}
172

173
QList<MenuItem*> MenuItem::getItems() const
174
{
175
    return _items;
176
}
177

178
// -----------------------------------------------------------
179

180
MenuManager* MenuManager::_instance=nullptr;
181

182
MenuManager* MenuManager::getInstance()
183
{
184
    if ( !_instance ) {
185
        _instance = new MenuManager;
186
    }
187
    return _instance;
188
}
189

190
void MenuManager::destruct()
191
{
192
    delete _instance;
193
    _instance = nullptr;
194
}
195

196
MenuManager::MenuManager() = default;
197

198
MenuManager::~MenuManager() = default;
199

200
void MenuManager::setup(MenuItem* menuItems) const
201
{
202
    if (!menuItems) {
203
        return; // empty menu bar
204
    }
205

206
    QMenuBar* menuBar = getMainWindow()->menuBar();
207

208
#if 0
209
#if defined(FC_OS_MACOSX) && QT_VERSION >= 0x050900
210
    // Unknown Qt macOS bug observed with Qt >= 5.9.4 causes random crashes when viewing reused top level menus.
211
    menuBar->clear();
212
#endif
213

214
    // On Kubuntu 18.10 global menu has issues with FreeCAD 0.18 menu bar.
215
    // Optional parameter, clearing the menu bar, can be set as a workaround.
216
    // Clearing the menu bar can cause issues, when trying to access menu bar through Python.
217
    // https://forum.freecad.org/viewtopic.php?f=10&t=30340&start=440#p289330
218
    if (App::GetApplication().GetParameterGroupByPath
219
        ("User parameter:BaseApp/Preferences/MainWindow")->GetBool("ClearMenuBar",false)) {
220
        menuBar->clear();
221
    }
222
#else
223
    // In addition to the reason described in the above comments, there is
224
    // another more subtle one that's making clearing menu bar a necessity for
225
    // all platforms.
226
    //
227
    // By right, it should be fine for more than one command action having the
228
    // same shortcut but in different workbench. It should not require manual
229
    // conflict resolving in this case, as the action in an inactive workbench
230
    // is expected to be inactive as well, or else user may experience
231
    // seemingly random shortcut miss firing based on the order he/she
232
    // switches workbenches. In fact, this may be considered as an otherwise
233
    // difficult to implement feature of context aware shortcut, where a
234
    // specific shortcut can activate different actions under different
235
    // workbenches.
236
    //
237
    // This works as expected for action adding to a toolbar. As Qt will ignore
238
    // actions inside an invisible toolbar.  However, Qt refuse to do the same
239
    // for actions in a hidden menu action of a menu bar. This is very likely a
240
    // Qt bug, as the behavior does not seem to conform to Qt's documentation
241
    // of Qt::ShortcutContext.
242
    //
243
    // Clearing the menu bar, and recreate it every time when switching
244
    // workbench with only the active actions can solve this problem.
245
    menuBar->clear();
246
#endif
247

248
    QList<QAction*> actions = menuBar->actions();
249
    for (auto& item : menuItems->getItems())
250
    {
251
        // search for the menu action
252
        QAction* action = findAction(actions, QString::fromLatin1(item->command().c_str()));
253
        if (!action) {
254
            // There must be not more than one separator in the menu bar, so
255
            // we can safely remove it if available and append it at the end
256
            if (item->command() == "Separator") {
257
                action = menuBar->addSeparator();
258
                action->setObjectName(QLatin1String("Separator"));
259
            }
260
            else {
261
                // create a new menu
262
                std::string menuName = item->command();
263
                QMenu* menu = menuBar->addMenu(
264
                    QApplication::translate("Workbench", menuName.c_str()));
265
                action = menu->menuAction();
266
                menu->setObjectName(QString::fromLatin1(menuName.c_str()));
267
                action->setObjectName(QString::fromLatin1(menuName.c_str()));
268
            }
269

270
            // set the menu user data
271
            action->setData(QString::fromLatin1(item->command().c_str()));
272
        }
273
        else {
274
            // put the menu at the end
275
            menuBar->removeAction(action);
276
            menuBar->addAction(action);
277
            action->setVisible(true);
278
            int index = actions.indexOf(action);
279
            actions.removeAt(index);
280
        }
281

282
        // flll up the menu
283
        if (!action->isSeparator()) {
284
            setup(item, action->menu());
285
        }
286
    }
287

288
    // hide all menus which we don't need for the moment
289
    for (auto& action : actions) {
290
        action->setVisible(false);
291
    }
292

293
    // enable update again
294
    //menuBar->setUpdatesEnabled(true);
295
}
296

297
void MenuManager::setup(MenuItem* item, QMenu* menu) const
298
{
299
    CommandManager& mgr = Application::Instance->commandManager();
300
    QList<QAction*> actions = menu->actions();
301
    for (auto& item : item->getItems()) {
302
        // search for the menu item
303
        QList<QAction*> used_actions = findActions(actions, QString::fromLatin1(item->command().c_str()));
304
        if (used_actions.isEmpty()) {
305
            if (item->command() == "Separator") {
306
                QAction* action = menu->addSeparator();
307
                action->setObjectName(QLatin1String("Separator"));
308
                action->setData(QLatin1String("Separator"));
309
                used_actions.append(action);
310
            }
311
            else {
312
                if (item->hasItems()) {
313
                    // Creste a submenu
314
                    std::string menuName = item->command();
315
                    QMenu* submenu = menu->addMenu(QApplication::translate("Workbench", menuName.c_str()));
316
                    QAction* action = submenu->menuAction();
317
                    submenu->setObjectName(QString::fromLatin1(item->command().c_str()));
318
                    action->setObjectName(QString::fromLatin1(item->command().c_str()));
319
                    // set the menu user data
320
                    action->setData(QString::fromLatin1(item->command().c_str()));
321
                    used_actions.append(action);
322
                }
323
                else {
324
                    // A command can have more than one QAction
325
                    int count = menu->actions().count();
326
                    // Check if action was added successfully
327
                    if (mgr.addTo(item->command().c_str(), menu)) {
328
                        QList<QAction*> acts = menu->actions();
329
                        for (int i=count; i < acts.count(); i++) {
330
                            QAction* act = acts[i];
331
                            // set the menu user data
332
                            act->setData(QString::fromLatin1(item->command().c_str()));
333
                            used_actions.append(act);
334
                        }
335
                    }
336
                }
337
            }
338
        }
339
        else {
340
            for (auto& action : used_actions) {
341
                // put the menu item at the end
342
                menu->removeAction(action);
343
                menu->addAction(action);
344
                int index = actions.indexOf(action);
345
                actions.removeAt(index);
346
            }
347
        }
348

349
        // fill up the submenu
350
        if (item->hasItems()) {
351
            setup(item, used_actions.front()->menu());
352
        }
353
    }
354

355
    // remove all menu items which we don't need for the moment
356
    for (auto& action : actions) {
357
        menu->removeAction(action);
358
    }
359
}
360

361
void MenuManager::retranslate() const
362
{
363
    QMenuBar* menuBar = getMainWindow()->menuBar();
364
    for (auto& action : menuBar->actions()) {
365
        if (action->menu()) {
366
            retranslate(action->menu());
367
        }
368
    }
369
}
370

371
void MenuManager::retranslate(QMenu* menu) const
372
{
373
    // Note: Here we search for all menus and submenus to retranslate their
374
    // titles. To ease the translation for each menu the native name is set
375
    // as user data. However, there are special menus that are created by
376
    // actions for which the name of the according command name is set. For
377
    // such menus we have to use the command's menu text instead. Examples
378
    // for such actions are Std_RecentFiles, Std_Workbench or Std_FreezeViews.
379
    CommandManager& mgr = Application::Instance->commandManager();
380
    QByteArray menuName = menu->menuAction()->data().toByteArray();
381
    Command* cmd = mgr.getCommandByName(menuName);
382
    if (cmd) {
383
        menu->setTitle(
384
            QApplication::translate(cmd->className(),
385
                                    cmd->getMenuText()));
386
    }
387
    else {
388
        menu->setTitle(
389
            QApplication::translate("Workbench",
390
                                    (const char*)menuName));
391
    }
392
    for (auto& action : menu->actions()) {
393
        if (action->menu()) {
394
            retranslate(action->menu());
395
        }
396
    }
397
}
398

399
QAction* MenuManager::findAction(const QList<QAction*>& acts, const QString& item) const
400
{
401
    for (auto& action : acts) {
402
        if (action->data().toString() == item) {
403
            return action;
404
        }
405
    }
406

407
    return nullptr; // no item with the user data found
408
}
409

410
QList<QAction*> MenuManager::findActions(const QList<QAction*>& acts, const QString& item) const
411
{
412
    // It is possible that the user text of several actions match with 'item'.
413
    // But for the first match all following actions must match. For example
414
    // the Std_WindowsMenu command provides several actions with the same user
415
    // name.
416
    bool first_match = false;
417
    QList<QAction*> used;
418
    for (auto& action : acts) {
419
        if (action->data().toString() == item) {
420
            used.append(action);
421
            first_match = true;
422
            // get only one separator per request
423
            if (item == QLatin1String("Separator")) {
424
                break;
425
            }
426
        }
427
        else if (first_match) {
428
            break;
429
        }
430
    }
431

432
    return used;
433
}
434

435
void MenuManager::setupContextMenu(MenuItem* item, QMenu &menu) const
436
{
437
    setup(item, &menu);
438
}
439

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.