1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2015 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 * Copyright (C) 2016 Piotr Wójcik <chocimier@tlen.pl>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **************************************************************************/
20
21 #include "ToolBarWidget.h"
22 #include "Action.h"
23 #include "ContentsWidget.h"
24 #include "MainWindow.h"
25 #include "Menu.h"
26 #include "SidebarWidget.h"
27 #include "Style.h"
28 #include "TabBarWidget.h"
29 #include "WidgetFactory.h"
30 #include "Window.h"
31 #include "../core/Application.h"
32 #include "../core/BookmarksManager.h"
33 #include "../core/GesturesManager.h"
34 #include "../core/ThemesManager.h"
35 #include "../modules/widgets/bookmark/BookmarkWidget.h"
36
37 #include <QtCore/QTimer>
38 #include <QtGui/QDrag>
39 #include <QtGui/QMouseEvent>
40 #include <QtGui/QPainter>
41 #include <QtWidgets/QLayout>
42 #include <QtWidgets/QStyleOption>
43
44 namespace Otter
45 {
46
ToolBarWidget(int identifier,Window * window,QWidget * parent)47 ToolBarWidget::ToolBarWidget(int identifier, Window *window, QWidget *parent) : QToolBar(parent),
48 m_mainWindow(MainWindow::findMainWindow(parent)),
49 m_window(window),
50 m_sidebarWidget(nullptr),
51 m_bookmark(nullptr),
52 m_dropBookmark(nullptr),
53 m_toggleButton(nullptr),
54 m_area(Qt::TopToolBarArea),
55 m_reloadTimer(0),
56 m_identifier(identifier),
57 m_dropIndex(-1),
58 m_isCollapsed(false),
59 m_isInitialized(false)
60 {
61 setAcceptDrops(true);
62 setAllowedAreas(Qt::NoToolBarArea);
63 setFloatable(false);
64
65 const ToolBarsManager::ToolBarDefinition definition(getDefinition());
66
67 m_state = ToolBarState(identifier, definition);
68 m_area = definition.location;
69
70 if (definition.isValid() && identifier != ToolBarsManager::MenuBar)
71 {
72 if (identifier == ToolBarsManager::TabBar)
73 {
74 m_isInitialized = true;
75 }
76 else
77 {
78 for (int i = 0; i < definition.entries.count(); ++i)
79 {
80 if (definition.entries.at(i).action == QLatin1String("AddressWidget") || definition.entries.at(i).action == QLatin1String("SearchWidget"))
81 {
82 m_isInitialized = true;
83
84 setDefinition(definition);
85
86 connect(ToolBarsManager::getInstance(), &ToolBarsManager::toolBarModified, this, &ToolBarWidget::handleToolBarModified);
87
88 break;
89 }
90 }
91 }
92
93 setToolBarLocked(ToolBarsManager::areToolBarsLocked());
94
95 connect(ToolBarsManager::getInstance(), &ToolBarsManager::toolBarRemoved, this, &ToolBarWidget::handleToolBarRemoved);
96 connect(ToolBarsManager::getInstance(), &ToolBarsManager::toolBarsLockedChanged, this, &ToolBarWidget::setToolBarLocked);
97 }
98
99 if (m_mainWindow)
100 {
101 if (definition.isGlobal())
102 {
103 connect(m_mainWindow, &MainWindow::currentWindowChanged, this, &ToolBarWidget::notifyWindowChanged);
104 }
105
106 connect(m_mainWindow, &MainWindow::fullScreenStateChanged, this, &ToolBarWidget::handleFullScreenStateChanged);
107 }
108 }
109
timerEvent(QTimerEvent * event)110 void ToolBarWidget::timerEvent(QTimerEvent *event)
111 {
112 if (event->timerId() == m_reloadTimer)
113 {
114 killTimer(m_reloadTimer);
115
116 m_reloadTimer = 0;
117
118 loadBookmarks();
119 }
120 }
121
changeEvent(QEvent * event)122 void ToolBarWidget::changeEvent(QEvent *event)
123 {
124 QToolBar::changeEvent(event);
125
126 switch (event->type())
127 {
128 case QEvent::LanguageChange:
129 if (m_toggleButton)
130 {
131 const QKeySequence shortcut(ActionsManager::getActionShortcut(ActionsManager::ShowToolBarAction, {{QLatin1String("toolBar"), ToolBarsManager::getToolBarName(m_identifier)}}));
132
133 m_toggleButton->setToolTip(tr("Toggle Visibility") + (shortcut.isEmpty() ? QString() : QLatin1String(" (") + shortcut.toString(QKeySequence::NativeText) + QLatin1Char(')')));
134 }
135
136 break;
137 case QEvent::StyleChange:
138 {
139 const int iconSize(getIconSize());
140
141 setIconSize(QSize(iconSize, iconSize));
142
143 emit iconSizeChanged(iconSize);
144 }
145
146 break;
147 default:
148 break;
149 }
150 }
151
paintEvent(QPaintEvent * event)152 void ToolBarWidget::paintEvent(QPaintEvent *event)
153 {
154 if (m_identifier != ToolBarsManager::StatusBar)
155 {
156 QToolBar::paintEvent(event);
157 }
158
159 if (getDefinition().type == ToolBarsManager::BookmarksBarType && m_dropIndex >= 0)
160 {
161 const QWidget *widget(widgetForAction(actions().value(m_dropIndex)));
162 const int spacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing));
163 int position(-1);
164
165 if (widget)
166 {
167 switch (m_area)
168 {
169 case Qt::LeftToolBarArea:
170 case Qt::RightToolBarArea:
171 position = (widget->geometry().top() - spacing);
172
173 break;
174 default:
175 if (isLeftToRight())
176 {
177 position = (widget->geometry().left() - spacing);
178 }
179 else
180 {
181 position = (widget->geometry().right() + spacing);
182 }
183
184 break;
185 }
186 }
187 else if (m_dropIndex > 0)
188 {
189 switch (m_area)
190 {
191 case Qt::LeftToolBarArea:
192 case Qt::RightToolBarArea:
193 position = (childrenRect().bottom() + spacing);
194
195 break;
196 default:
197 position = (isLeftToRight() ? (childrenRect().right() + spacing) : (childrenRect().left() - spacing));
198
199 break;
200 }
201 }
202
203 if (position >= 0)
204 {
205 QPainter painter(this);
206
207 switch (m_area)
208 {
209 case Qt::LeftToolBarArea:
210 case Qt::RightToolBarArea:
211 Application::getStyle()->drawDropZone(QLine(0, position, width(), position), &painter);
212
213 break;
214 default:
215 Application::getStyle()->drawDropZone(QLine(position, 0, position, height()), &painter);
216
217 break;
218 }
219 }
220 }
221 }
222
showEvent(QShowEvent * event)223 void ToolBarWidget::showEvent(QShowEvent *event)
224 {
225 if (!m_isInitialized)
226 {
227 m_isInitialized = true;
228
229 reload();
230
231 connect(ToolBarsManager::getInstance(), &ToolBarsManager::toolBarModified, this, &ToolBarWidget::handleToolBarModified);
232 }
233
234 QToolBar::showEvent(event);
235 }
236
resizeEvent(QResizeEvent * event)237 void ToolBarWidget::resizeEvent(QResizeEvent *event)
238 {
239 QToolBar::resizeEvent(event);
240
241 if (m_toggleButton && m_toggleButton->isVisible())
242 {
243 updateToggleGeometry();
244 }
245 }
246
enterEvent(QEvent * event)247 void ToolBarWidget::enterEvent(QEvent *event)
248 {
249 QToolBar::enterEvent(event);
250
251 if (m_toggleButton && !m_isCollapsed)
252 {
253 updateToggleGeometry();
254 }
255 }
256
leaveEvent(QEvent * event)257 void ToolBarWidget::leaveEvent(QEvent *event)
258 {
259 QToolBar::leaveEvent(event);
260
261 if (m_toggleButton && !m_isCollapsed && getDefinition().type != ToolBarsManager::SideBarType)
262 {
263 m_toggleButton->hide();
264 }
265 }
266
mousePressEvent(QMouseEvent * event)267 void ToolBarWidget::mousePressEvent(QMouseEvent *event)
268 {
269 QWidget::mousePressEvent(event);
270
271 if (event->button() == Qt::LeftButton && isDragHandle(event->pos()))
272 {
273 m_dragStartPosition = event->pos();
274 }
275 else
276 {
277 m_dragStartPosition = {};
278 }
279 }
280
mouseMoveEvent(QMouseEvent * event)281 void ToolBarWidget::mouseMoveEvent(QMouseEvent *event)
282 {
283 if (!m_mainWindow || !event->buttons().testFlag(Qt::LeftButton) || m_dragStartPosition.isNull() || !isMovable() || (event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance())
284 {
285 return;
286 }
287
288 m_dragStartPosition = {};
289
290 m_mainWindow->beginToolBarDragging(getDefinition().type == ToolBarsManager::SideBarType);
291
292 QMimeData *mimeData(new QMimeData());
293 mimeData->setProperty("x-toolbar-identifier", m_identifier);
294
295 QDrag *drag(new QDrag(this));
296 drag->setMimeData(mimeData);
297 drag->exec(Qt::MoveAction);
298
299 m_mainWindow->endToolBarDragging();
300 }
301
mouseReleaseEvent(QMouseEvent * event)302 void ToolBarWidget::mouseReleaseEvent(QMouseEvent *event)
303 {
304 QWidget::mouseReleaseEvent(event);
305
306 if (m_mainWindow)
307 {
308 m_mainWindow->endToolBarDragging();
309 }
310 }
311
dragEnterEvent(QDragEnterEvent * event)312 void ToolBarWidget::dragEnterEvent(QDragEnterEvent *event)
313 {
314 if (canDrop(event))
315 {
316 event->accept();
317
318 updateDropIndex(event->pos());
319 }
320 else
321 {
322 event->ignore();
323 }
324 }
325
dragMoveEvent(QDragMoveEvent * event)326 void ToolBarWidget::dragMoveEvent(QDragMoveEvent *event)
327 {
328 if (canDrop(event))
329 {
330 event->accept();
331
332 updateDropIndex(event->pos());
333 }
334 else
335 {
336 event->ignore();
337 }
338 }
339
dragLeaveEvent(QDragLeaveEvent * event)340 void ToolBarWidget::dragLeaveEvent(QDragLeaveEvent *event)
341 {
342 QWidget::dragLeaveEvent(event);
343
344 updateDropIndex({});
345 }
346
dropEvent(QDropEvent * event)347 void ToolBarWidget::dropEvent(QDropEvent *event)
348 {
349 if (canDrop(event))
350 {
351 event->accept();
352
353 const QVector<QUrl> urls(Utils::extractUrls(event->mimeData()));
354
355 for (int i = 0; i < urls.count(); ++i)
356 {
357 const QString title(event->mimeData()->property("x-url-title").toString());
358 QMap<int, QVariant> metaData({{BookmarksModel::UrlRole, urls.at(i)}});
359
360 if (!title.isEmpty())
361 {
362 metaData[BookmarksModel::TitleRole] = title;
363 }
364
365 if (m_dropBookmark)
366 {
367 BookmarksManager::addBookmark(BookmarksModel::UrlBookmark, metaData, m_dropBookmark);
368 }
369 else
370 {
371 BookmarksManager::addBookmark(BookmarksModel::UrlBookmark, metaData, m_bookmark, (m_dropIndex + i));
372 }
373 }
374 }
375 else
376 {
377 event->ignore();
378 }
379
380 updateDropIndex({});
381 }
382
clearEntries()383 void ToolBarWidget::clearEntries()
384 {
385 if (getDefinition().type == ToolBarsManager::SideBarType && m_sidebarWidget)
386 {
387 m_sidebarWidget->reload();
388 }
389 else
390 {
391 clear();
392 }
393 }
394
populateEntries()395 void ToolBarWidget::populateEntries()
396 {
397 const ToolBarsManager::ToolBarDefinition definition(getDefinition());
398 const bool isHorizontal(m_area != Qt::LeftToolBarArea && m_area != Qt::RightToolBarArea);
399
400 m_addressFields.clear();
401 m_searchFields.clear();
402
403 switch (definition.type)
404 {
405 case ToolBarsManager::BookmarksBarType:
406 m_bookmark = BookmarksManager::getBookmark(definition.bookmarksPath);
407
408 loadBookmarks();
409
410 connect(BookmarksManager::getModel(), &BookmarksModel::bookmarkAdded, this, &ToolBarWidget::handleBookmarkModified);
411 connect(BookmarksManager::getModel(), &BookmarksModel::bookmarkModified, this, &ToolBarWidget::handleBookmarkModified);
412 connect(BookmarksManager::getModel(), &BookmarksModel::bookmarkRestored, this, &ToolBarWidget::handleBookmarkModified);
413 connect(BookmarksManager::getModel(), &BookmarksModel::bookmarkMoved, this, &ToolBarWidget::handleBookmarkMoved);
414 connect(BookmarksManager::getModel(), &BookmarksModel::bookmarkTrashed, this, &ToolBarWidget::handleBookmarkMoved);
415 connect(BookmarksManager::getModel(), &BookmarksModel::bookmarkRemoved, this, &ToolBarWidget::handleBookmarkRemoved);
416
417 break;
418 case ToolBarsManager::SideBarType:
419 if (!m_sidebarWidget)
420 {
421 m_sidebarWidget = new SidebarWidget(this);
422
423 addWidget(m_sidebarWidget);
424 }
425
426 if (m_toggleButton)
427 {
428 updateToggleGeometry();
429 }
430
431 break;
432 default:
433 m_addressFields.clear();
434 m_searchFields.clear();
435
436 for (int i = 0; i < definition.entries.count(); ++i)
437 {
438 if (definition.entries.at(i).action == QLatin1String("separator"))
439 {
440 addSeparator();
441 }
442 else if (definition.entries.at(i).action != QLatin1String("TabBarWidget"))
443 {
444 QWidget *widget(WidgetFactory::createToolBarItem(definition.entries.at(i), m_window, this));
445
446 if (widget)
447 {
448 addWidget(widget);
449
450 if (definition.entries.at(i).action == QLatin1String("AddressWidget"))
451 {
452 m_addressFields.append(widget);
453 }
454 else if (definition.entries.at(i).action == QLatin1String("SearchWidget"))
455 {
456 m_searchFields.append(widget);
457 }
458
459 layout()->setAlignment(widget, (isHorizontal ? Qt::AlignVCenter : Qt::AlignHCenter));
460 }
461 }
462 }
463
464 break;
465 }
466 }
467
updateDropIndex(const QPoint & position)468 void ToolBarWidget::updateDropIndex(const QPoint &position)
469 {
470 if (!m_bookmark)
471 {
472 return;
473 }
474
475 int dropIndex(-1);
476
477 m_dropBookmark = nullptr;
478
479 if (!position.isNull())
480 {
481 QAction *action(actionAt(position));
482 const bool isHorizontal(m_area != Qt::LeftToolBarArea && m_area != Qt::RightToolBarArea);
483
484 if (!action)
485 {
486 const QPoint center(contentsRect().center());
487 const int spacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing) * 2);
488
489 if (isHorizontal)
490 {
491 const QPoint adjustedPosition(position.x(), center.y());
492
493 action = (actionAt(adjustedPosition + QPoint(spacing, 0)));
494
495 if (!action)
496 {
497 action = (actionAt(adjustedPosition - QPoint(spacing, 0)));
498 }
499 }
500 else
501 {
502 const QPoint adjustedPosition(center.x(), position.y());
503
504 action = (actionAt(adjustedPosition + QPoint(0, spacing)));
505
506 if (!action)
507 {
508 action = (actionAt(adjustedPosition - QPoint(0, spacing)));
509 }
510 }
511 }
512
513 const QWidget *widget(widgetForAction(action));
514
515 dropIndex = actions().indexOf(action);
516
517 if (dropIndex >= 0 && dropIndex < m_bookmark->rowCount())
518 {
519 BookmarksModel::Bookmark *dropBookmark(BookmarksManager::getModel()->getBookmark(m_bookmark->index().child(dropIndex, 0)));
520
521 if (dropBookmark && dropBookmark->getType() == BookmarksModel::FolderBookmark)
522 {
523 bool canNest(false);
524
525 if (widget)
526 {
527 const int margin((isHorizontal ? widget->geometry().width() : widget->geometry().height()) / 3);
528
529 if (isHorizontal)
530 {
531 canNest = (position.x() >= (widget->geometry().left() + margin) && position.x() <= (widget->geometry().right() - margin));
532 }
533 else
534 {
535 canNest = (position.y() >= (widget->geometry().top() + margin) && position.y() <= (widget->geometry().bottom() - margin));
536 }
537 }
538
539 if (canNest)
540 {
541 m_dropBookmark = dropBookmark;
542
543 dropIndex = -1;
544 }
545 }
546 }
547
548 if (!m_dropBookmark)
549 {
550 if (widget && dropIndex >= 0)
551 {
552 if (isHorizontal)
553 {
554 if (isLeftToRight() && position.x() >= widget->geometry().center().x())
555 {
556 ++dropIndex;
557 }
558 else if (!isLeftToRight() && position.x() < widget->geometry().center().x())
559 {
560 ++dropIndex;
561 }
562 }
563 else if (!isHorizontal && position.y() >= widget->geometry().center().y())
564 {
565 ++dropIndex;
566 }
567 }
568
569 if (dropIndex < 0)
570 {
571 const QPoint center(contentsRect().center());
572
573 if (isHorizontal)
574 {
575 if (isLeftToRight())
576 {
577 dropIndex = ((position.x() < center.x()) ? 0 : actions().count());
578 }
579 else
580 {
581 dropIndex = ((position.x() < center.x()) ? actions().count() : 0);
582 }
583 }
584 else
585 {
586 dropIndex = ((position.y() < center.y()) ? 0 : actions().count());
587 }
588 }
589 }
590 }
591
592 if (dropIndex != m_dropIndex)
593 {
594 m_dropIndex = dropIndex;
595
596 update();
597 }
598 }
599
contextMenuEvent(QContextMenuEvent * event)600 void ToolBarWidget::contextMenuEvent(QContextMenuEvent *event)
601 {
602 if (m_identifier < 0)
603 {
604 event->ignore();
605
606 return;
607 }
608
609 if (event->reason() == QContextMenuEvent::Mouse)
610 {
611 event->accept();
612
613 return;
614 }
615
616 QMenu *menu(createCustomizationMenu(m_identifier));
617 menu->exec(event->globalPos());
618 menu->deleteLater();
619 }
620
reload()621 void ToolBarWidget::reload()
622 {
623 setDefinition(getDefinition());
624 }
625
resetGeometry()626 void ToolBarWidget::resetGeometry()
627 {
628 for (int i = 0; i < actions().count(); ++i)
629 {
630 QWidget *widget(widgetForAction(actions().at(i)));
631
632 if (widget)
633 {
634 widget->setMaximumSize(0, 0);
635 }
636 }
637
638 setMaximumSize(0, 0);
639
640 for (int i = 0; i < actions().count(); ++i)
641 {
642 QWidget *widget(widgetForAction(actions().at(i)));
643
644 if (widget)
645 {
646 widget->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
647 }
648 }
649
650 setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
651 }
652
scheduleBookmarksReload()653 void ToolBarWidget::scheduleBookmarksReload()
654 {
655 if (m_reloadTimer != 0)
656 {
657 killTimer(m_reloadTimer);
658 }
659
660 m_reloadTimer = startTimer(100);
661 }
662
loadBookmarks()663 void ToolBarWidget::loadBookmarks()
664 {
665 clear();
666
667 if (!m_bookmark)
668 {
669 return;
670 }
671
672 for (int i = 0; i < m_bookmark->rowCount(); ++i)
673 {
674 BookmarksModel::Bookmark *bookmark(static_cast<BookmarksModel::Bookmark*>(m_bookmark->child(i)));
675
676 if (bookmark)
677 {
678 if (bookmark->getType() == BookmarksModel::SeparatorBookmark)
679 {
680 addSeparator();
681 }
682 else
683 {
684 addWidget(new BookmarkWidget(bookmark, ToolBarsManager::ToolBarDefinition::Entry(), this));
685 }
686 }
687 }
688 }
689
toggleVisibility()690 void ToolBarWidget::toggleVisibility()
691 {
692 const ToolBarsManager::ToolBarsMode mode((m_mainWindow ? m_mainWindow->windowState().testFlag(Qt::WindowFullScreen) : false) ? ToolBarsManager::FullScreenMode : ToolBarsManager::NormalMode);
693 ToolBarState state(m_state);
694 state.setVisibility(mode, (calculateShouldBeVisible(getDefinition(), m_state, mode) ? ToolBarState::AlwaysHiddenToolBar : ToolBarState::AlwaysVisibleToolBar));
695
696 setState(state);
697 }
698
notifyWindowChanged(quint64 identifier)699 void ToolBarWidget::notifyWindowChanged(quint64 identifier)
700 {
701 m_window = m_mainWindow->getWindowByIdentifier(identifier);
702
703 emit windowChanged(m_window);
704 }
705
handleToolBarModified(int identifier)706 void ToolBarWidget::handleToolBarModified(int identifier)
707 {
708 if (identifier == m_identifier)
709 {
710 reload();
711
712 emit toolBarModified();
713 }
714 }
715
handleToolBarRemoved(int identifier)716 void ToolBarWidget::handleToolBarRemoved(int identifier)
717 {
718 if (identifier == m_identifier)
719 {
720 deleteLater();
721 }
722 }
723
handleBookmarkModified(BookmarksModel::Bookmark * bookmark)724 void ToolBarWidget::handleBookmarkModified(BookmarksModel::Bookmark *bookmark)
725 {
726 if (bookmark == m_bookmark || m_bookmark->isAncestorOf(bookmark))
727 {
728 scheduleBookmarksReload();
729 }
730 }
731
handleBookmarkMoved(BookmarksModel::Bookmark * bookmark,BookmarksModel::Bookmark * previousParent)732 void ToolBarWidget::handleBookmarkMoved(BookmarksModel::Bookmark *bookmark, BookmarksModel::Bookmark *previousParent)
733 {
734 if (bookmark == m_bookmark || previousParent == m_bookmark || m_bookmark->isAncestorOf(bookmark) || m_bookmark->isAncestorOf(previousParent))
735 {
736 scheduleBookmarksReload();
737 }
738 }
739
handleBookmarkRemoved(BookmarksModel::Bookmark * bookmark,BookmarksModel::Bookmark * previousParent)740 void ToolBarWidget::handleBookmarkRemoved(BookmarksModel::Bookmark *bookmark, BookmarksModel::Bookmark *previousParent)
741 {
742 if (bookmark == m_bookmark)
743 {
744 m_bookmark = nullptr;
745
746 loadBookmarks();
747 }
748 else if (previousParent == m_bookmark || m_bookmark->isAncestorOf(previousParent))
749 {
750 loadBookmarks();
751 }
752 }
753
handleFullScreenStateChanged(bool isFullScreen)754 void ToolBarWidget::handleFullScreenStateChanged(bool isFullScreen)
755 {
756 if (getDefinition().hasToggle)
757 {
758 reload();
759 }
760
761 setVisible(shouldBeVisible(isFullScreen ? ToolBarsManager::FullScreenMode : ToolBarsManager::NormalMode));
762 }
763
updateToggleGeometry()764 void ToolBarWidget::updateToggleGeometry()
765 {
766 if (!m_toggleButton)
767 {
768 return;
769 }
770
771 if (m_isCollapsed || underMouse())
772 {
773 m_toggleButton->show();
774 m_toggleButton->raise();
775 }
776
777 const bool isHorizontal(orientation() == Qt::Horizontal);
778
779 m_toggleButton->setMaximumSize((isHorizontal ? QWIDGETSIZE_MAX : 6), (isHorizontal ? 6 : QWIDGETSIZE_MAX));
780 m_toggleButton->resize((isHorizontal ? width() : 6), (isHorizontal ? 6 : height()));
781
782 if (m_isCollapsed)
783 {
784 setMaximumSize((isHorizontal ? QWIDGETSIZE_MAX : 8), (isHorizontal ? 8 : QWIDGETSIZE_MAX));
785 }
786 else
787 {
788 setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
789 }
790
791 switch (m_area)
792 {
793 case Qt::BottomToolBarArea:
794 m_toggleButton->move(0, (height() - 6));
795
796 break;
797 case Qt::LeftToolBarArea:
798 m_toggleButton->move(0, 0);
799
800 break;
801 case Qt::RightToolBarArea:
802 m_toggleButton->move((width() - 6), 0);
803
804 break;
805 default:
806 m_toggleButton->move(0, 0);
807
808 break;
809 }
810 }
811
setAddressFields(QVector<QPointer<QWidget>> addressFields)812 void ToolBarWidget::setAddressFields(QVector<QPointer<QWidget> > addressFields)
813 {
814 m_addressFields = addressFields;
815 }
816
setSearchFields(QVector<QPointer<QWidget>> searchFields)817 void ToolBarWidget::setSearchFields(QVector<QPointer<QWidget> > searchFields)
818 {
819 m_searchFields = searchFields;
820 }
821
setArea(Qt::ToolBarArea area)822 void ToolBarWidget::setArea(Qt::ToolBarArea area)
823 {
824 if (area != m_area)
825 {
826 m_area = area;
827
828 emit areaChanged(m_area);
829 }
830 }
831
setDefinition(const ToolBarsManager::ToolBarDefinition & definition)832 void ToolBarWidget::setDefinition(const ToolBarsManager::ToolBarDefinition &definition)
833 {
834 const ToolBarsManager::ToolBarsMode mode((m_mainWindow ? m_mainWindow->windowState().testFlag(Qt::WindowFullScreen) : false) ? ToolBarsManager::FullScreenMode : ToolBarsManager::NormalMode);
835
836 m_isCollapsed = (definition.hasToggle && !calculateShouldBeVisible(definition, m_state, mode));
837
838 setVisible(shouldBeVisible(mode));
839 setOrientation((m_area != Qt::LeftToolBarArea && m_area != Qt::RightToolBarArea) ? Qt::Horizontal : Qt::Vertical);
840 clearEntries();
841
842 if (definition.hasToggle)
843 {
844 if (!m_toggleButton)
845 {
846 const QKeySequence shortcut(ActionsManager::getActionShortcut(ActionsManager::ShowToolBarAction, {{QLatin1String("toolBar"), ToolBarsManager::getToolBarName(m_identifier)}}));
847
848 m_toggleButton = new QPushButton(this);
849 m_toggleButton->setToolTip(tr("Toggle Visibility") + (shortcut.isEmpty() ? QString() : QLatin1String(" (") + shortcut.toString(QKeySequence::NativeText) + QLatin1Char(')')));
850 m_toggleButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
851
852 connect(m_toggleButton, &QPushButton::clicked, this, &ToolBarWidget::toggleVisibility);
853 }
854
855 updateToggleGeometry();
856
857 if (m_isCollapsed)
858 {
859 return;
860 }
861 }
862 else if (m_toggleButton)
863 {
864 m_toggleButton->deleteLater();
865 m_toggleButton = nullptr;
866 }
867
868 const int iconSize(getIconSize());
869
870 setToolButtonStyle(definition.buttonStyle);
871 setIconSize(QSize(iconSize, iconSize));
872
873 emit buttonStyleChanged(definition.buttonStyle);
874 emit iconSizeChanged(iconSize);
875
876 populateEntries();
877 }
878
setState(const ToolBarState & state)879 void ToolBarWidget::setState(const ToolBarState &state)
880 {
881 m_state = state;
882
883 handleFullScreenStateChanged(m_mainWindow ? m_mainWindow->windowState().testFlag(Qt::WindowFullScreen) : false);
884 }
885
setToolBarLocked(bool locked)886 void ToolBarWidget::setToolBarLocked(bool locked)
887 {
888 setMovable(!locked && m_identifier != ToolBarsManager::MenuBar && m_identifier != ToolBarsManager::ProgressBar);
889 }
890
getMainWindow() const891 MainWindow* ToolBarWidget::getMainWindow() const
892 {
893 return m_mainWindow;
894 }
895
getWindow() const896 Window* ToolBarWidget::getWindow() const
897 {
898 return m_window;
899 }
900
createCustomizationMenu(int identifier,QVector<QAction * > actions,QWidget * parent)901 QMenu* ToolBarWidget::createCustomizationMenu(int identifier, QVector<QAction*> actions, QWidget *parent)
902 {
903 const ToolBarsManager::ToolBarDefinition definition(ToolBarsManager::getToolBarDefinition(identifier));
904 QMenu *menu(new QMenu(parent));
905 menu->setTitle(tr("Customize"));
906
907 QMenu *toolBarMenu(menu->addMenu(definition.getTitle().isEmpty() ? tr("(Untitled)") : definition.getTitle()));
908 toolBarMenu->addAction(tr("Configure…"), menu, [=]()
909 {
910 ToolBarsManager::configureToolBar(identifier);
911 });
912 toolBarMenu->addAction(tr("Reset to Defaults…"), menu, [=]()
913 {
914 ToolBarsManager::resetToolBar(identifier);
915 })->setEnabled(definition.canReset());
916
917 if (!actions.isEmpty())
918 {
919 toolBarMenu->addSeparator();
920 toolBarMenu->addActions(actions.toList());
921
922 for (int i = 0; i < actions.count(); ++i)
923 {
924 actions.at(i)->setParent(toolBarMenu);
925 }
926 }
927
928 toolBarMenu->addSeparator();
929 toolBarMenu->addAction(ThemesManager::createIcon(QLatin1String("list-remove")), tr("Remove…"), menu, [=]()
930 {
931 ToolBarsManager::removeToolBar(identifier);
932 })->setEnabled(!definition.canReset());
933
934 menu->addMenu(new Menu(Menu::ToolBarsMenu, menu));
935
936 return menu;
937 }
938
getTitle() const939 QString ToolBarWidget::getTitle() const
940 {
941 return getDefinition().getTitle();
942 }
943
getDefinition() const944 ToolBarsManager::ToolBarDefinition ToolBarWidget::getDefinition() const
945 {
946 return ToolBarsManager::getToolBarDefinition(m_identifier);
947 }
948
getState() const949 ToolBarState ToolBarWidget::getState() const
950 {
951 return m_state;
952 }
953
getAddressFields() const954 QVector<QPointer<QWidget> > ToolBarWidget::getAddressFields() const
955 {
956 return m_addressFields;
957 }
958
getSearchFields() const959 QVector<QPointer<QWidget> > ToolBarWidget::getSearchFields() const
960 {
961 return m_searchFields;
962 }
963
getArea() const964 Qt::ToolBarArea ToolBarWidget::getArea() const
965 {
966 return m_area;
967 }
968
getButtonStyle() const969 Qt::ToolButtonStyle ToolBarWidget::getButtonStyle() const
970 {
971 return getDefinition().buttonStyle;
972 }
973
getIdentifier() const974 int ToolBarWidget::getIdentifier() const
975 {
976 return m_identifier;
977 }
978
getIconSize() const979 int ToolBarWidget::getIconSize() const
980 {
981 const int iconSize(getDefinition().iconSize);
982
983 if (iconSize > 0)
984 {
985 return iconSize;
986 }
987
988 return style()->pixelMetric(QStyle::PM_ToolBarIconSize);
989 }
990
getMaximumButtonSize() const991 int ToolBarWidget::getMaximumButtonSize() const
992 {
993 return getDefinition().maximumButtonSize;
994 }
995
calculateShouldBeVisible(const ToolBarsManager::ToolBarDefinition & definition,const ToolBarState & state,ToolBarsManager::ToolBarsMode mode)996 bool ToolBarWidget::calculateShouldBeVisible(const ToolBarsManager::ToolBarDefinition &definition, const ToolBarState &state, ToolBarsManager::ToolBarsMode mode)
997 {
998 const ToolBarState::ToolBarVisibility visibility(state.getVisibility(mode));
999
1000 if (visibility != ToolBarState::UnspecifiedVisibilityToolBar)
1001 {
1002 return (visibility == ToolBarState::AlwaysVisibleToolBar);
1003 }
1004
1005 return (definition.getVisibility(mode) == ToolBarsManager::AlwaysVisibleToolBar);
1006 }
1007
canDrop(QDropEvent * event) const1008 bool ToolBarWidget::canDrop(QDropEvent *event) const
1009 {
1010 return (m_bookmark && event->mimeData()->hasUrls() && (event->keyboardModifiers().testFlag(Qt::ShiftModifier) || !ToolBarsManager::areToolBarsLocked()));
1011 }
1012
isCollapsed() const1013 bool ToolBarWidget::isCollapsed() const
1014 {
1015 return m_isCollapsed;
1016 }
1017
isDragHandle(const QPoint & position) const1018 bool ToolBarWidget::isDragHandle(const QPoint &position) const
1019 {
1020 if (!isMovable())
1021 {
1022 return false;
1023 }
1024
1025 QStyleOptionToolBar option;
1026 initStyleOption(&option);
1027
1028 return (style()->subElementRect(QStyle::SE_ToolBarHandle, &option, this).contains(position));
1029 }
1030
shouldBeVisible(ToolBarsManager::ToolBarsMode mode) const1031 bool ToolBarWidget::shouldBeVisible(ToolBarsManager::ToolBarsMode mode) const
1032 {
1033 const ToolBarsManager::ToolBarDefinition definition(getDefinition());
1034
1035 return ((mode == ToolBarsManager::NormalMode && definition.hasToggle) || calculateShouldBeVisible(definition, m_state, mode));
1036 }
1037
event(QEvent * event)1038 bool ToolBarWidget::event(QEvent *event)
1039 {
1040 if (!GesturesManager::isTracking() && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick || event->type() == QEvent::Wheel))
1041 {
1042 QPoint position;
1043
1044 if (event->type() == QEvent::Wheel)
1045 {
1046 position = static_cast<QWheelEvent*>(event)->pos();
1047 }
1048 else
1049 {
1050 position = static_cast<QMouseEvent*>(event)->pos();
1051 }
1052
1053 if (position.isNull() || !isDragHandle(position))
1054 {
1055 QVector<GesturesManager::GesturesContext> contexts({GesturesManager::ToolBarContext, GesturesManager::GenericContext});
1056
1057 if (m_identifier == ToolBarsManager::TabBar)
1058 {
1059 contexts.prepend(GesturesManager::NoTabHandleContext);
1060 }
1061
1062 GesturesManager::startGesture(this, event, contexts);
1063 }
1064 }
1065
1066 if (event->type() == QEvent::MouseButtonPress)
1067 {
1068 return QWidget::event(event);
1069 }
1070
1071 return QToolBar::event(event);
1072 }
1073
TabBarToolBarWidget(int identifier,Window * window,QWidget * parent)1074 TabBarToolBarWidget::TabBarToolBarWidget(int identifier, Window *window, QWidget *parent) : ToolBarWidget(identifier, window, parent),
1075 m_tabBar(nullptr)
1076 {
1077 setContentsMargins(0, 0, 0, 0);
1078
1079 layout()->setMargin(0);
1080
1081 setDefinition(getDefinition());
1082
1083 connect(ThemesManager::getInstance(), &ThemesManager::widgetStyleChanged, this, &TabBarToolBarWidget::resetGeometry);
1084 connect(ToolBarsManager::getInstance(), &ToolBarsManager::toolBarModified, this, &TabBarToolBarWidget::handleToolBarModified);
1085 }
1086
paintEvent(QPaintEvent * event)1087 void TabBarToolBarWidget::paintEvent(QPaintEvent *event)
1088 {
1089 ToolBarWidget::paintEvent(event);
1090
1091 QStyleOptionTab tabOption;
1092
1093 switch (getArea())
1094 {
1095 case Qt::BottomToolBarArea:
1096 tabOption.shape = QTabBar::RoundedSouth;
1097
1098 break;
1099 case Qt::LeftToolBarArea:
1100 tabOption.shape = QTabBar::RoundedWest;
1101
1102 break;
1103 case Qt::RightToolBarArea:
1104 tabOption.shape = QTabBar::RoundedEast;
1105
1106 break;
1107 default:
1108 tabOption.shape = QTabBar::RoundedNorth;
1109
1110 break;
1111 }
1112
1113 const int overlap(style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOption));
1114 QPainter painter(this);
1115 QStyleOptionTabBarBase tabBarBaseOption;
1116 tabBarBaseOption.initFrom(this);
1117 tabBarBaseOption.documentMode = true;
1118 tabBarBaseOption.rect = contentsRect();
1119 tabBarBaseOption.shape = tabOption.shape;
1120 tabBarBaseOption.tabBarRect = contentsRect();
1121
1122 if (m_tabBar)
1123 {
1124 tabBarBaseOption.selectedTabRect = m_tabBar->tabRect(m_tabBar->currentIndex()).translated(m_tabBar->pos());
1125 tabBarBaseOption.tabBarRect = m_tabBar->geometry();
1126 }
1127
1128 if (overlap > 0)
1129 {
1130 const QSize size(contentsRect().size());
1131
1132 switch (getArea())
1133 {
1134 case Qt::BottomToolBarArea:
1135 tabBarBaseOption.rect.setRect(0, 0, size.width(), overlap);
1136
1137 break;
1138 case Qt::LeftToolBarArea:
1139 tabBarBaseOption.rect.setRect((size.width() - overlap), 0, overlap, size.height());
1140
1141 break;
1142 case Qt::RightToolBarArea:
1143 tabBarBaseOption.rect.setRect(0, 0, overlap, size.height());
1144
1145 break;
1146 default:
1147 tabBarBaseOption.rect.setRect(0, (size.height() - overlap), size.width(), overlap);
1148
1149 break;
1150 }
1151 }
1152
1153 style()->drawPrimitive(QStyle::PE_FrameTabBarBase, &tabBarBaseOption, &painter, this);
1154 }
1155
resizeEvent(QResizeEvent * event)1156 void TabBarToolBarWidget::resizeEvent(QResizeEvent *event)
1157 {
1158 ToolBarWidget::resizeEvent(event);
1159
1160 if (m_tabBar)
1161 {
1162 QTimer::singleShot(200, m_tabBar, &TabBarWidget::updateSize);
1163 }
1164 }
1165
contextMenuEvent(QContextMenuEvent * event)1166 void TabBarToolBarWidget::contextMenuEvent(QContextMenuEvent *event)
1167 {
1168 if (event->reason() == QContextMenuEvent::Mouse)
1169 {
1170 event->accept();
1171
1172 return;
1173 }
1174
1175 QAction *cycleAction(new QAction(tr("Switch Tabs Using the Mouse Wheel"), this));
1176 cycleAction->setCheckable(true);
1177 cycleAction->setChecked(!SettingsManager::getOption(SettingsManager::TabBar_RequireModifierToSwitchTabOnScrollOption).toBool());
1178
1179 QAction *thumbnailsAction(new QAction(tr("Show Thumbnails in Tabs"), this));
1180 thumbnailsAction->setCheckable(true);
1181 thumbnailsAction->setChecked(SettingsManager::getOption(SettingsManager::TabBar_EnableThumbnailsOption).toBool());
1182
1183 connect(cycleAction, &QAction::toggled, [&](bool isEnabled)
1184 {
1185 SettingsManager::setOption(SettingsManager::TabBar_RequireModifierToSwitchTabOnScrollOption, !isEnabled);
1186 });
1187 connect(thumbnailsAction, &QAction::toggled, [&](bool areEnabled)
1188 {
1189 SettingsManager::setOption(SettingsManager::TabBar_EnableThumbnailsOption, areEnabled);
1190 });
1191
1192 ActionExecutor::Object executor(getMainWindow(), getMainWindow());
1193 QMenu menu(this);
1194 menu.addAction(new Action(ActionsManager::NewTabAction, {}, executor, &menu));
1195 menu.addAction(new Action(ActionsManager::NewTabPrivateAction, {}, executor, &menu));
1196 menu.addSeparator();
1197
1198 QMenu *arrangeMenu(menu.addMenu(tr("Arrange")));
1199 arrangeMenu->addAction(new Action(ActionsManager::RestoreTabAction, {}, executor, arrangeMenu));
1200 arrangeMenu->addSeparator();
1201 arrangeMenu->addAction(new Action(ActionsManager::RestoreAllAction, {}, executor, arrangeMenu));
1202 arrangeMenu->addAction(new Action(ActionsManager::MaximizeAllAction, {}, executor, arrangeMenu));
1203 arrangeMenu->addAction(new Action(ActionsManager::MinimizeAllAction, {}, executor, arrangeMenu));
1204 arrangeMenu->addSeparator();
1205 arrangeMenu->addAction(new Action(ActionsManager::CascadeAllAction, {}, executor, arrangeMenu));
1206 arrangeMenu->addAction(new Action(ActionsManager::TileAllAction, {}, executor, arrangeMenu));
1207
1208 menu.addMenu(createCustomizationMenu(getIdentifier(), {cycleAction, thumbnailsAction}, &menu));
1209 menu.exec(event->globalPos());
1210 }
1211
findTabBar()1212 void TabBarToolBarWidget::findTabBar()
1213 {
1214 TabBarWidget *tabBar(m_tabBar ? m_tabBar : findChild<TabBarWidget*>());
1215
1216 if (!tabBar && getMainWindow())
1217 {
1218 tabBar = getMainWindow()->getTabBar();
1219
1220 if (tabBar)
1221 {
1222 connect(tabBar, &TabBarWidget::tabsAmountChanged, this, &TabBarToolBarWidget::updateVisibility);
1223
1224 updateVisibility();
1225 }
1226 }
1227
1228 if (tabBar && !m_tabBar)
1229 {
1230 tabBar->setParent(this);
1231 tabBar->setVisible(!isCollapsed());
1232
1233 m_tabBar = tabBar;
1234 }
1235 }
1236
clearEntries()1237 void TabBarToolBarWidget::clearEntries()
1238 {
1239 findTabBar();
1240
1241 for (int i = (actions().count() - 1); i >= 0; --i)
1242 {
1243 if (m_tabBar && widgetForAction(actions().at(i)) != m_tabBar)
1244 {
1245 removeAction(actions().at(i));
1246 }
1247 }
1248
1249 resetGeometry();
1250 }
1251
populateEntries()1252 void TabBarToolBarWidget::populateEntries()
1253 {
1254 const ToolBarsManager::ToolBarDefinition definition(getDefinition());
1255 const bool isHorizontal(getArea() != Qt::LeftToolBarArea && getArea() != Qt::RightToolBarArea);
1256 QVector<QPointer<QWidget> > addressFields;
1257 QVector<QPointer<QWidget> > searchFields;
1258
1259 findTabBar();
1260
1261 for (int i = 0; i < definition.entries.count(); ++i)
1262 {
1263 if (definition.entries.at(i).action == QLatin1String("separator"))
1264 {
1265 addSeparator();
1266 }
1267 else
1268 {
1269 if (m_tabBar && definition.entries.at(i).action == QLatin1String("TabBarWidget"))
1270 {
1271 addWidget(m_tabBar);
1272 }
1273 else
1274 {
1275 const bool isTabBar(definition.entries.at(i).action == QLatin1String("TabBarWidget"));
1276
1277 if (isTabBar && m_tabBar)
1278 {
1279 continue;
1280 }
1281
1282 QWidget *widget(WidgetFactory::createToolBarItem(definition.entries.at(i), getWindow(), this));
1283
1284 if (widget)
1285 {
1286 addWidget(widget);
1287
1288 if (isTabBar)
1289 {
1290 m_tabBar = qobject_cast<TabBarWidget*>(widget);
1291
1292 if (m_tabBar)
1293 {
1294 connect(m_tabBar, &TabBarWidget::tabsAmountChanged, this, &TabBarToolBarWidget::updateVisibility);
1295
1296 updateVisibility();
1297 }
1298 }
1299 else
1300 {
1301 if (definition.entries.at(i).action == QLatin1String("AddressWidget"))
1302 {
1303 addressFields.append(widget);
1304 }
1305 else if (definition.entries.at(i).action == QLatin1String("SearchWidget"))
1306 {
1307 searchFields.append(widget);
1308 }
1309
1310 layout()->setAlignment(widget, (isHorizontal ? Qt::AlignVCenter : Qt::AlignHCenter));
1311 }
1312 }
1313 }
1314 }
1315 }
1316
1317 if (m_tabBar)
1318 {
1319 switch (getArea())
1320 {
1321 case Qt::BottomToolBarArea:
1322 layout()->setAlignment(m_tabBar, Qt::AlignTop);
1323
1324 break;
1325 case Qt::LeftToolBarArea:
1326 layout()->setAlignment(m_tabBar, Qt::AlignRight);
1327
1328 break;
1329 case Qt::RightToolBarArea:
1330 layout()->setAlignment(m_tabBar, Qt::AlignLeft);
1331
1332 break;
1333 default:
1334 layout()->setAlignment(m_tabBar, Qt::AlignBottom);
1335
1336 break;
1337 }
1338
1339 QTimer::singleShot(200, m_tabBar, &TabBarWidget::updateSize);
1340 }
1341
1342 setAddressFields(addressFields);
1343 setSearchFields(searchFields);
1344 }
1345
updateVisibility()1346 void TabBarToolBarWidget::updateVisibility()
1347 {
1348 setVisible(shouldBeVisible((getMainWindow() ? getMainWindow()->windowState().testFlag(Qt::WindowFullScreen) : false) ? ToolBarsManager::FullScreenMode : ToolBarsManager::NormalMode));
1349 }
1350
shouldBeVisible(ToolBarsManager::ToolBarsMode mode) const1351 bool TabBarToolBarWidget::shouldBeVisible(ToolBarsManager::ToolBarsMode mode) const
1352 {
1353 const ToolBarsManager::ToolBarDefinition definition(getDefinition());
1354
1355 if (definition.getVisibility(mode) == ToolBarsManager::AutoVisibilityToolBar)
1356 {
1357 const TabBarWidget *tabBar(findChild<TabBarWidget*>());
1358
1359 return (tabBar && tabBar->count() > 1);
1360 }
1361
1362 return ((mode == ToolBarsManager::NormalMode && definition.hasToggle) || calculateShouldBeVisible(definition, getState(), mode));
1363 }
1364
event(QEvent * event)1365 bool TabBarToolBarWidget::event(QEvent *event)
1366 {
1367 if (event->type() == QEvent::LayoutRequest)
1368 {
1369 const bool result(QToolBar::event(event));
1370
1371 setContentsMargins(0, 0, 0, 0);
1372
1373 layout()->setMargin(0);
1374
1375 return result;
1376 }
1377
1378 return ToolBarWidget::event(event);
1379 }
1380
1381 }
1382