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 QtWidgets module 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 "qtoolbar.h"
41 
42 #include <qapplication.h>
43 #if QT_CONFIG(combobox)
44 #include <qcombobox.h>
45 #endif
46 #include <qevent.h>
47 #include <qlayout.h>
48 #include <qmainwindow.h>
49 #include <qmenu.h>
50 #if QT_CONFIG(menubar)
51 #include <qmenubar.h>
52 #endif
53 #if QT_CONFIG(rubberband)
54 #include <qrubberband.h>
55 #endif
56 #include <qstylepainter.h>
57 #include <qstyleoption.h>
58 #include <qtoolbutton.h>
59 #include <qwidgetaction.h>
60 #include <qtimer.h>
61 #include <private/qwidgetaction_p.h>
62 #include <private/qmainwindowlayout_p.h>
63 
64 #ifdef Q_OS_MACOS
65 #include <qpa/qplatformnativeinterface.h>
66 #endif
67 
68 #include "qtoolbar_p.h"
69 #include "qtoolbarseparator_p.h"
70 #include "qtoolbarlayout_p.h"
71 
72 #include  "qdebug.h"
73 
74 #define POPUP_TIMER_INTERVAL 500
75 
76 QT_BEGIN_NAMESPACE
77 
78 // qmainwindow.cpp
79 extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
80 
81 /******************************************************************************
82 ** QToolBarPrivate
83 */
84 
init()85 void QToolBarPrivate::init()
86 {
87     Q_Q(QToolBar);
88     q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
89     q->setBackgroundRole(QPalette::Button);
90     q->setAttribute(Qt::WA_Hover);
91     q->setAttribute(Qt::WA_X11NetWmWindowTypeToolBar);
92 
93     QStyle *style = q->style();
94     int e = style->pixelMetric(QStyle::PM_ToolBarIconSize, nullptr, q);
95     iconSize = QSize(e, e);
96 
97     layout = new QToolBarLayout(q);
98     layout->updateMarginAndSpacing();
99 
100     toggleViewAction = new QAction(q);
101     toggleViewAction->setCheckable(true);
102     q->setMovable(q->style()->styleHint(QStyle::SH_ToolBar_Movable, nullptr, q ));
103     QObject::connect(toggleViewAction, SIGNAL(triggered(bool)), q, SLOT(_q_toggleView(bool)));
104 }
105 
_q_toggleView(bool b)106 void QToolBarPrivate::_q_toggleView(bool b)
107 {
108     Q_Q(QToolBar);
109     if (b == q->isHidden()) {
110         if (b)
111             q->show();
112         else
113             q->close();
114     }
115 }
116 
_q_updateIconSize(const QSize & sz)117 void QToolBarPrivate::_q_updateIconSize(const QSize &sz)
118 {
119     Q_Q(QToolBar);
120     if (!explicitIconSize) {
121         // iconSize not explicitly set
122         q->setIconSize(sz);
123         explicitIconSize = false;
124     }
125 }
126 
_q_updateToolButtonStyle(Qt::ToolButtonStyle style)127 void QToolBarPrivate::_q_updateToolButtonStyle(Qt::ToolButtonStyle style)
128 {
129     Q_Q(QToolBar);
130     if (!explicitToolButtonStyle) {
131         q->setToolButtonStyle(style);
132         explicitToolButtonStyle = false;
133     }
134 }
135 
updateWindowFlags(bool floating,bool unplug)136 void QToolBarPrivate::updateWindowFlags(bool floating, bool unplug)
137 {
138     Q_Q(QToolBar);
139     Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
140 
141     flags |= Qt::FramelessWindowHint;
142 
143     if (unplug)
144         flags |= Qt::X11BypassWindowManagerHint;
145 
146     q->setWindowFlags(flags);
147 }
148 
setWindowState(bool floating,bool unplug,const QRect & rect)149 void QToolBarPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
150 {
151     Q_Q(QToolBar);
152     bool visible = !q->isHidden();
153     bool wasFloating = q->isFloating(); // ...is also currently using popup menus
154 
155     q->hide();
156 
157     updateWindowFlags(floating, unplug);
158 
159     if (floating != wasFloating)
160         layout->checkUsePopupMenu();
161 
162     if (!rect.isNull())
163         q->setGeometry(rect);
164 
165     if (visible)
166         q->show();
167 
168     if (floating != wasFloating)
169         emit q->topLevelChanged(floating);
170 }
171 
initDrag(const QPoint & pos)172 void QToolBarPrivate::initDrag(const QPoint &pos)
173 {
174     Q_Q(QToolBar);
175 
176     if (state != nullptr)
177         return;
178 
179     QMainWindow *win = qobject_cast<QMainWindow*>(parent);
180     Q_ASSERT(win != nullptr);
181     QMainWindowLayout *layout = qt_mainwindow_layout(win);
182     Q_ASSERT(layout != nullptr);
183     if (layout->pluggingWidget != nullptr) // the main window is animating a docking operation
184         return;
185 
186     state = new DragState;
187     state->pressPos = pos;
188     state->dragging = false;
189     state->moving = false;
190     state->widgetItem = nullptr;
191 
192     if (q->isRightToLeft())
193         state->pressPos = QPoint(q->width() - state->pressPos.x(), state->pressPos.y());
194 }
195 
startDrag(bool moving)196 void QToolBarPrivate::startDrag(bool moving)
197 {
198     Q_Q(QToolBar);
199 
200     Q_ASSERT(state != nullptr);
201 
202     if ((moving && state->moving) || state->dragging)
203         return;
204 
205     QMainWindow *win = qobject_cast<QMainWindow*>(parent);
206     Q_ASSERT(win != nullptr);
207     QMainWindowLayout *layout = qt_mainwindow_layout(win);
208     Q_ASSERT(layout != nullptr);
209 
210     if (!moving) {
211         state->widgetItem = layout->unplug(q);
212         Q_ASSERT(state->widgetItem != nullptr);
213     }
214     state->dragging = !moving;
215     state->moving = moving;
216 }
217 
endDrag()218 void QToolBarPrivate::endDrag()
219 {
220     Q_Q(QToolBar);
221     Q_ASSERT(state != nullptr);
222 
223     q->releaseMouse();
224 
225     if (state->dragging) {
226         QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
227         Q_ASSERT(layout != nullptr);
228 
229         if (!layout->plug(state->widgetItem)) {
230             if (q->isFloatable()) {
231                 layout->restore();
232                 setWindowState(true); // gets rid of the X11BypassWindowManager window flag
233                                       // and activates the resizer
234                 q->activateWindow();
235             } else {
236                 layout->revert(state->widgetItem);
237             }
238         }
239     }
240 
241     delete state;
242     state = nullptr;
243 }
244 
mousePressEvent(QMouseEvent * event)245 bool QToolBarPrivate::mousePressEvent(QMouseEvent *event)
246 {
247     Q_Q(QToolBar);
248     QStyleOptionToolBar opt;
249     q->initStyleOption(&opt);
250     if (q->style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, q).contains(event->pos()) == false) {
251 #ifdef Q_OS_MACOS
252         // When using the unified toolbar on OS X, the user can click and
253         // drag between toolbar contents to move the window. Make this work by
254         // implementing the standard mouse-dragging code and then call
255         // window->move() in mouseMoveEvent below.
256         if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent)) {
257             if (mainWindow->toolBarArea(q) == Qt::TopToolBarArea
258                     && mainWindow->unifiedTitleAndToolBarOnMac()
259                     && q->childAt(event->pos()) == 0) {
260                 macWindowDragging = true;
261                 macWindowDragPressPosition = event->pos();
262                 return true;
263             }
264         }
265 #endif
266         return false;
267     }
268 
269     if (event->button() != Qt::LeftButton)
270         return true;
271 
272     if (!layout->movable())
273         return true;
274 
275     initDrag(event->pos());
276     return true;
277 }
278 
mouseReleaseEvent(QMouseEvent *)279 bool QToolBarPrivate::mouseReleaseEvent(QMouseEvent*)
280 {
281     if (state != nullptr) {
282         endDrag();
283         return true;
284     } else {
285 #ifdef Q_OS_MACOS
286         if (!macWindowDragging)
287             return false;
288         macWindowDragging = false;
289         macWindowDragPressPosition = QPoint();
290         return true;
291 #endif
292         return false;
293     }
294 }
295 
mouseMoveEvent(QMouseEvent * event)296 bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event)
297 {
298     Q_Q(QToolBar);
299 
300     if (!state) {
301 #ifdef Q_OS_MACOS
302         if (!macWindowDragging)
303             return false;
304         QWidget *w = q->window();
305         const QPoint delta = event->pos() - macWindowDragPressPosition;
306         w->move(w->pos() + delta);
307         return true;
308 #endif
309         return false;
310     }
311 
312     QMainWindow *win = qobject_cast<QMainWindow*>(parent);
313     if (win == nullptr)
314         return true;
315 
316     QMainWindowLayout *layout = qt_mainwindow_layout(win);
317     Q_ASSERT(layout != nullptr);
318 
319     if (layout->pluggingWidget == nullptr
320         && (event->pos() - state->pressPos).manhattanLength() > QApplication::startDragDistance()) {
321             const bool wasDragging = state->dragging;
322             const bool moving = !q->isWindow() && (orientation == Qt::Vertical ?
323                 event->x() >= 0 && event->x() < q->width() :
324                 event->y() >= 0 && event->y() < q->height());
325 
326             startDrag(moving);
327             if (!moving && !wasDragging)
328                 q->grabMouse();
329     }
330 
331     if (state->dragging) {
332         QPoint pos = event->globalPos();
333         // if we are right-to-left, we move so as to keep the right edge the same distance
334         // from the mouse
335         if (q->isLeftToRight())
336             pos -= state->pressPos;
337         else
338             pos += QPoint(state->pressPos.x() - q->width(), -state->pressPos.y());
339 
340         q->move(pos);
341         layout->hover(state->widgetItem, event->globalPos());
342     } else if (state->moving) {
343 
344         const QPoint rtl(q->width() - state->pressPos.x(), state->pressPos.y()); //for RTL
345         const QPoint globalPressPos = q->mapToGlobal(q->isRightToLeft() ? rtl : state->pressPos);
346         int pos = 0;
347 
348         QPoint delta = event->globalPos() - globalPressPos;
349         if (orientation == Qt::Vertical) {
350             pos = q->y() + delta.y();
351         } else {
352             if (q->isRightToLeft()) {
353                 pos = win->width() - q->width() - q->x()  - delta.x();
354             } else {
355                 pos = q->x() + delta.x();
356             }
357         }
358 
359         layout->moveToolBar(q, pos);
360     }
361     return true;
362 }
363 
unplug(const QRect & _r)364 void QToolBarPrivate::unplug(const QRect &_r)
365 {
366     Q_Q(QToolBar);
367     QRect r = _r;
368     r.moveTopLeft(q->mapToGlobal(QPoint(0, 0)));
369     setWindowState(true, true, r);
370     layout->setExpanded(false);
371 }
372 
plug(const QRect & r)373 void QToolBarPrivate::plug(const QRect &r)
374 {
375     setWindowState(false, false, r);
376 }
377 
378 /******************************************************************************
379 ** QToolBar
380 */
381 
382 /*!
383     \class QToolBar
384 
385     \brief The QToolBar class provides a movable panel that contains a
386     set of controls.
387 
388     \ingroup mainwindow-classes
389     \inmodule QtWidgets
390 
391     Toolbar buttons are added by adding \e actions, using addAction()
392     or insertAction(). Groups of buttons can be separated using
393     addSeparator() or insertSeparator(). If a toolbar button is not
394     appropriate, a widget can be inserted instead using addWidget() or
395     insertWidget(). Examples of suitable widgets are QSpinBox,
396     QDoubleSpinBox, and QComboBox. When a toolbar button is pressed, it
397     emits the actionTriggered() signal.
398 
399     A toolbar can be fixed in place in a particular area (e.g., at the
400     top of the window), or it can be movable between toolbar areas;
401     see setMovable(), isMovable(), allowedAreas() and isAreaAllowed().
402 
403     When a toolbar is resized in such a way that it is too small to
404     show all the items it contains, an extension button will appear as
405     the last item in the toolbar. Pressing the extension button will
406     pop up a menu containing the items that do not currently fit in
407     the toolbar.
408 
409     When a QToolBar is not a child of a QMainWindow, it loses the ability
410     to populate the extension pop up with widgets added to the toolbar using
411     addWidget(). Please use widget actions created by inheriting QWidgetAction
412     and implementing QWidgetAction::createWidget() instead.
413 
414     \sa QToolButton, QMenu, QAction, {Application Example}
415 */
416 
417 /*!
418     \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
419 
420     Returns \c true if this toolbar is dockable in the given \a area;
421     otherwise returns \c false.
422 */
423 
424 /*!
425     \fn void QToolBar::actionTriggered(QAction *action)
426 
427     This signal is emitted when an action in this toolbar is triggered.
428     This happens when the action's tool button is pressed, or when the
429     action is triggered in some other way outside the toolbar. The parameter
430     holds the triggered \a action.
431 */
432 
433 /*!
434     \fn void QToolBar::allowedAreasChanged(Qt::ToolBarAreas allowedAreas)
435 
436     This signal is emitted when the collection of allowed areas for the
437     toolbar is changed. The new areas in which the toolbar can be positioned
438     are specified by \a allowedAreas.
439 
440     \sa allowedAreas
441 */
442 
443 /*!
444     \fn void QToolBar::iconSizeChanged(const QSize &iconSize)
445 
446     This signal is emitted when the icon size is changed. The \a
447     iconSize parameter holds the toolbar's new icon size.
448 
449     \sa iconSize, QMainWindow::iconSize
450 */
451 
452 /*!
453     \fn void QToolBar::movableChanged(bool movable)
454 
455     This signal is emitted when the toolbar becomes movable or fixed.
456     If the toolbar can be moved, \a movable is true; otherwise it is
457     false.
458 
459     \sa movable
460 */
461 
462 /*!
463     \fn void QToolBar::orientationChanged(Qt::Orientation orientation)
464 
465     This signal is emitted when the orientation of the toolbar changes.
466     The \a orientation parameter holds the toolbar's new orientation.
467 
468     \sa orientation
469 */
470 
471 /*!
472     \fn void QToolBar::toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle)
473 
474     This signal is emitted when the tool button style is changed. The
475     \a toolButtonStyle parameter holds the toolbar's new tool button
476     style.
477 
478     \sa toolButtonStyle, QMainWindow::toolButtonStyle
479 */
480 
481 /*!
482     \since 4.6
483 
484     \fn void QToolBar::topLevelChanged(bool topLevel)
485 
486     This signal is emitted when the \l floating property changes.
487     The \a topLevel parameter is true if the toolbar is now floating;
488     otherwise it is false.
489 
490     \sa isWindow()
491 */
492 
493 
494 /*!
495     \fn void QToolBar::visibilityChanged(bool visible)
496     \since 4.7
497 
498     This signal is emitted when the toolbar becomes \a visible (or
499     invisible). This happens when the widget is hidden or shown.
500 */
501 
502 /*!
503     Constructs a QToolBar with the given \a parent.
504 */
QToolBar(QWidget * parent)505 QToolBar::QToolBar(QWidget *parent)
506     : QWidget(*new QToolBarPrivate, parent, { })
507 {
508     Q_D(QToolBar);
509     d->init();
510 }
511 
512 /*!
513     Constructs a QToolBar with the given \a parent.
514 
515     The given window \a title identifies the toolbar and is shown in
516     the context menu provided by QMainWindow.
517 
518     \sa setWindowTitle()
519 */
QToolBar(const QString & title,QWidget * parent)520 QToolBar::QToolBar(const QString &title, QWidget *parent)
521     : QToolBar(parent)
522 {
523     setWindowTitle(title);
524 }
525 
526 
527 /*!
528     Destroys the toolbar.
529 */
~QToolBar()530 QToolBar::~QToolBar()
531 {
532 }
533 
534 /*! \property QToolBar::movable
535     \brief whether the user can move the toolbar within the toolbar area,
536     or between toolbar areas.
537 
538     By default, this property is \c true.
539 
540     This property only makes sense if the toolbar is in a
541     QMainWindow.
542 
543     \sa allowedAreas
544 */
545 
setMovable(bool movable)546 void QToolBar::setMovable(bool movable)
547 {
548     Q_D(QToolBar);
549     if (!movable == !d->movable)
550         return;
551     d->movable = movable;
552     d->layout->invalidate();
553     emit movableChanged(d->movable);
554 }
555 
isMovable() const556 bool QToolBar::isMovable() const
557 {
558     Q_D(const QToolBar);
559     return d->movable;
560 }
561 
562 /*!
563     \property QToolBar::floatable
564     \brief whether the toolbar can be dragged and dropped as an independent window.
565 
566     The default is true.
567 */
isFloatable() const568 bool QToolBar::isFloatable() const
569 {
570     Q_D(const QToolBar);
571     return d->floatable;
572 }
573 
setFloatable(bool floatable)574 void QToolBar::setFloatable(bool floatable)
575 {
576     Q_D(QToolBar);
577     d->floatable = floatable;
578 }
579 
580 /*!
581     \property QToolBar::floating
582     \brief whether the toolbar is an independent window.
583 
584     By default, this property is \c true.
585 
586     \sa QWidget::isWindow()
587 */
isFloating() const588 bool QToolBar::isFloating() const
589 {
590     return isWindow();
591 }
592 
593 /*!
594     \property QToolBar::allowedAreas
595     \brief areas where the toolbar may be placed
596 
597     The default is Qt::AllToolBarAreas.
598 
599     This property only makes sense if the toolbar is in a
600     QMainWindow.
601 
602     \sa movable
603 */
604 
setAllowedAreas(Qt::ToolBarAreas areas)605 void QToolBar::setAllowedAreas(Qt::ToolBarAreas areas)
606 {
607     Q_D(QToolBar);
608     areas &= Qt::ToolBarArea_Mask;
609     if (areas == d->allowedAreas)
610         return;
611     d->allowedAreas = areas;
612     emit allowedAreasChanged(d->allowedAreas);
613 }
614 
allowedAreas() const615 Qt::ToolBarAreas QToolBar::allowedAreas() const
616 {
617     Q_D(const QToolBar);
618     return d->allowedAreas;
619 }
620 
621 /*! \property QToolBar::orientation
622     \brief orientation of the toolbar
623 
624     The default is Qt::Horizontal.
625 
626     This function should not be used when the toolbar is managed
627     by QMainWindow. You can use QMainWindow::addToolBar() or
628     QMainWindow::insertToolBar() if you wish to move a toolbar that
629     is already added to a main window to another Qt::ToolBarArea.
630 */
631 
setOrientation(Qt::Orientation orientation)632 void QToolBar::setOrientation(Qt::Orientation orientation)
633 {
634     Q_D(QToolBar);
635     if (orientation == d->orientation)
636         return;
637 
638     d->orientation = orientation;
639 
640     if (orientation == Qt::Vertical)
641         setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
642     else
643         setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
644 
645     d->layout->invalidate();
646     d->layout->activate();
647 
648     emit orientationChanged(d->orientation);
649 }
650 
orientation() const651 Qt::Orientation QToolBar::orientation() const
652 { Q_D(const QToolBar); return d->orientation; }
653 
654 /*!
655     \property QToolBar::iconSize
656     \brief size of icons in the toolbar.
657 
658     The default size is determined by the application's style and is
659     derived from the QStyle::PM_ToolBarIconSize pixel metric. It is
660     the maximum size an icon can have. Icons of smaller size will not
661     be scaled up.
662 */
663 
iconSize() const664 QSize QToolBar::iconSize() const
665 { Q_D(const QToolBar); return d->iconSize; }
666 
setIconSize(const QSize & iconSize)667 void QToolBar::setIconSize(const QSize &iconSize)
668 {
669     Q_D(QToolBar);
670     QSize sz = iconSize;
671     if (!sz.isValid()) {
672         QMainWindow *mw = qobject_cast<QMainWindow *>(parentWidget());
673         if (mw && mw->layout()) {
674             QLayout *layout = mw->layout();
675             int i = 0;
676             QLayoutItem *item = nullptr;
677             do {
678                 item = layout->itemAt(i++);
679                 if (item && (item->widget() == this))
680                     sz = mw->iconSize();
681             } while (!sz.isValid() && item != nullptr);
682         }
683     }
684     if (!sz.isValid()) {
685         const int metric = style()->pixelMetric(QStyle::PM_ToolBarIconSize, nullptr, this);
686         sz = QSize(metric, metric);
687     }
688     if (d->iconSize != sz) {
689         d->iconSize = sz;
690         setMinimumSize(0, 0);
691         emit iconSizeChanged(d->iconSize);
692     }
693     d->explicitIconSize = iconSize.isValid();
694 
695     d->layout->invalidate();
696 }
697 
698 /*!
699     \property QToolBar::toolButtonStyle
700     \brief the style of toolbar buttons
701 
702     This property defines the style of all tool buttons that are added
703     as \l{QAction}s. Note that if you add a QToolButton with the
704     addWidget() method, it will not get this button style.
705 
706     To have the style of toolbuttons follow the system settings, set this property to Qt::ToolButtonFollowStyle.
707     On Unix, the user settings from the desktop environment will be used.
708     On other platforms, Qt::ToolButtonFollowStyle means icon only.
709 
710     The default is Qt::ToolButtonIconOnly.
711 */
712 
toolButtonStyle() const713 Qt::ToolButtonStyle QToolBar::toolButtonStyle() const
714 { Q_D(const QToolBar); return d->toolButtonStyle; }
715 
setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)716 void QToolBar::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)
717 {
718     Q_D(QToolBar);
719     d->explicitToolButtonStyle = true;
720     if (d->toolButtonStyle == toolButtonStyle)
721         return;
722     d->toolButtonStyle = toolButtonStyle;
723     setMinimumSize(0, 0);
724     emit toolButtonStyleChanged(d->toolButtonStyle);
725 }
726 
727 /*!
728     Removes all actions from the toolbar.
729 
730     \sa removeAction()
731 */
clear()732 void QToolBar::clear()
733 {
734     QList<QAction *> actions = this->actions();
735     for(int i = 0; i < actions.size(); i++)
736         removeAction(actions.at(i));
737 }
738 
739 /*!
740     Creates a new action with the given \a text. This action is added to
741     the end of the toolbar.
742 */
addAction(const QString & text)743 QAction *QToolBar::addAction(const QString &text)
744 {
745     QAction *action = new QAction(text, this);
746     addAction(action);
747     return action;
748 }
749 
750 /*!
751     \overload
752 
753     Creates a new action with the given \a icon and \a text. This
754     action is added to the end of the toolbar.
755 */
addAction(const QIcon & icon,const QString & text)756 QAction *QToolBar::addAction(const QIcon &icon, const QString &text)
757 {
758     QAction *action = new QAction(icon, text, this);
759     addAction(action);
760     return action;
761 }
762 
763 /*!
764     \overload
765 
766     Creates a new action with the given \a text. This action is added to
767     the end of the toolbar. The action's \l{QAction::triggered()}{triggered()}
768     signal is connected to \a member in \a receiver.
769 */
addAction(const QString & text,const QObject * receiver,const char * member)770 QAction *QToolBar::addAction(const QString &text,
771                              const QObject *receiver, const char* member)
772 {
773     QAction *action = new QAction(text, this);
774     QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
775     addAction(action);
776     return action;
777 }
778 
779 /*!
780     \overload
781 
782     Creates a new action with the given \a icon and \a text. This
783     action is added to the end of the toolbar. The action's
784     \l{QAction::triggered()}{triggered()} signal is connected to \a
785     member in \a receiver.
786 */
addAction(const QIcon & icon,const QString & text,const QObject * receiver,const char * member)787 QAction *QToolBar::addAction(const QIcon &icon, const QString &text,
788                              const QObject *receiver, const char* member)
789 {
790     QAction *action = new QAction(icon, text, this);
791     QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
792     addAction(action);
793     return action;
794 }
795 
796 /*!\fn template<typename Functor> QAction *QToolBar::addAction(const QString &text, Functor functor)
797 
798     \since 5.6
799 
800     \overload
801 
802     Creates a new action with the given \a text. This action is added to
803     the end of the toolbar. The action's
804     \l{QAction::triggered()}{triggered()} signal is connected to the
805     \a functor.
806 */
807 
808 /*!\fn template<typename Functor> QAction *QToolBar::addAction(const QString &text, const QObject *context, Functor functor)
809 
810     \since 5.6
811 
812     \overload
813 
814     Creates a new action with the given \a text. This action is added
815     to the end of the toolbar. The action's
816     \l{QAction::triggered()}{triggered()} signal is connected to the
817     \a functor. The \a functor can be a pointer to a member function
818     in the \a context object.
819 
820     If the \a context object is destroyed, the \a functor will not be called.
821 */
822 
823 /*!\fn template<typename Functor> QAction *QToolBar::addAction(const QIcon &icon, const QString &text, Functor functor)
824 
825     \since 5.6
826 
827     \overload
828 
829     Creates a new action with the given \a icon and \a text. This
830     action is added to the end of the toolbar. The action's
831     \l{QAction::triggered()}{triggered()} signal is connected to the
832     \a functor.
833 */
834 
835 /*!\fn template<typename Functor> QAction *QToolBar::addAction(const QIcon &icon, const QString &text, const QObject *context, Functor functor)
836 
837     \since 5.6
838 
839     \overload
840 
841     Creates a new action with the given \a icon and \a text. This
842     action is added to the end of the toolbar. The action's
843     \l{QAction::triggered()}{triggered()} signal is connected to the
844     \a functor. The \a functor can be a pointer to a member function
845     of the \a context object.
846 
847     If the \a context object is destroyed, the \a functor will not be called.
848 */
849 
850 /*!
851      Adds a separator to the end of the toolbar.
852 
853      \sa insertSeparator()
854 */
addSeparator()855 QAction *QToolBar::addSeparator()
856 {
857     QAction *action = new QAction(this);
858     action->setSeparator(true);
859     addAction(action);
860     return action;
861 }
862 
863 /*!
864     Inserts a separator into the toolbar in front of the toolbar
865     item associated with the \a before action.
866 
867     \sa addSeparator()
868 */
insertSeparator(QAction * before)869 QAction *QToolBar::insertSeparator(QAction *before)
870 {
871     QAction *action = new QAction(this);
872     action->setSeparator(true);
873     insertAction(before, action);
874     return action;
875 }
876 
877 /*!
878     Adds the given \a widget to the toolbar as the toolbar's last
879     item.
880 
881     The toolbar takes ownership of \a widget.
882 
883     If you add a QToolButton with this method, the toolbar's
884     Qt::ToolButtonStyle will not be respected.
885 
886     \note You should use QAction::setVisible() to change the
887     visibility of the widget. Using QWidget::setVisible(),
888     QWidget::show() and QWidget::hide() does not work.
889 
890     \sa insertWidget()
891 */
addWidget(QWidget * widget)892 QAction *QToolBar::addWidget(QWidget *widget)
893 {
894     QWidgetAction *action = new QWidgetAction(this);
895     action->setDefaultWidget(widget);
896     action->d_func()->autoCreated = true;
897     addAction(action);
898     return action;
899 }
900 
901 /*!
902     Inserts the given \a widget in front of the toolbar item
903     associated with the \a before action.
904 
905     Note: You should use QAction::setVisible() to change the
906     visibility of the widget. Using QWidget::setVisible(),
907     QWidget::show() and QWidget::hide() does not work.
908 
909     \sa addWidget()
910 */
insertWidget(QAction * before,QWidget * widget)911 QAction *QToolBar::insertWidget(QAction *before, QWidget *widget)
912 {
913     QWidgetAction *action = new QWidgetAction(this);
914     action->setDefaultWidget(widget);
915     action->d_func()->autoCreated = true;
916     insertAction(before, action);
917     return action;
918 }
919 
920 /*!
921     \internal
922 
923     Returns the geometry of the toolbar item associated with the given
924     \a action, or an invalid QRect if no matching item is found.
925 */
actionGeometry(QAction * action) const926 QRect QToolBar::actionGeometry(QAction *action) const
927 {
928     Q_D(const QToolBar);
929 
930     int index = d->layout->indexOf(action);
931     if (index == -1)
932         return QRect();
933     return d->layout->itemAt(index)->widget()->geometry();
934 }
935 
936 /*!
937     Returns the action at point \a p. This function returns zero if no
938     action was found.
939 
940     \sa QWidget::childAt()
941 */
actionAt(const QPoint & p) const942 QAction *QToolBar::actionAt(const QPoint &p) const
943 {
944     Q_D(const QToolBar);
945     QWidget *widget = childAt(p);
946     int index = d->layout->indexOf(widget);
947     if (index == -1)
948         return nullptr;
949     QLayoutItem *item = d->layout->itemAt(index);
950     return static_cast<QToolBarItem*>(item)->action;
951 }
952 
953 /*! \fn QAction *QToolBar::actionAt(int x, int y) const
954     \overload
955 
956     Returns the action at the point \a x, \a y. This function returns
957     zero if no action was found.
958 */
959 
960 /*! \reimp */
actionEvent(QActionEvent * event)961 void QToolBar::actionEvent(QActionEvent *event)
962 {
963     Q_D(QToolBar);
964     QAction *action = event->action();
965     QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(action);
966 
967     switch (event->type()) {
968         case QEvent::ActionAdded: {
969             Q_ASSERT_X(widgetAction == nullptr || d->layout->indexOf(widgetAction) == -1,
970                         "QToolBar", "widgets cannot be inserted multiple times");
971 
972             // reparent the action to this toolbar if it has been created
973             // using the addAction(text) etc. convenience functions, to
974             // preserve Qt 4.1.x behavior. The widget is already
975             // reparented to us due to the createWidget call inside
976             // createItem()
977             if (widgetAction != nullptr && widgetAction->d_func()->autoCreated)
978                 widgetAction->setParent(this);
979 
980             int index = d->layout->count();
981             if (event->before()) {
982                 index = d->layout->indexOf(event->before());
983                 Q_ASSERT_X(index != -1, "QToolBar::insertAction", "internal error");
984             }
985             d->layout->insertAction(index, action);
986             break;
987         }
988 
989         case QEvent::ActionChanged:
990             d->layout->invalidate();
991             break;
992 
993         case QEvent::ActionRemoved: {
994             int index = d->layout->indexOf(action);
995             if (index != -1) {
996                 delete d->layout->takeAt(index);
997             }
998             break;
999         }
1000 
1001         default:
1002             Q_ASSERT_X(false, "QToolBar::actionEvent", "internal error");
1003     }
1004 }
1005 
1006 /*! \reimp */
changeEvent(QEvent * event)1007 void QToolBar::changeEvent(QEvent *event)
1008 {
1009     Q_D(QToolBar);
1010     switch (event->type()) {
1011     case QEvent::WindowTitleChange:
1012         d->toggleViewAction->setText(windowTitle());
1013         break;
1014     case QEvent::StyleChange:
1015         d->layout->invalidate();
1016         if (!d->explicitIconSize)
1017             setIconSize(QSize());
1018         d->layout->updateMarginAndSpacing();
1019         break;
1020     case QEvent::LayoutDirectionChange:
1021         d->layout->invalidate();
1022         break;
1023     default:
1024         break;
1025     }
1026     QWidget::changeEvent(event);
1027 }
1028 
1029 /*! \reimp */
paintEvent(QPaintEvent *)1030 void QToolBar::paintEvent(QPaintEvent *)
1031 {
1032     Q_D(QToolBar);
1033 
1034     QPainter p(this);
1035     QStyle *style = this->style();
1036     QStyleOptionToolBar opt;
1037     initStyleOption(&opt);
1038 
1039     if (d->layout->expanded || d->layout->animating || isWindow()) {
1040         //if the toolbar is expended, we need to fill the background with the window color
1041         //because some styles may expects that.
1042         p.fillRect(opt.rect, palette().window());
1043         style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
1044         style->drawPrimitive(QStyle::PE_FrameMenu, &opt, &p, this);
1045     } else {
1046         style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
1047     }
1048 
1049     opt.rect = style->subElementRect(QStyle::SE_ToolBarHandle, &opt, this);
1050     if (opt.rect.isValid())
1051         style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this);
1052 }
1053 
1054 /*
1055     Checks if an expanded toolbar has to wait for this popup to close before
1056     the toolbar collapses. This is true if
1057     1) the popup has the toolbar in its parent chain,
1058     2) the popup is a menu whose menuAction is somewhere in the toolbar.
1059 */
waitForPopup(QToolBar * tb,QWidget * popup)1060 static bool waitForPopup(QToolBar *tb, QWidget *popup)
1061 {
1062     if (popup == nullptr || popup->isHidden())
1063         return false;
1064 
1065     QWidget *w = popup;
1066     while (w != nullptr) {
1067         if (w == tb)
1068             return true;
1069         w = w->parentWidget();
1070     }
1071 
1072     QMenu *menu = qobject_cast<QMenu*>(popup);
1073     if (menu == nullptr)
1074         return false;
1075 
1076     QAction *action = menu->menuAction();
1077     QList<QWidget*> widgets = action->associatedWidgets();
1078     for (int i = 0; i < widgets.count(); ++i) {
1079         if (waitForPopup(tb, widgets.at(i)))
1080             return true;
1081     }
1082 
1083     return false;
1084 }
1085 
1086 #ifdef Q_OS_MACOS
enableMacToolBar(QToolBar * toolbar,bool enable)1087 static void enableMacToolBar(QToolBar *toolbar, bool enable)
1088 {
1089     QPlatformNativeInterface *nativeInterface = QApplication::platformNativeInterface();
1090     if (!nativeInterface)
1091         return;
1092     QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
1093         nativeInterface->nativeResourceFunctionForIntegration("setContentBorderAreaEnabled");
1094     if (!function)
1095         return; // Not Cocoa platform plugin.
1096 
1097     typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, void *identifier, bool enabled);
1098     (reinterpret_cast<SetContentBorderAreaEnabledFunction>(function))(toolbar->window()->windowHandle(), toolbar, enable);
1099 }
1100 #endif
1101 
1102 
1103 /*! \reimp */
event(QEvent * event)1104 bool QToolBar::event(QEvent *event)
1105 {
1106     Q_D(QToolBar);
1107 
1108     switch (event->type()) {
1109     case QEvent::Timer:
1110         if (d->waitForPopupTimer.timerId() == static_cast<QTimerEvent*>(event)->timerId()) {
1111             QWidget *w = QApplication::activePopupWidget();
1112             if (!waitForPopup(this, w)) {
1113                 d->waitForPopupTimer.stop();
1114                 if (!this->underMouse())
1115                     d->layout->setExpanded(false);
1116             }
1117         }
1118         break;
1119     case QEvent::Hide:
1120         if (!isHidden())
1121             break;
1122         Q_FALLTHROUGH();
1123     case QEvent::Show:
1124         d->toggleViewAction->setChecked(event->type() == QEvent::Show);
1125 #ifdef Q_OS_MACOS
1126         enableMacToolBar(this, event->type() == QEvent::Show);
1127 #endif
1128         emit visibilityChanged(event->type() == QEvent::Show);
1129         break;
1130     case QEvent::ParentChange:
1131         d->layout->checkUsePopupMenu();
1132         break;
1133 
1134     case QEvent::MouseButtonPress: {
1135         if (d->mousePressEvent(static_cast<QMouseEvent*>(event)))
1136             return true;
1137         break;
1138     }
1139     case QEvent::MouseButtonRelease:
1140         if (d->mouseReleaseEvent(static_cast<QMouseEvent*>(event)))
1141             return true;
1142         break;
1143     case QEvent::HoverEnter:
1144     case QEvent::HoverLeave:
1145         // there's nothing special to do here and we don't want to update the whole widget
1146         return true;
1147     case QEvent::HoverMove: {
1148 #ifndef QT_NO_CURSOR
1149         QHoverEvent *e = static_cast<QHoverEvent*>(event);
1150         QStyleOptionToolBar opt;
1151         initStyleOption(&opt);
1152         if (style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, this).contains(e->pos()))
1153             setCursor(Qt::SizeAllCursor);
1154         else
1155             unsetCursor();
1156 #endif
1157         break;
1158     }
1159     case QEvent::MouseMove:
1160         if (d->mouseMoveEvent(static_cast<QMouseEvent*>(event)))
1161             return true;
1162         break;
1163     case QEvent::Leave:
1164         if (d->state != nullptr && d->state->dragging) {
1165 #ifdef Q_OS_WIN
1166             // This is a workaround for loosing the mouse on Vista.
1167             QPoint pos = QCursor::pos();
1168             QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton,
1169                              QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
1170             d->mouseMoveEvent(&fake);
1171 #endif
1172         } else {
1173             if (!d->layout->expanded)
1174                 break;
1175 
1176             QWidget *w = QApplication::activePopupWidget();
1177             if (waitForPopup(this, w)) {
1178                 d->waitForPopupTimer.start(POPUP_TIMER_INTERVAL, this);
1179                 break;
1180             }
1181 
1182             d->waitForPopupTimer.stop();
1183             d->layout->setExpanded(false);
1184             break;
1185         }
1186     default:
1187         break;
1188     }
1189     return QWidget::event(event);
1190 }
1191 
1192 /*!
1193     Returns a checkable action that can be used to show or hide this
1194     toolbar.
1195 
1196     The action's text is set to the toolbar's window title.
1197 
1198     \sa QAction::text, QWidget::windowTitle
1199 */
toggleViewAction() const1200 QAction *QToolBar::toggleViewAction() const
1201 { Q_D(const QToolBar); return d->toggleViewAction; }
1202 
1203 /*!
1204     \since 4.2
1205 
1206     Returns the widget associated with the specified \a action.
1207 
1208     \sa addWidget()
1209 */
widgetForAction(QAction * action) const1210 QWidget *QToolBar::widgetForAction(QAction *action) const
1211 {
1212     Q_D(const QToolBar);
1213 
1214     int index = d->layout->indexOf(action);
1215     if (index == -1)
1216         return nullptr;
1217 
1218     return d->layout->itemAt(index)->widget();
1219 }
1220 
1221 extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
1222 
1223 /*!
1224     \internal
1225 */
initStyleOption(QStyleOptionToolBar * option) const1226 void QToolBar::initStyleOption(QStyleOptionToolBar *option) const
1227 {
1228     Q_D(const QToolBar);
1229 
1230     if (!option)
1231         return;
1232 
1233     option->initFrom(this);
1234     if (orientation() == Qt::Horizontal)
1235         option->state |= QStyle::State_Horizontal;
1236     option->lineWidth = style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, nullptr, this);
1237     option->features = d->layout->movable()
1238                         ? QStyleOptionToolBar::Movable
1239                         : QStyleOptionToolBar::None;
1240     // if the tool bar is not in a QMainWindow, this will make the painting right
1241     option->toolBarArea = Qt::NoToolBarArea;
1242 
1243     // Add more styleoptions if the toolbar has been added to a mainwindow.
1244     QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
1245 
1246     if (!mainWindow)
1247         return;
1248 
1249     QMainWindowLayout *layout = qt_mainwindow_layout(mainWindow);
1250     Q_ASSERT_X(layout != nullptr, "QToolBar::initStyleOption()",
1251                "QMainWindow->layout() != QMainWindowLayout");
1252 
1253     layout->getStyleOptionInfo(option, const_cast<QToolBar *>(this));
1254 }
1255 
1256 QT_END_NAMESPACE
1257 
1258 #include "moc_qtoolbar.cpp"
1259