1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qttoolbardialog.h"
41 #include "ui_qttoolbardialog.h"
42 
43 #include <QtCore/QSet>
44 #include <QtGui/QtEvents>
45 #include <QtWidgets/QAction>
46 #include <QtWidgets/QToolBar>
47 #include <QtWidgets/QMainWindow>
48 #include <QtWidgets/QHeaderView>
49 #include <QtWidgets/QPushButton>
50 
51 #include <algorithm>
52 
53 QT_BEGIN_NAMESPACE
54 
55 class QtFullToolBarManagerPrivate;
56 
57 class QtFullToolBarManager : public QObject
58 {
59     Q_OBJECT
60 public:
61     QtFullToolBarManager(QObject *parent);
62     ~QtFullToolBarManager();
63 
64     void setMainWindow(QMainWindow *mainWindow);
65     QMainWindow *mainWindow() const;
66 
67     void addCategory(const QString &category);
68     bool hasCategory(const QString &category) const;
69     QStringList categories() const;
70     QList<QAction *> categoryActions(const QString &category) const;
71     QString actionCategory(QAction *action) const;
72 
73     // only non-separator
74     void addAction(QAction *action, const QString &category);
75 
76     void removeAction(QAction *action);
77 
78     QSet<QAction *> actions() const;
79     bool isWidgetAction(QAction *action) const;
80 
81     /*
82     Adds (registers) toolBar. Adds (registers) actions that already exists in toolBar.
83     Remembers toolbar and its actions as a default.
84     */
85     void addDefaultToolBar(QToolBar *toolBar, const QString &category);
86 
87     void removeDefaultToolBar(QToolBar *toolBar);
88     // NULL on action list means separator.
89     QMap<QToolBar *, QList<QAction *> > defaultToolBars() const;
90     bool isDefaultToolBar(QToolBar *toolBar) const;
91 
92     QToolBar *createToolBar(const QString &toolBarName);
93     void deleteToolBar(QToolBar *toolBar); // only those which were created, not added
94 
95     QList<QAction *> actions(QToolBar *toolBar) const;
96 
97     void setToolBars(const QMap<QToolBar *, QList<QAction *> > &actions);
98     void setToolBar(QToolBar *toolBar, const QList<QAction *> &actions);
99 
100     QMap<QToolBar *, QList<QAction *> > toolBarsActions() const;
101     QByteArray saveState(int version = 0) const;
102     bool restoreState(const QByteArray &state, int version = 0);
103 
104 public slots:
105 
106     void resetToolBar(QToolBar *toolBar);
107     void resetAllToolBars();
108 
109 signals:
110     void toolBarCreated(QToolBar *toolBar);
111     void toolBarRemoved(QToolBar *toolBar);
112 
113     /*
114     If QToolBarWidgetAction was in another tool bar and is inserted into
115     this toolBar, toolBarChanged is first emitted for other toolbar - without
116     that action. (Another approach may be that user first must call setToolBar
117     without that action for old tool bar)
118     */
119     void toolBarChanged(QToolBar *toolBar, const QList<QAction *> &actions);
120 
121 private:
122     QScopedPointer<QtFullToolBarManagerPrivate> d_ptr;
123     Q_DECLARE_PRIVATE(QtFullToolBarManager)
124     Q_DISABLE_COPY_MOVE(QtFullToolBarManager)
125 };
126 
127 class QtFullToolBarManagerPrivate
128 {
129     class QtFullToolBarManager *q_ptr;
130     Q_DECLARE_PUBLIC(QtFullToolBarManager)
131 
132 public:
133 
134     QToolBar *toolBarWidgetAction(QAction *action) const;
135     void removeWidgetActions(const QMap<QToolBar *, QList<QAction *> > &actions);
136 
137     enum {
138         VersionMarker = 0xff,
139         ToolBarMarker = 0xfe,
140         CustomToolBarMarker = 0xfd,
141     };
142 
143     void saveState(QDataStream &stream) const;
144     bool restoreState(QDataStream &stream) const;
145     QToolBar *findDefaultToolBar(const QString &objectName) const;
146     QAction *findAction(const QString &actionName) const;
147 
148     QToolBar *toolBarByName(const QString &toolBarName) const;
149 
150     QMap<QString, QList<QAction *> > categoryToActions;
151     QMap<QAction *, QString>         actionToCategory;
152 
153     QSet<QAction *> allActions;
154     QMap<QAction *, QToolBar *> widgetActions;
155     QSet<QAction *> regularActions;
156     QMap<QAction *, QList<QToolBar *> > actionToToolBars;
157 
158     QMap<QToolBar *, QList<QAction *> > toolBars;
159     QMap<QToolBar *, QList<QAction *> > toolBarsWithSeparators;
160     QMap<QToolBar *, QList<QAction *> > defaultToolBars;
161     QList<QToolBar *> customToolBars;
162 
163     QMainWindow *theMainWindow{nullptr};
164 };
165 
toolBarWidgetAction(QAction * action) const166 QToolBar *QtFullToolBarManagerPrivate::toolBarWidgetAction(QAction *action) const
167 {
168     if (widgetActions.contains(action))
169         return widgetActions.value(action);
170     return 0;
171 }
172 
removeWidgetActions(const QMap<QToolBar *,QList<QAction * >> & actions)173 void QtFullToolBarManagerPrivate::removeWidgetActions(const QMap<QToolBar *, QList<QAction *> >
174             &actions)
175 {
176     auto itToolBar = actions.constBegin();
177     while (itToolBar != actions.constEnd()) {
178         QToolBar *toolBar = itToolBar.key();
179         auto newActions = toolBars.value(toolBar);
180         auto newActionsWithSeparators = toolBarsWithSeparators.value(toolBar);
181 
182         QList<QAction *> removedActions;
183         const auto actionList = itToolBar.value();
184         for (QAction *action : actionList) {
185             if (newActions.contains(action) && toolBarWidgetAction(action) == toolBar) {
186                 newActions.removeAll(action);
187                 newActionsWithSeparators.removeAll(action);
188                 removedActions.append(action);
189             }
190         }
191 
192         //emit q_ptr->toolBarChanged(toolBar, newActions);
193 
194         toolBars.insert(toolBar, newActions);
195         toolBarsWithSeparators.insert(toolBar, newActionsWithSeparators);
196         for (QAction *oldAction : qAsConst(removedActions)) {
197             widgetActions.insert(oldAction, 0);
198             actionToToolBars[oldAction].removeAll(toolBar);
199         }
200 
201         ++itToolBar;
202     }
203 }
204 
saveState(QDataStream & stream) const205 void QtFullToolBarManagerPrivate::saveState(QDataStream &stream) const
206 {
207     stream << (uchar) ToolBarMarker;
208     stream << defaultToolBars.size();
209     auto itToolBar = defaultToolBars.constBegin();
210     while (itToolBar != defaultToolBars.constEnd()) {
211         QToolBar *tb = itToolBar.key();
212         if (tb->objectName().isEmpty()) {
213             qWarning("QtToolBarManager::saveState(): 'objectName' not set for QToolBar "
214                 "%p '%s', using 'windowTitle' instead",
215             tb, tb->windowTitle().toLocal8Bit().constData());
216             stream << tb->windowTitle();
217         } else {
218             stream << tb->objectName();
219         }
220 
221         const auto actions = toolBars.value(tb);
222         stream << actions.size();
223         for (QAction *action : actions) {
224             if (action) {
225                 if (action->objectName().isEmpty()) {
226                     qWarning("QtToolBarManager::saveState(): 'objectName' not set for QAction "
227                                 "%p '%s', using 'text' instead",
228                             action, action->text().toLocal8Bit().constData());
229                     stream << action->text();
230                 } else {
231                     stream << action->objectName();
232                 }
233             } else {
234                 stream << QString();
235             }
236         }
237         ++itToolBar;
238     }
239 
240 
241     stream << (uchar) CustomToolBarMarker;
242     stream << toolBars.size() - defaultToolBars.size();
243     itToolBar = toolBars.constBegin();
244     while (itToolBar != toolBars.constEnd()) {
245         QToolBar *tb = itToolBar.key();
246         if (!defaultToolBars.contains(tb)) {
247             stream << tb->objectName();
248             stream << tb->windowTitle();
249 
250             stream << toolBars[tb].size();
251 
252             const auto actions = toolBars.value(tb);
253             for (QAction *action : actions) {
254                 if (action) {
255                     if (action->objectName().isEmpty()) {
256                         qWarning("QtToolBarManager::saveState(): 'objectName' not set for QAction "
257                                     "%p '%s', using 'text' instead",
258                                 action, action->text().toLocal8Bit().constData());
259                         stream << action->text();
260                     } else {
261                         stream << action->objectName();
262                     }
263                 } else {
264                     stream << QString();
265                 }
266             }
267         }
268         ++itToolBar;
269     }
270 }
271 
restoreState(QDataStream & stream) const272 bool QtFullToolBarManagerPrivate::restoreState(QDataStream &stream) const
273 {
274     uchar tmarker;
275     stream >> tmarker;
276     if (tmarker != ToolBarMarker)
277         return false;
278 
279     int toolBars;
280     stream >> toolBars;
281     for (int i = 0; i < toolBars; i++) {
282         QString objectName;
283         stream >> objectName;
284         int actionCount;
285         stream >> actionCount;
286         QList<QAction *> actions;
287         for (int j = 0; j < actionCount; j++) {
288             QString actionName;
289             stream >> actionName;
290 
291             if (actionName.isEmpty())
292                 actions.append(0);
293             else {
294                 QAction *action = findAction(actionName);
295                 if (action)
296                     actions.append(action);
297             }
298         }
299 
300         QToolBar *toolBar = findDefaultToolBar(objectName);
301         if (toolBar)
302             q_ptr->setToolBar(toolBar, actions);
303     }
304 
305 
306 
307     uchar ctmarker;
308     stream >> ctmarker;
309     if (ctmarker != CustomToolBarMarker)
310         return false;
311 
312     auto oldCustomToolBars = customToolBars;
313 
314     stream >> toolBars;
315     for (int i = 0; i < toolBars; i++) {
316         QString objectName;
317         QString toolBarName;
318         int actionCount;
319         stream >> objectName;
320         stream >> toolBarName;
321         stream >> actionCount;
322         QList<QAction *> actions;
323         for (int j = 0; j < actionCount; j++) {
324             QString actionName;
325             stream >> actionName;
326 
327             if (actionName.isEmpty())
328                 actions.append(0);
329             else {
330                 QAction *action = findAction(actionName);
331                 if (action)
332                     actions.append(action);
333             }
334         }
335 
336         QToolBar *toolBar = toolBarByName(objectName);
337         if (toolBar) {
338             toolBar->setWindowTitle(toolBarName);
339             oldCustomToolBars.removeAll(toolBar);
340         }
341         else
342             toolBar = q_ptr->createToolBar(toolBarName);
343         if (toolBar) {
344             toolBar->setObjectName(objectName);
345             q_ptr->setToolBar(toolBar, actions);
346         }
347     }
348     for (QToolBar *toolBar : qAsConst(oldCustomToolBars))
349         q_ptr->deleteToolBar(toolBar);
350     return true;
351 }
352 
findDefaultToolBar(const QString & objectName) const353 QToolBar *QtFullToolBarManagerPrivate::findDefaultToolBar(const QString &objectName) const
354 {
355     auto itToolBar = defaultToolBars.constBegin();
356     while (itToolBar != defaultToolBars.constEnd()) {
357         QToolBar *tb = itToolBar.key();
358         if (tb->objectName() == objectName)
359             return tb;
360 
361         ++itToolBar;
362     }
363 
364     qWarning("QtToolBarManager::restoreState(): cannot find a QToolBar named "
365         "'%s', trying to match using 'windowTitle' instead.",
366         objectName.toLocal8Bit().constData());
367 
368     itToolBar = defaultToolBars.constBegin();
369     while (itToolBar != defaultToolBars.constEnd()) {
370         QToolBar *tb = itToolBar.key();
371         if (tb->windowTitle() == objectName)
372             return tb;
373 
374         ++itToolBar;
375     }
376     qWarning("QtToolBarManager::restoreState(): cannot find a QToolBar with "
377         "matching 'windowTitle' (looking for '%s').",
378         objectName.toLocal8Bit().constData());
379 
380     return 0;
381 }
382 
findAction(const QString & actionName) const383 QAction *QtFullToolBarManagerPrivate::findAction(const QString &actionName) const
384 {
385     auto it =
386         std::find_if(allActions.cbegin(), allActions.cend(),
387                      [&actionName] (const QAction *a) { return a->objectName() == actionName; });
388     if (it != allActions.cend())
389         return *it;
390     qWarning("QtToolBarManager::restoreState(): cannot find a QAction named "
391         "'%s', trying to match using 'text' instead.",
392         actionName.toLocal8Bit().constData());
393 
394     it = std::find_if(allActions.cbegin(), allActions.cend(),
395                       [&actionName] (const QAction *a) { return a->text() == actionName; });
396     if (it != allActions.cend())
397         return *it;
398     qWarning("QtToolBarManager::restoreState(): cannot find a QAction with "
399         "matching 'text' (looking for '%s').",
400         actionName.toLocal8Bit().constData());
401 
402     return 0;
403 }
404 
toolBarByName(const QString & toolBarName) const405 QToolBar *QtFullToolBarManagerPrivate::toolBarByName(const QString &toolBarName) const
406 {
407     auto itToolBar = toolBars.constBegin();
408     while (itToolBar != toolBars.constEnd()) {
409         QToolBar *toolBar = itToolBar.key();
410         if (toolBar->objectName() == toolBarName)
411             return toolBar;
412 
413         ++itToolBar;
414     }
415     return 0;
416 }
417 
418 //////////////////////////////
419 
QtFullToolBarManager(QObject * parent)420 QtFullToolBarManager::QtFullToolBarManager(QObject *parent)
421     : QObject(parent), d_ptr(new QtFullToolBarManagerPrivate)
422 {
423     d_ptr->q_ptr = this;
424 }
425 
~QtFullToolBarManager()426 QtFullToolBarManager::~QtFullToolBarManager()
427 {
428 }
429 
setMainWindow(QMainWindow * mainWindow)430 void QtFullToolBarManager::setMainWindow(QMainWindow *mainWindow)
431 {
432     d_ptr->theMainWindow = mainWindow;
433 }
434 
mainWindow() const435 QMainWindow *QtFullToolBarManager::mainWindow() const
436 {
437     return d_ptr->theMainWindow;
438 }
439 
addCategory(const QString & category)440 void QtFullToolBarManager::addCategory(const QString &category)
441 {
442     d_ptr->categoryToActions[category] = QList<QAction *>();
443 }
444 
hasCategory(const QString & category) const445 bool QtFullToolBarManager::hasCategory(const QString &category) const
446 {
447     return d_ptr->categoryToActions.contains(category);
448 }
449 
categories() const450 QStringList QtFullToolBarManager::categories() const
451 {
452     return d_ptr->categoryToActions.keys();
453 }
454 
categoryActions(const QString & category) const455 QList<QAction *> QtFullToolBarManager::categoryActions(const QString &category) const
456 {
457     const auto it = d_ptr->categoryToActions.constFind(category);
458     if (it != d_ptr->categoryToActions.constEnd())
459         return it.value();
460     return {};
461 }
462 
actionCategory(QAction * action) const463 QString QtFullToolBarManager::actionCategory(QAction *action) const
464 {
465     QMap<QAction *, QString>::ConstIterator it = d_ptr->actionToCategory.find(action);
466     if (it != d_ptr->actionToCategory.constEnd())
467         return it.value();
468     return QString();
469 }
470 
addAction(QAction * action,const QString & category)471 void QtFullToolBarManager::addAction(QAction *action, const QString &category)
472 {
473     if (!action)
474         return;
475     if (action->isSeparator())
476         return;
477     if (d_ptr->allActions.contains(action))
478         return;
479     if (QLatin1String(action->metaObject()->className()) ==
480                 QLatin1String("QToolBarWidgetAction"))
481         d_ptr->widgetActions.insert(action, 0);
482     else
483         d_ptr->regularActions.insert(action);
484     d_ptr->allActions.insert(action);
485     d_ptr->categoryToActions[category].append(action);
486     d_ptr->actionToCategory[action] = category;
487 }
488 
removeAction(QAction * action)489 void QtFullToolBarManager::removeAction(QAction *action)
490 {
491     if (!d_ptr->allActions.contains(action))
492         return;
493 
494     const auto toolBars = d_ptr->actionToToolBars[action];
495     for (QToolBar *toolBar : toolBars) {
496         d_ptr->toolBars[toolBar].removeAll(action);
497         d_ptr->toolBarsWithSeparators[toolBar].removeAll(action);
498 
499         toolBar->removeAction(action);
500     }
501 
502     auto itDefault = d_ptr->defaultToolBars.constBegin();
503     while (itDefault != d_ptr->defaultToolBars.constEnd()) {
504         if (itDefault.value().contains(action))
505             d_ptr->defaultToolBars[itDefault.key()].removeAll(action);
506 
507         itDefault++;
508     }
509 
510     d_ptr->allActions.remove(action);
511     d_ptr->widgetActions.remove(action);
512     d_ptr->regularActions.remove(action);
513     d_ptr->actionToToolBars.remove(action);
514 
515     QString category = d_ptr->actionToCategory.value(action);
516     d_ptr->actionToCategory.remove(action);
517     d_ptr->categoryToActions[category].removeAll(action);
518 
519     if (d_ptr->categoryToActions[category].isEmpty())
520         d_ptr->categoryToActions.remove(category);
521 }
522 
actions() const523 QSet<QAction *> QtFullToolBarManager::actions() const
524 {
525     return d_ptr->allActions;
526 }
527 
isWidgetAction(QAction * action) const528 bool QtFullToolBarManager::isWidgetAction(QAction *action) const
529 {
530     if (d_ptr->widgetActions.contains(action))
531         return true;
532     return false;
533 }
534 
addDefaultToolBar(QToolBar * toolBar,const QString & category)535 void QtFullToolBarManager::addDefaultToolBar(QToolBar *toolBar, const QString &category)
536 {
537     if (!toolBar)
538         return;
539     if (d_ptr->toolBars.contains(toolBar))
540         return;
541     // could be also checked if toolBar belongs to mainwindow
542 
543     QList<QAction *> newActionsWithSeparators;
544     QList<QAction *> newActions;
545     const auto actions = toolBar->actions();
546     for (QAction *action : actions) {
547         addAction(action, category);
548         if (d_ptr->widgetActions.contains(action))
549             d_ptr->widgetActions.insert(action, toolBar);
550         newActionsWithSeparators.append(action);
551         if (action->isSeparator())
552             action = 0;
553         else
554             d_ptr->actionToToolBars[action].append(toolBar);
555         newActions.append(action);
556     }
557     d_ptr->defaultToolBars.insert(toolBar, newActions);
558     //Below could be done by call setToolBar() if we want signal emission here.
559     d_ptr->toolBars.insert(toolBar, newActions);
560     d_ptr->toolBarsWithSeparators.insert(toolBar, newActionsWithSeparators);
561 }
562 
removeDefaultToolBar(QToolBar * toolBar)563 void QtFullToolBarManager::removeDefaultToolBar(QToolBar *toolBar)
564 {
565     if (!d_ptr->defaultToolBars.contains(toolBar))
566         return;
567 
568     const auto defaultActions = d_ptr->defaultToolBars[toolBar];
569     setToolBar(toolBar, QList<QAction *>());
570     for (QAction *action : defaultActions)
571         removeAction(action);
572 
573     d_ptr->toolBars.remove(toolBar);
574     d_ptr->toolBarsWithSeparators.remove(toolBar);
575     d_ptr->defaultToolBars.remove(toolBar);
576 
577     for (QAction *action : defaultActions) {
578         if (action)
579             toolBar->insertAction(0, action);
580         else
581             toolBar->insertSeparator(0);
582     }
583 }
584 
defaultToolBars() const585 QMap<QToolBar *, QList<QAction *> > QtFullToolBarManager::defaultToolBars() const
586 {
587     return d_ptr->defaultToolBars;
588 }
589 
isDefaultToolBar(QToolBar * toolBar) const590 bool QtFullToolBarManager::isDefaultToolBar(QToolBar *toolBar) const
591 {
592     if (d_ptr->defaultToolBars.contains(toolBar))
593         return true;
594     return false;
595 }
596 
createToolBar(const QString & toolBarName)597 QToolBar *QtFullToolBarManager::createToolBar(const QString &toolBarName)
598 {
599     if (!mainWindow())
600         return 0;
601     QToolBar *toolBar = new QToolBar(toolBarName, mainWindow());
602     int i = 1;
603     const QString prefix = QLatin1String("_Custom_Toolbar_%1");
604     QString name = prefix.arg(i);
605     while (d_ptr->toolBarByName(name))
606         name = prefix.arg(++i);
607     toolBar->setObjectName(name);
608     mainWindow()->addToolBar(toolBar);
609     d_ptr->customToolBars.append(toolBar);
610     d_ptr->toolBars.insert(toolBar, QList<QAction *>());
611     d_ptr->toolBarsWithSeparators.insert(toolBar, QList<QAction *>());
612     return toolBar;
613 }
614 
deleteToolBar(QToolBar * toolBar)615 void QtFullToolBarManager::deleteToolBar(QToolBar *toolBar)
616 {
617     if (!d_ptr->toolBars.contains(toolBar))
618         return;
619     if (d_ptr->defaultToolBars.contains(toolBar))
620         return;
621     setToolBar(toolBar, QList<QAction *>());
622     d_ptr->customToolBars.removeAll(toolBar);
623     d_ptr->toolBars.remove(toolBar);
624     d_ptr->toolBarsWithSeparators.remove(toolBar);
625     delete toolBar;
626 }
627 
actions(QToolBar * toolBar) const628 QList<QAction *> QtFullToolBarManager::actions(QToolBar *toolBar) const
629 {
630     if (d_ptr->toolBars.contains(toolBar))
631         return d_ptr->toolBars.value(toolBar);
632     return QList<QAction *>();
633 }
634 
setToolBars(const QMap<QToolBar *,QList<QAction * >> & actions)635 void QtFullToolBarManager::setToolBars(const QMap<QToolBar *, QList<QAction *> > &actions)
636 {
637     auto it = actions.constBegin();
638     while (it != actions.constEnd()) {
639         setToolBar(it.key(), it.value());
640         ++it;
641     }
642 }
643 
setToolBar(QToolBar * toolBar,const QList<QAction * > & actions)644 void QtFullToolBarManager::setToolBar(QToolBar *toolBar, const QList<QAction *> &actions)
645 {
646     if (!toolBar)
647         return;
648     if (!d_ptr->toolBars.contains(toolBar))
649         return;
650 
651     if (actions == d_ptr->toolBars[toolBar])
652         return;
653 
654     QMap<QToolBar *, QList<QAction *> > toRemove;
655 
656     QList<QAction *> newActions;
657     for (QAction *action : actions) {
658         if (!action || (!newActions.contains(action) && d_ptr->allActions.contains(action)))
659             newActions.append(action);
660 
661         QToolBar *oldToolBar = d_ptr->toolBarWidgetAction(action);
662         if (oldToolBar && oldToolBar != toolBar)
663             toRemove[oldToolBar].append(action);
664     }
665 
666     d_ptr->removeWidgetActions(toRemove);
667 
668     const auto oldActions = d_ptr->toolBarsWithSeparators.value(toolBar);
669     for (QAction *action : oldActions) {
670         /*
671         When addDefaultToolBar() separator actions could be checked if they are
672         inserted in other toolbars - if yes then create new one.
673         */
674         if (d_ptr->toolBarWidgetAction(action) == toolBar)
675             d_ptr->widgetActions.insert(action, 0);
676         toolBar->removeAction(action);
677         if (action->isSeparator())
678             delete action;
679         else
680             d_ptr->actionToToolBars[action].removeAll(toolBar);
681     }
682 
683     QList<QAction *> newActionsWithSeparators;
684     for (QAction *action : qAsConst(newActions)) {
685         QAction *newAction = 0;
686         if (!action)
687             newAction = toolBar->insertSeparator(0);
688         if (d_ptr->allActions.contains(action)) {
689             toolBar->insertAction(0, action);
690             newAction = action;
691             d_ptr->actionToToolBars[action].append(toolBar);
692         }
693         newActionsWithSeparators.append(newAction);
694     }
695     d_ptr->toolBars.insert(toolBar, newActions);
696     d_ptr->toolBarsWithSeparators.insert(toolBar, newActionsWithSeparators);
697 }
698 
toolBarsActions() const699 QMap<QToolBar *, QList<QAction *> > QtFullToolBarManager::toolBarsActions() const
700 {
701     return d_ptr->toolBars;
702 }
703 
resetToolBar(QToolBar * toolBar)704 void QtFullToolBarManager::resetToolBar(QToolBar *toolBar)
705 {
706     if (!isDefaultToolBar(toolBar))
707         return;
708     setToolBar(toolBar, defaultToolBars().value(toolBar));
709 }
710 
resetAllToolBars()711 void QtFullToolBarManager::resetAllToolBars()
712 {
713     setToolBars(defaultToolBars());
714     const auto oldCustomToolBars = d_ptr->customToolBars;
715     for (QToolBar *tb : oldCustomToolBars)
716         deleteToolBar(tb);
717 }
718 
saveState(int version) const719 QByteArray QtFullToolBarManager::saveState(int version) const
720 {
721     QByteArray data;
722     QDataStream stream(&data, QIODevice::WriteOnly);
723     stream << int(QtFullToolBarManagerPrivate::VersionMarker);
724     stream << version;
725     d_ptr->saveState(stream);
726     return data;
727 }
728 
restoreState(const QByteArray & state,int version)729 bool QtFullToolBarManager::restoreState(const QByteArray &state, int version)
730 {
731     QByteArray sd = state;
732     QDataStream stream(&sd, QIODevice::ReadOnly);
733     int marker, v;
734     stream >> marker;
735     stream >> v;
736     if (marker != QtFullToolBarManagerPrivate::VersionMarker || v != version)
737         return false;
738     return d_ptr->restoreState(stream);
739 }
740 
741 
742 class QtToolBarManagerPrivate
743 {
744     class QtToolBarManager *q_ptr;
745     Q_DECLARE_PUBLIC(QtToolBarManager)
746 public:
747     QtFullToolBarManager *manager;
748 };
749 
750 //////////////////////////////////////
751 
752 /*! \class QtToolBarManager
753     \internal
754     \inmodule QtDesigner
755     \since 4.4
756 
757     \brief The QtToolBarManager class provides toolbar management for
758     main windows.
759 
760     The QtToolBarManager is typically used with a QtToolBarDialog
761     which allows the user to customize the toolbars for a given main
762     window. The QtToolBarDialog class's functionality is controlled by
763     an instance of the QtToolBarManager class, and the main window is
764     specified using the QtToolBarManager class's setMainWindow()
765     function.
766 
767     The currently specified main window can be retrieved using the
768     mainWindow() function.
769 
770     The toolbar manager holds lists of the given main window's actions
771     and toolbars, and can add actions and toolbars to these
772     lists using the addAction() and addToolBar() functions
773     respectively. The actions can in addition be categorized
774     acccording to the user's preferences. The toolbar manager can also
775     remove custom actions and toolbars using the removeAction() and
776     removeToolBar() functions.
777 
778     Finally, the QtToolBarManager is able to save the customized state
779     of its toolbars using the saveState() function as well as restore
780     the toolbars' saved state using restoreState() function.
781 
782     \sa QtToolBarDialog
783 */
784 
785 /*!
786     Creates a toolbar manager with the given \a parent.
787 */
QtToolBarManager(QObject * parent)788 QtToolBarManager::QtToolBarManager(QObject *parent)
789     : QObject(parent), d_ptr(new QtToolBarManagerPrivate)
790 {
791     d_ptr->q_ptr = this;
792 
793     d_ptr->manager = new QtFullToolBarManager(this);
794 }
795 
796 /*!
797     Destroys the toolbar manager.
798 */
~QtToolBarManager()799 QtToolBarManager::~QtToolBarManager()
800 {
801 }
802 
803 /*!
804     Sets the main window upon which the toolbar manager operates, to
805     be the given \a mainWindow.
806 */
setMainWindow(QMainWindow * mainWindow)807 void QtToolBarManager::setMainWindow(QMainWindow *mainWindow)
808 {
809     d_ptr->manager->setMainWindow(mainWindow);
810 }
811 
812 /*!
813     Returns the main window associated this toolbar manager.
814 */
mainWindow() const815 QMainWindow *QtToolBarManager::mainWindow() const
816 {
817     return d_ptr->manager->mainWindow();
818 }
819 
820 /*!
821     Adds the given \a action to the given \a category in the manager's
822     list of actions. If the \a category doesn't exist it is created.
823     Only non separator actions can be added. If the action is already
824     added to the list, the function doesn't do anything.
825 
826     \sa removeAction()
827 */
addAction(QAction * action,const QString & category)828 void QtToolBarManager::addAction(QAction *action, const QString &category)
829 {
830     d_ptr->manager->addAction(action, category);
831 }
832 
833 /*!
834     Removes the specified \a action from the manager's list of
835     actions. The action is also removed from all the registered
836     toolbars.  If the specified \a action is the only action in its
837     category, that category is removed as well.
838 
839     \sa addAction()
840 */
removeAction(QAction * action)841 void QtToolBarManager::removeAction(QAction *action)
842 {
843     d_ptr->manager->removeAction(action);
844 }
845 
846 /*!
847     Adds the given \a toolBar to the manager's toolbar list.
848 
849     All the \a toolBar's actions are automatically added to the given
850     \a category in the manager's list of actions if they're not
851     already there. The manager remembers which toolbar the actions
852     belonged to, so, when the \a toolBar is removed, its actions will
853     be removed as well.
854 
855     Custom toolbars are created with the main window returned by
856     the mainWindow() function, as its parent.
857 
858     \sa removeToolBar()
859 */
addToolBar(QToolBar * toolBar,const QString & category)860 void QtToolBarManager::addToolBar(QToolBar *toolBar, const QString &category)
861 {
862     d_ptr->manager->addDefaultToolBar(toolBar, category);
863 }
864 
865 /*!
866     Removes the specified \a toolBar from the manager's list. All the
867     actions that existed in the specified \a toolBar when it was
868     added are removed as well.
869 
870     \sa addToolBar()
871 */
removeToolBar(QToolBar * toolBar)872 void QtToolBarManager::removeToolBar(QToolBar *toolBar)
873 {
874     d_ptr->manager->removeDefaultToolBar(toolBar);
875 }
876 
877 /*!
878     Returns the manager's toolbar list.
879 */
toolBars() const880 QList<QToolBar *> QtToolBarManager::toolBars() const
881 {
882     return d_ptr->manager->toolBarsActions().keys();
883 }
884 
885 /*
886 void QtToolBarManager::resetToolBar(QToolBar *toolBar)
887 {
888     d_ptr->manager->resetToolBar(toolBar);
889 }
890 
891 void QtToolBarManager::resetAllToolBars()
892 {
893     d_ptr->manager->resetAllToolBars();
894 }
895 */
896 
897 /*!
898     Saves the state of the toolbar manager's toolbars. The \a version
899     number is stored as part of the data.
900 
901     Identifies all the QToolBar and QAction objects by their object
902     name property. Ensure that this property is unique for each
903     QToolBar and QAction that you add using the QtToolBarManager.
904 
905     Returns an identifier for the state which can be passed along with
906     the version number to the restoreState() function to restore the
907     saved state.
908 
909     \sa restoreState()
910 */
saveState(int version) const911 QByteArray QtToolBarManager::saveState(int version) const
912 {
913     return d_ptr->manager->saveState(version);
914 }
915 
916 /*!
917     Restores the saved state of the toolbar manager's toolbars.  The
918     \a version number is compared with the version number of the
919     stored \a state.
920 
921     Returns true if the version numbers are matching and the toolbar
922     manager's state is restored; otherwise the toolbar manager's state
923     is left unchanged and the function returns false.
924 
925     Note that the state of the toolbar manager's toolbars should be
926     restored before restoring the state of the main window's toolbars
927     and dockwidgets using the QMainWindow::restoreState() function. In
928     that way the restoreState() function can create the custom
929     toolbars before the QMainWindow::restoreState() function restores
930     the custom toolbars' positions.
931 
932     \sa saveState()
933 */
restoreState(const QByteArray & state,int version)934 bool QtToolBarManager::restoreState(const QByteArray &state, int version)
935 {
936     return d_ptr->manager->restoreState(state, version);
937 }
938 
939 //////////////////////
940 
941 class ToolBarItem {
942 public:
ToolBarItem()943     ToolBarItem() : tb(0) {}
ToolBarItem(QToolBar * toolBar)944     ToolBarItem(QToolBar *toolBar) : tb(toolBar) {}
ToolBarItem(QToolBar * toolBar,const QString & toolBarName)945     ToolBarItem(QToolBar *toolBar, const QString &toolBarName)
946             : tb(toolBar), tbName(toolBarName) {}
ToolBarItem(const QString & toolBarName)947     ToolBarItem(const QString &toolBarName) : tb(0), tbName(toolBarName) {}
toolBar() const948     QToolBar *toolBar() const
949         { return tb; }
setToolBar(QToolBar * toolBar)950     void setToolBar(QToolBar *toolBar)
951         { tb = toolBar; }
toolBarName() const952     QString toolBarName() const
953         { return tbName; }
setToolBarName(const QString & toolBarName)954     void setToolBarName(const QString &toolBarName)
955         { tbName = toolBarName; }
956 private:
957     QToolBar *tb;
958     QString tbName;
959 };
960 
961 class QtToolBarDialogPrivate {
962     QtToolBarDialog *q_ptr;
963     Q_DECLARE_PUBLIC(QtToolBarDialog)
964 public:
QtToolBarDialogPrivate()965     QtToolBarDialogPrivate()
966         : toolBarManager(0),
967           currentAction(0),
968           currentToolBar(0)
969           { }
970 
971     ToolBarItem *createItem(QToolBar *toolBar);
972     ToolBarItem *createItem(const QString &toolBarName);
973     void deleteItem(ToolBarItem *item);
974 
975     void newClicked();
976     void removeClicked();
977     void defaultClicked();
978     void okClicked();
979     void applyClicked();
980     void cancelClicked();
981     void upClicked();
982     void downClicked();
983     void leftClicked();
984     void rightClicked();
985     void renameClicked();
986     void toolBarRenamed(QListWidgetItem *item);
987     void currentActionChanged(QTreeWidgetItem *current);
988     void currentToolBarChanged(QListWidgetItem *current);
989     void currentToolBarActionChanged(QListWidgetItem *current);
990 
991     void removeToolBar(ToolBarItem *item);
992     bool isDefaultToolBar(ToolBarItem *item) const;
993     void setButtons();
994     void clearOld();
995     void fillNew();
996     QtFullToolBarManager *toolBarManager;
997     QMap<ToolBarItem *, QList<QAction *> > currentState;
998     QMap<QToolBar *, ToolBarItem *> toolBarItems;
999     QSet<ToolBarItem *> createdItems;
1000     QSet<ToolBarItem *> removedItems;
1001 
1002     QSet<ToolBarItem *> allToolBarItems;
1003 
1004     // static
1005     QTreeWidgetItem *currentAction;
1006     QMap<QAction *, QTreeWidgetItem *> actionToItem;
1007     QMap<QTreeWidgetItem *, QAction *> itemToAction;
1008 
1009     // dynamic
1010     ToolBarItem *currentToolBar;
1011     QMap<ToolBarItem *, QListWidgetItem *> toolBarToItem;
1012     QMap<QListWidgetItem *, ToolBarItem *> itemToToolBar;
1013 
1014     // dynamic
1015     QMap<QAction *, QListWidgetItem *> actionToCurrentItem;
1016     QMap<QListWidgetItem *, QAction *> currentItemToAction;
1017 
1018     QMap<QAction *, ToolBarItem *> widgetActionToToolBar;
1019     QMap<ToolBarItem *, QSet<QAction *> > toolBarToWidgetActions;
1020 
1021     QString separatorText;
1022     Ui::QtToolBarDialog ui;
1023 };
1024 
createItem(QToolBar * toolBar)1025 ToolBarItem *QtToolBarDialogPrivate::createItem(QToolBar *toolBar)
1026 {
1027     if (!toolBar)
1028         return 0;
1029     ToolBarItem *item = new ToolBarItem(toolBar, toolBar->windowTitle());
1030     allToolBarItems.insert(item);
1031     return item;
1032 }
1033 
createItem(const QString & toolBarName)1034 ToolBarItem *QtToolBarDialogPrivate::createItem(const QString &toolBarName)
1035 {
1036     ToolBarItem *item = new ToolBarItem(toolBarName);
1037     allToolBarItems.insert(item);
1038     return item;
1039 }
1040 
deleteItem(ToolBarItem * item)1041 void QtToolBarDialogPrivate::deleteItem(ToolBarItem *item)
1042 {
1043     if (!allToolBarItems.contains(item))
1044         return;
1045     allToolBarItems.remove(item);
1046     delete item;
1047 }
1048 
clearOld()1049 void QtToolBarDialogPrivate::clearOld()
1050 {
1051     ui.actionTree->clear();
1052     ui.toolBarList->clear();
1053     ui.currentToolBarList->clear();
1054     ui.removeButton->setEnabled(false);
1055     ui.newButton->setEnabled(false);
1056     ui.upButton->setEnabled(false);
1057     ui.downButton->setEnabled(false);
1058     ui.leftButton->setEnabled(false);
1059     ui.rightButton->setEnabled(false);
1060 
1061     actionToItem.clear();
1062     itemToAction.clear();
1063     toolBarToItem.clear();
1064     itemToToolBar.clear();
1065     actionToCurrentItem.clear();
1066     currentItemToAction.clear();
1067     widgetActionToToolBar.clear();
1068     toolBarToWidgetActions.clear();
1069 
1070     toolBarItems.clear();
1071     currentState.clear();
1072     createdItems.clear();
1073     removedItems.clear();
1074     qDeleteAll(allToolBarItems);
1075     allToolBarItems.clear();
1076 
1077     currentToolBar = 0;
1078     currentAction = 0;
1079 }
1080 
fillNew()1081 void QtToolBarDialogPrivate::fillNew()
1082 {
1083     if (!toolBarManager)
1084         return;
1085 
1086     QTreeWidgetItem *item = new QTreeWidgetItem(ui.actionTree);
1087     item->setText(0, separatorText);
1088     ui.actionTree->setCurrentItem(item);
1089     currentAction = item;
1090     actionToItem.insert(0, item);
1091     itemToAction.insert(item, 0);
1092     const QStringList categories = toolBarManager->categories();
1093     for (const QString &category : categories) {
1094         QTreeWidgetItem *categoryItem = new QTreeWidgetItem(ui.actionTree);
1095         categoryItem->setText(0, category);
1096         const auto actions = toolBarManager->categoryActions(category);
1097         for (QAction *action : actions) {
1098             item = new QTreeWidgetItem(categoryItem);
1099             item->setText(0, action->text());
1100             item->setIcon(0, action->icon());
1101             item->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic);
1102             actionToItem.insert(action, item);
1103             itemToAction.insert(item, action);
1104             if (toolBarManager->isWidgetAction(action)) {
1105                 item->setData(0, Qt::ForegroundRole, QColor(Qt::blue));
1106                 widgetActionToToolBar.insert(action, 0);
1107             }
1108             item->setFlags(item->flags() | Qt::ItemIsDragEnabled);
1109         }
1110         categoryItem->setExpanded(true);
1111     }
1112     //ui.actionTree->sortItems(0, Qt::AscendingOrder);
1113 
1114     const auto toolBars = toolBarManager->toolBarsActions();
1115     auto it = toolBars.constBegin();
1116     while (it != toolBars.constEnd()) {
1117         QToolBar *toolBar = it.key();
1118         ToolBarItem *tbItem = createItem(toolBar);
1119         toolBarItems.insert(toolBar, tbItem);
1120         QListWidgetItem *item = new QListWidgetItem(toolBar->windowTitle(),
1121                 ui.toolBarList);
1122         toolBarToItem.insert(tbItem, item);
1123         itemToToolBar.insert(item, tbItem);
1124         const auto actions = it.value();
1125         for (QAction *action : actions) {
1126             if (toolBarManager->isWidgetAction(action)) {
1127                 widgetActionToToolBar.insert(action, tbItem);
1128                 toolBarToWidgetActions[tbItem].insert(action);
1129             }
1130         }
1131         currentState.insert(tbItem, actions);
1132         if (it == toolBars.constBegin())
1133             ui.toolBarList->setCurrentItem(item);
1134         if (isDefaultToolBar(tbItem))
1135             item->setData(Qt::ForegroundRole, QColor(Qt::darkGreen));
1136         else
1137             item->setFlags(item->flags() | Qt::ItemIsEditable);
1138 
1139         ++it;
1140     }
1141     ui.toolBarList->sortItems();
1142     setButtons();
1143 }
1144 
isDefaultToolBar(ToolBarItem * item) const1145 bool QtToolBarDialogPrivate::isDefaultToolBar(ToolBarItem *item) const
1146 {
1147     if (!item)
1148         return false;
1149     if (!item->toolBar())
1150         return false;
1151     return toolBarManager->isDefaultToolBar(item->toolBar());
1152 }
1153 
setButtons()1154 void QtToolBarDialogPrivate::setButtons()
1155 {
1156     bool newEnabled = false;
1157     bool removeEnabled = false;
1158     bool renameEnabled = false;
1159     bool upEnabled = false;
1160     bool downEnabled = false;
1161     bool leftEnabled = false;
1162     bool rightEnabled = false;
1163 
1164     if (toolBarManager) {
1165         newEnabled = true;
1166         removeEnabled = !isDefaultToolBar(currentToolBar);
1167         renameEnabled = removeEnabled;
1168         QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem();
1169         if (currentToolBarAction) {
1170             int row = ui.currentToolBarList->row(currentToolBarAction);
1171             upEnabled = row > 0;
1172             downEnabled = row < ui.currentToolBarList->count() - 1;
1173             leftEnabled = true;
1174         }
1175         if (currentAction && currentToolBar)
1176             rightEnabled = true;
1177     }
1178     ui.newButton->setEnabled(newEnabled);
1179     ui.removeButton->setEnabled(removeEnabled);
1180     ui.renameButton->setEnabled(renameEnabled);
1181     ui.upButton->setEnabled(upEnabled);
1182     ui.downButton->setEnabled(downEnabled);
1183     ui.leftButton->setEnabled(leftEnabled);
1184     ui.rightButton->setEnabled(rightEnabled);
1185 }
1186 
newClicked()1187 void QtToolBarDialogPrivate::newClicked()
1188 {
1189     QString toolBarName = QtToolBarDialog::tr("Custom Toolbar"); // = QInputDialog::getString();
1190     // produce unique name
1191     ToolBarItem *item = createItem(toolBarName);
1192     currentState.insert(item, QList<QAction *>());
1193     createdItems.insert(item);
1194     QListWidgetItem *i = new QListWidgetItem(toolBarName, ui.toolBarList);
1195     i->setFlags(i->flags() | Qt::ItemIsEditable);
1196     ui.toolBarList->setCurrentItem(i);
1197     itemToToolBar.insert(i, item);
1198     toolBarToItem.insert(item, i);
1199     ui.toolBarList->sortItems();
1200     ui.toolBarList->setCurrentItem(i);
1201     currentToolBarChanged(i);
1202     renameClicked();
1203 }
1204 
removeToolBar(ToolBarItem * item)1205 void QtToolBarDialogPrivate::removeToolBar(ToolBarItem *item)
1206 {
1207     if (!item)
1208         return;
1209     if (item->toolBar() && toolBarManager->isDefaultToolBar(item->toolBar()))
1210         return;
1211     if (!toolBarToItem.contains(item))
1212         return;
1213     QListWidgetItem *i = toolBarToItem.value(item);
1214     bool wasCurrent = false;
1215     if (i == ui.toolBarList->currentItem())
1216         wasCurrent = true;
1217     int row = ui.toolBarList->row(i);
1218     const auto itToolBar = toolBarToWidgetActions.find(item);
1219     if (itToolBar != toolBarToWidgetActions.end()) {
1220         for (QAction *action : qAsConst(itToolBar.value()))
1221             widgetActionToToolBar.insert(action, 0);
1222         toolBarToWidgetActions.erase(itToolBar);
1223     }
1224 
1225     currentState.remove(item);
1226     createdItems.remove(item);
1227     toolBarToItem.remove(item);
1228     itemToToolBar.remove(i);
1229     delete i;
1230     if (item->toolBar())
1231         removedItems.insert(item);
1232     else
1233         deleteItem(item);
1234     if (wasCurrent) {
1235         if (row == ui.toolBarList->count())
1236             row--;
1237         if (row < 0)
1238             ;
1239         else
1240             ui.toolBarList->setCurrentRow(row);
1241     }
1242     setButtons();
1243 }
1244 
removeClicked()1245 void QtToolBarDialogPrivate::removeClicked()
1246 {
1247     QListWidgetItem *i = ui.toolBarList->currentItem();
1248     if (!i)
1249         return;
1250     ToolBarItem *item = itemToToolBar.value(i);
1251     removeToolBar(item);
1252 }
1253 
defaultClicked()1254 void QtToolBarDialogPrivate::defaultClicked()
1255 {
1256     const auto defaultToolBars = toolBarManager->defaultToolBars();
1257     auto itToolBar = defaultToolBars.constBegin();
1258     while (itToolBar != defaultToolBars.constEnd()) {
1259         QToolBar *toolBar = itToolBar.key();
1260         ToolBarItem *toolBarItem = toolBarItems.value(toolBar);
1261 
1262         const auto tbwit = toolBarToWidgetActions.find(toolBarItem);
1263         if (tbwit != toolBarToWidgetActions.end()) {
1264             for (QAction *action : qAsConst(tbwit.value()))
1265                 widgetActionToToolBar.insert(action, 0);
1266             toolBarToWidgetActions.erase(tbwit);
1267         }
1268 
1269         currentState.remove(toolBarItem);
1270 
1271         for (QAction *action : itToolBar.value()) {
1272             if (toolBarManager->isWidgetAction(action)) {
1273                 ToolBarItem *otherToolBar = widgetActionToToolBar.value(action);
1274                 if (otherToolBar) {
1275                     toolBarToWidgetActions[otherToolBar].remove(action);
1276                     currentState[otherToolBar].removeAll(action);
1277                 }
1278                 widgetActionToToolBar.insert(action, toolBarItem);
1279                 toolBarToWidgetActions[toolBarItem].insert(action);
1280             }
1281         }
1282         currentState.insert(toolBarItem, itToolBar.value());
1283 
1284         ++itToolBar;
1285     }
1286     currentToolBarChanged(toolBarToItem.value(currentToolBar));
1287 
1288     const auto toolBars = currentState.keys();
1289     for (ToolBarItem *tb : toolBars)
1290         removeToolBar(tb);
1291 }
1292 
okClicked()1293 void QtToolBarDialogPrivate::okClicked()
1294 {
1295     applyClicked();
1296     q_ptr->accept();
1297 }
1298 
applyClicked()1299 void QtToolBarDialogPrivate::applyClicked()
1300 {
1301     const auto toolBars = currentState;
1302     auto itToolBar = toolBars.constBegin();
1303     while (itToolBar != toolBars.constEnd()) {
1304         ToolBarItem *item = itToolBar.key();
1305         QToolBar *toolBar = item->toolBar();
1306         if (toolBar) {
1307             toolBarManager->setToolBar(toolBar, itToolBar.value());
1308             toolBar->setWindowTitle(item->toolBarName());
1309         }
1310 
1311         ++itToolBar;
1312     }
1313 
1314     const QSet<ToolBarItem *> toRemove = removedItems;
1315     for (ToolBarItem *item : toRemove) {
1316         QToolBar *toolBar = item->toolBar();
1317         removedItems.remove(item);
1318         currentState.remove(item);
1319         deleteItem(item);
1320         if (toolBar)
1321             toolBarManager->deleteToolBar(toolBar);
1322     }
1323 
1324     const QSet<ToolBarItem *> toCreate = createdItems;
1325     for (ToolBarItem *item : toCreate) {
1326         QString toolBarName = item->toolBarName();
1327         createdItems.remove(item);
1328         const auto actions = currentState.value(item);
1329         QToolBar *toolBar = toolBarManager->createToolBar(toolBarName);
1330         item->setToolBar(toolBar);
1331         toolBarManager->setToolBar(toolBar, actions);
1332     }
1333 }
1334 
upClicked()1335 void QtToolBarDialogPrivate::upClicked()
1336 {
1337     QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem();
1338     if (!currentToolBarAction)
1339         return;
1340     int row = ui.currentToolBarList->row(currentToolBarAction);
1341     if (row == 0)
1342         return;
1343     ui.currentToolBarList->takeItem(row);
1344     int newRow = row - 1;
1345     ui.currentToolBarList->insertItem(newRow, currentToolBarAction);
1346     auto actions = currentState.value(currentToolBar);
1347     QAction *action = actions.at(row);
1348     actions.removeAt(row);
1349     actions.insert(newRow, action);
1350     currentState.insert(currentToolBar, actions);
1351     ui.currentToolBarList->setCurrentItem(currentToolBarAction);
1352     setButtons();
1353 }
1354 
downClicked()1355 void QtToolBarDialogPrivate::downClicked()
1356 {
1357     QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem();
1358     if (!currentToolBarAction)
1359         return;
1360     int row = ui.currentToolBarList->row(currentToolBarAction);
1361     if (row == ui.currentToolBarList->count() - 1)
1362         return;
1363     ui.currentToolBarList->takeItem(row);
1364     int newRow = row + 1;
1365     ui.currentToolBarList->insertItem(newRow, currentToolBarAction);
1366     auto actions = currentState.value(currentToolBar);
1367     QAction *action = actions.at(row);
1368     actions.removeAt(row);
1369     actions.insert(newRow, action);
1370     currentState.insert(currentToolBar, actions);
1371     ui.currentToolBarList->setCurrentItem(currentToolBarAction);
1372     setButtons();
1373 }
1374 
leftClicked()1375 void QtToolBarDialogPrivate::leftClicked()
1376 {
1377     QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem();
1378     if (!currentToolBarAction)
1379         return;
1380     int row = ui.currentToolBarList->row(currentToolBarAction);
1381     currentState[currentToolBar].removeAt(row);
1382     QAction *action = currentItemToAction.value(currentToolBarAction);
1383     if (widgetActionToToolBar.contains(action)) {
1384         ToolBarItem *item = widgetActionToToolBar.value(action);
1385         if (item == currentToolBar) { // have to be
1386             toolBarToWidgetActions[item].remove(action);
1387             if (toolBarToWidgetActions[item].isEmpty())
1388                 toolBarToWidgetActions.remove(item);
1389         }
1390         widgetActionToToolBar.insert(action, 0);
1391     }
1392     if (action)
1393         actionToCurrentItem.remove(action);
1394     currentItemToAction.remove(currentToolBarAction);
1395     delete currentToolBarAction;
1396     if (row == ui.currentToolBarList->count())
1397         row--;
1398     if (row >= 0) {
1399         QListWidgetItem *item = ui.currentToolBarList->item(row);
1400         ui.currentToolBarList->setCurrentItem(item);
1401     }
1402     setButtons();
1403 }
1404 
rightClicked()1405 void QtToolBarDialogPrivate::rightClicked()
1406 {
1407     if (!currentAction)
1408         return;
1409     if (!currentToolBar)
1410         return;
1411     QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem();
1412 
1413     QAction *action = itemToAction.value(currentAction);
1414     QListWidgetItem *item = 0;
1415     if (action) {
1416         if (currentState[currentToolBar].contains(action)) {
1417             item = actionToCurrentItem.value(action);
1418             if (item == currentToolBarAction)
1419                 return;
1420             int row = ui.currentToolBarList->row(item);
1421             ui.currentToolBarList->takeItem(row);
1422             currentState[currentToolBar].removeAt(row);
1423             // only reorder here
1424         } else {
1425             item = new QListWidgetItem(action->text());
1426             item->setIcon(action->icon());
1427             item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic);
1428             currentItemToAction.insert(item, action);
1429             actionToCurrentItem.insert(action, item);
1430             if (widgetActionToToolBar.contains(action)) {
1431                 item->setData(Qt::ForegroundRole, QColor(Qt::blue));
1432                 ToolBarItem *toolBar = widgetActionToToolBar.value(action);
1433                 if (toolBar) {
1434                     currentState[toolBar].removeAll(action);
1435                     toolBarToWidgetActions[toolBar].remove(action);
1436                     if (toolBarToWidgetActions[toolBar].isEmpty())
1437                         toolBarToWidgetActions.remove(toolBar);
1438                 }
1439                 widgetActionToToolBar.insert(action, currentToolBar);
1440                 toolBarToWidgetActions[currentToolBar].insert(action);
1441             }
1442         }
1443     } else {
1444         item = new QListWidgetItem(separatorText);
1445         currentItemToAction.insert(item, 0);
1446     }
1447 
1448     int row = ui.currentToolBarList->count();
1449     if (currentToolBarAction) {
1450         row = ui.currentToolBarList->row(currentToolBarAction) + 1;
1451     }
1452     ui.currentToolBarList->insertItem(row, item);
1453     currentState[currentToolBar].insert(row, action);
1454     ui.currentToolBarList->setCurrentItem(item);
1455 
1456     setButtons();
1457 }
1458 
renameClicked()1459 void QtToolBarDialogPrivate::renameClicked()
1460 {
1461     if (!currentToolBar)
1462         return;
1463 
1464     QListWidgetItem *item = toolBarToItem.value(currentToolBar);
1465     ui.toolBarList->editItem(item);
1466 }
1467 
toolBarRenamed(QListWidgetItem * item)1468 void QtToolBarDialogPrivate::toolBarRenamed(QListWidgetItem *item)
1469 {
1470     if (!currentToolBar)
1471         return;
1472 
1473     ToolBarItem *tbItem = itemToToolBar.value(item);
1474     if (!tbItem)
1475         return;
1476     tbItem->setToolBarName(item->text());
1477     //ui.toolBarList->sortItems();
1478 }
1479 
currentActionChanged(QTreeWidgetItem * current)1480 void QtToolBarDialogPrivate::currentActionChanged(QTreeWidgetItem *current)
1481 {
1482     if (itemToAction.contains(current))
1483         currentAction = current;
1484     else
1485         currentAction = NULL;
1486     setButtons();
1487 }
1488 
currentToolBarChanged(QListWidgetItem * current)1489 void QtToolBarDialogPrivate::currentToolBarChanged(QListWidgetItem *current)
1490 {
1491     currentToolBar = itemToToolBar.value(current);
1492     ui.currentToolBarList->clear();
1493     actionToCurrentItem.clear();
1494     currentItemToAction.clear();
1495     setButtons();
1496     if (!currentToolBar) {
1497         return;
1498     }
1499     const auto actions = currentState.value(currentToolBar);
1500     QListWidgetItem *first = 0;
1501     for (QAction *action : actions) {
1502         QString actionName = separatorText;
1503         if (action)
1504             actionName = action->text();
1505         QListWidgetItem *item = new QListWidgetItem(actionName, ui.currentToolBarList);
1506         if (action) {
1507             item->setIcon(action->icon());
1508             item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic);
1509             actionToCurrentItem.insert(action, item);
1510             if (widgetActionToToolBar.contains(action))
1511                 item->setData(Qt::ForegroundRole, QColor(Qt::blue));
1512         }
1513         currentItemToAction.insert(item, action);
1514         if (!first)
1515             first = item;
1516     }
1517     if (first)
1518         ui.currentToolBarList->setCurrentItem(first);
1519 }
1520 
currentToolBarActionChanged(QListWidgetItem *)1521 void QtToolBarDialogPrivate::currentToolBarActionChanged(QListWidgetItem *)
1522 {
1523     setButtons();
1524 }
1525 
cancelClicked()1526 void QtToolBarDialogPrivate::cancelClicked()
1527 {
1528     // just nothing
1529     q_ptr->reject();
1530 }
1531 
1532 //////////////////////
1533 /*
1534 class FeedbackItemDelegate : public QItemDelegate
1535 {
1536     Q_OBJECT
1537 public:
1538     FeedbackItemDelegate(QObject *parent = 0) : QItemDelegate(parent) { }
1539 
1540     virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
1541                     const QModelIndex & index) const;
1542     virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
1543 };
1544 
1545 void FeedbackItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
1546                             const QModelIndex &index) const
1547 {
1548     if ()
1549     painter->save();
1550     QRect r = option.rect;
1551     float yCentral = r.height() / 2.0;
1552     float margin = 2.0;
1553     float arrowWidth = 5.0;
1554     float width = 20;
1555     qDebug("rect: x %d, y %d, w %d, h %d", r.x(), r.y(), r.width(), r.height());
1556     QLineF lineBase(0.0 + margin, r.y() + yCentral, width - margin, r.y() + yCentral);
1557     QLineF lineArrowLeft(width - margin - arrowWidth, r.y() + yCentral - arrowWidth,
1558                     width - margin, r.y() + yCentral);
1559     QLineF lineArrowRight(width - margin - arrowWidth, r.y() + yCentral + arrowWidth,
1560                     width - margin, r.y() + yCentral);
1561     painter->drawLine(lineBase);
1562     painter->drawLine(lineArrowLeft);
1563     painter->drawLine(lineArrowRight);
1564     painter->translate(QPoint(width, 0));
1565     QItemDelegate::paint(painter, option, index);
1566     painter->restore();
1567 }
1568 
1569 QSize FeedbackItemDelegate::sizeHint(const QStyleOptionViewItem &option,
1570             const QModelIndex &index) const
1571 {
1572     //return QItemDelegate::sizeHint(option, index);
1573     QSize s = QItemDelegate::sizeHint(option, index);
1574     s.setWidth(s.width() - 20);
1575     return s;
1576 }
1577 
1578 class QtToolBarListWidget : public QListWidget
1579 {
1580     Q_OBJECT
1581 public:
1582     QtToolBarListWidget(QWidget *parent) : QListWidget(parent), actionDrag(false) {}
1583 
1584 protected:
1585     void startDrag(Qt::DropActions supportedActions);
1586 
1587     void dragEnterEvent(QDragEnterEvent *event);
1588     void dragMoveEvent(QDragMoveEvent *event);
1589     void dragLeaveEvent(QDragLeaveEvent *);
1590     void dropEvent(QDropEvent *event);
1591 
1592     void setDragAction(const QString *action) { actionName = action; }
1593 private:
1594     QPersistentModelIndex lastDropIndicator;
1595     QString actionName;
1596     bool actionDrag;
1597 };
1598 
1599 void QtToolBarListWidget::startDrag(Qt::DropActions supportedActions)
1600 {
1601     QListWidgetItem *item = currentItem();
1602     if (item) {
1603         actionName = QString();
1604         emit aboutToDrag(item);
1605         if (!actionName.isEmpty()) {
1606             QDrag *drag = new QDrag(this);
1607             QMimeData *data = new QMimeData;
1608             data->setData("action", actionName.toLocal8Bit().constData());
1609             drag->setMimeData(data);
1610             drag->exec(supportedActions);
1611         }
1612     }
1613 }
1614 
1615 void QtToolBarListWidget::dragEnterEvent(QDragEnterEvent *event)
1616 {
1617     const QMimeData *mime = event->mimeData();
1618     actionDrag = mime->hasFormat("action");
1619     if (actionDrag)
1620         event->accept();
1621     else
1622         event->ignore();
1623 }
1624 
1625 void QtToolBarListWidget::dragMoveEvent(QDragMoveEvent *event)
1626 {
1627     event->ignore();
1628     if (actionDrag) {
1629         QPoint p = event->pos();
1630         QListWidgetItem *item = itemAt(p);
1631         Indicator indic = QtToolBarListWidget::None;
1632         if (item) {
1633             QRect rect = visualItemRect(item);
1634             if (p.y() - rect.top() < rect.height() / 2)
1635                 indic = QtToolBarListWidget::Above;
1636             else
1637                 indic = QtToolBarListWidget::Below;
1638         }
1639         setIndicator(item, indic);
1640         event->accept();
1641     }
1642 }
1643 
1644 void QtToolBarListWidget::dragLeaveEvent(QDragLeaveEvent *)
1645 {
1646     if (actionDrag) {
1647         actionDrag = false;
1648         setIndicator(item, QtToolBarListWidget::None);
1649     }
1650 }
1651 
1652 void QtToolBarListWidget::dropEvent(QDropEvent *event)
1653 {
1654     if (actionDrag) {
1655         QListWidgetItem *item = indicatorItem();
1656         Indicator indic = indicator();
1657         QByteArray array = event->mimeData()->data("action");
1658         QDataStream stream(&array, QIODevice::ReadOnly);
1659         QString action;
1660         stream >> action;
1661         emit actionDropped(action, item, );
1662 
1663         actionDrag = false;
1664         setIndicator(item, QtToolBarListWidget::None);
1665     }
1666 }
1667 */
1668 
1669 /*! \class QtToolBarDialog
1670     \internal
1671     \inmodule QtDesigner
1672     \since 4.4
1673 
1674     \brief The QtToolBarDialog class provides a dialog for customizing
1675     toolbars.
1676 
1677     QtToolBarDialog allows the user to customize the toolbars for a
1678     given main window.
1679 
1680     \image qttoolbardialog.png
1681 
1682     The dialog lets the users add, rename and remove custom toolbars.
1683     Note that built-in toolbars are marked with a green color, and
1684     cannot be removed or renamed.
1685 
1686     The users can also add and remove actions from the toolbars. An
1687     action can be added to many toolbars, but a toolbar can only
1688     contain one instance of each action. Actions that contains a
1689     widget are marked with a blue color in the list of actions, and
1690     can only be added to one single toolbar.
1691 
1692     Finally, the users can add separators to the toolbars.
1693 
1694     The original toolbars can be restored by clicking the \gui
1695     {Restore all} button. All custom toolbars will then be removed,
1696     and all built-in toolbars will be restored to their original state.
1697 
1698     The QtToolBarDialog class's functionality is controlled by an
1699     instance of the QtToolBarManager class, and the main window is
1700     specified using the QtToolBarManager::setMainWindow() function.
1701 
1702     All you need to do to use QtToolBarDialog is to specify an
1703     QtToolBarManager instance and call the QDialog::exec() slot:
1704 
1705     \snippet doc/src/snippets/code/tools_shared_qttoolbardialog_qttoolbardialog.cpp 0
1706 
1707     \sa QtToolBarManager
1708 */
1709 
1710 /*!
1711     Creates a toolbar dialog with the given \a parent and the specified
1712     window \a flags.
1713 */
QtToolBarDialog(QWidget * parent,Qt::WindowFlags flags)1714 QtToolBarDialog::QtToolBarDialog(QWidget *parent, Qt::WindowFlags flags)
1715     : QDialog(parent, flags), d_ptr(new QtToolBarDialogPrivate)
1716 {
1717     d_ptr->q_ptr = this;
1718     d_ptr->ui.setupUi(this);
1719     d_ptr->separatorText = tr("< S E P A R A T O R >");
1720 
1721     d_ptr->ui.actionTree->setColumnCount(1);
1722     d_ptr->ui.actionTree->setRootIsDecorated(false);
1723     d_ptr->ui.actionTree->header()->hide();
1724 
1725     d_ptr->ui.upButton->setIcon(QIcon(QLatin1String(":/qt-project.org/qttoolbardialog/images/up.png")));
1726     d_ptr->ui.downButton->setIcon(QIcon(QLatin1String(":/qt-project.org/qttoolbardialog/images/down.png")));
1727     d_ptr->ui.leftButton->setIcon(QIcon(QLatin1String(":/qt-project.org/qttoolbardialog/images/back.png")));
1728     d_ptr->ui.rightButton->setIcon(QIcon(QLatin1String(":/qt-project.org/qttoolbardialog/images/forward.png")));
1729     d_ptr->ui.newButton->setIcon(QIcon(QLatin1String(":/qt-project.org/qttoolbardialog/images/plus.png")));
1730     d_ptr->ui.removeButton->setIcon(QIcon(QLatin1String(":/qt-project.org/qttoolbardialog/images/minus.png")));
1731 
1732     connect(d_ptr->ui.newButton, SIGNAL(clicked()), this, SLOT(newClicked()));
1733     connect(d_ptr->ui.removeButton, SIGNAL(clicked()), this, SLOT(removeClicked()));
1734     connect(d_ptr->ui.renameButton, SIGNAL(clicked()), this, SLOT(renameClicked()));
1735     connect(d_ptr->ui.upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
1736     connect(d_ptr->ui.downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
1737     connect(d_ptr->ui.leftButton, SIGNAL(clicked()), this, SLOT(leftClicked()));
1738     connect(d_ptr->ui.rightButton, SIGNAL(clicked()), this, SLOT(rightClicked()));
1739 
1740     connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(defaultClicked()));
1741     connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked()));
1742     connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked()));
1743     connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked()));
1744 
1745     connect(d_ptr->ui.actionTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
1746                     this, SLOT(currentActionChanged(QTreeWidgetItem*)));
1747     connect(d_ptr->ui.toolBarList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
1748                     this, SLOT(currentToolBarChanged(QListWidgetItem*)));
1749     connect(d_ptr->ui.currentToolBarList,
1750                     SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
1751                     this, SLOT(currentToolBarActionChanged(QListWidgetItem*)));
1752 
1753     connect(d_ptr->ui.actionTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
1754                     this, SLOT(rightClicked()));
1755     connect(d_ptr->ui.currentToolBarList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
1756                     this, SLOT(leftClicked()));
1757     connect(d_ptr->ui.toolBarList, SIGNAL(itemChanged(QListWidgetItem*)),
1758                     this, SLOT(toolBarRenamed(QListWidgetItem*)));
1759 }
1760 
1761 /*!
1762     Destroys the toolbar dialog.
1763 */
~QtToolBarDialog()1764 QtToolBarDialog::~QtToolBarDialog()
1765 {
1766     d_ptr->clearOld();
1767 }
1768 
1769 /*!
1770     Connects the toolbar dialog to the given \a toolBarManager. Then,
1771     when exec() is called, the toolbar dialog will operate using the
1772     given \a toolBarManager.
1773 */
setToolBarManager(QtToolBarManager * toolBarManager)1774 void QtToolBarDialog::setToolBarManager(QtToolBarManager *toolBarManager)
1775 {
1776     if (d_ptr->toolBarManager == toolBarManager->d_ptr->manager)
1777         return;
1778     if (isVisible())
1779         d_ptr->clearOld();
1780     d_ptr->toolBarManager = toolBarManager->d_ptr->manager;
1781     if (isVisible())
1782         d_ptr->fillNew();
1783 }
1784 
1785 /*!
1786     \reimp
1787 */
showEvent(QShowEvent * event)1788 void QtToolBarDialog::showEvent(QShowEvent *event)
1789 {
1790     if (!event->spontaneous())
1791         d_ptr->fillNew();
1792 }
1793 
1794 /*!
1795     \reimp
1796 */
hideEvent(QHideEvent * event)1797 void QtToolBarDialog::hideEvent(QHideEvent *event)
1798 {
1799     if (!event->spontaneous())
1800         d_ptr->clearOld();
1801 }
1802 
1803 QT_END_NAMESPACE
1804 
1805 #include "moc_qttoolbardialog.cpp"
1806 #include "qttoolbardialog.moc"
1807