File indexing completed on 2024-04-28 04:37:32

0001 /*
0002     SPDX-FileCopyrightText: 2007 Roberto Raggi <roberto@kdevelop.org>
0003     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
0004     SPDX-FileCopyrightText: 2011 Alexander Dymo <adymo@kdevelop.org>
0005 
0006     SPDX-License-Identifier: LicenseRef-MIT-KDevelop-Ideal
0007 */
0008 
0009 #include "idealdockwidget.h"
0010 #include "mainwindow.h"
0011 #include "area.h"
0012 #include "document.h"
0013 #include "view.h"
0014 
0015 #include <util/scopeddialog.h>
0016 
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 #include <KSharedConfig>
0020 #include <KShortcutWidget>
0021 
0022 #include <QAbstractButton>
0023 #include <QDialog>
0024 #include <QDialogButtonBox>
0025 #include <QToolBar>
0026 #include <QVBoxLayout>
0027 #include <QActionGroup>
0028 
0029 using namespace Sublime;
0030 
0031 IdealDockWidget::IdealDockWidget(IdealController *controller, Sublime::MainWindow *parent)
0032     : QDockWidget(parent),
0033       m_area(nullptr),
0034       m_view(nullptr),
0035       m_docking_area(Qt::NoDockWidgetArea),
0036       m_controller(controller)
0037 {
0038     setAutoFillBackground(true);
0039     setContextMenuPolicy(Qt::CustomContextMenu);
0040     connect(this, &IdealDockWidget::customContextMenuRequested,
0041             this, &IdealDockWidget::contextMenuRequested);
0042 
0043     auto *closeButton = findChild<QAbstractButton *>(QStringLiteral("qt_dockwidget_closebutton"));
0044     if (closeButton) {
0045     disconnect(closeButton, &QAbstractButton::clicked, nullptr, nullptr);
0046 
0047     connect(closeButton, &QAbstractButton::clicked, this, &IdealDockWidget::closeRequested);
0048     }
0049 
0050     setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0051     // do not allow to move docks to the top dock area (no buttonbar there in our current UI)
0052     setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea);
0053 }
0054 
0055 IdealDockWidget::~IdealDockWidget()
0056 {
0057 }
0058 
0059 Area *IdealDockWidget::area() const
0060 { return m_area; }
0061 
0062 void IdealDockWidget::setArea(Area *area)
0063 { m_area = area; }
0064 
0065 View *IdealDockWidget::view() const
0066 { return m_view; }
0067 
0068 void IdealDockWidget::setView(View *view)
0069 { m_view = view; }
0070 
0071 Qt::DockWidgetArea IdealDockWidget::dockWidgetArea() const
0072 { return m_docking_area; }
0073 
0074 void IdealDockWidget::setDockWidgetArea(Qt::DockWidgetArea dockingArea)
0075 { m_docking_area = dockingArea; }
0076 
0077 void IdealDockWidget::slotRemove()
0078 {
0079     m_area->removeToolView(m_view);
0080 }
0081 
0082 void IdealDockWidget::contextMenuRequested(const QPoint &point)
0083 {
0084     QWidget* senderWidget = qobject_cast<QWidget*>(sender());
0085     Q_ASSERT(senderWidget);
0086 
0087     KDevelop::ScopedDialog<QMenu> menu(senderWidget);
0088     menu->addSection(windowIcon(), m_view->document()->title());
0089 
0090     const QList<QAction*> viewActions = m_view->contextMenuActions();
0091     if(!viewActions.isEmpty()) {
0092         // add the view's actions to the context menu,
0093         // checking each if it can be represented
0094         for (const auto action : viewActions) {
0095             if (!action->text().isEmpty() && !action->iconText().isEmpty()) {
0096                 // avoid adding empty menu items
0097                 menu->addAction(action);
0098             }
0099         }
0100         menu->addSeparator();
0101     }
0102 
0103     ///TODO: can this be cleaned up?
0104     if(auto* toolBar = widget()->findChild<QToolBar*>()) {
0105         menu->addAction(toolBar->toggleViewAction());
0106         menu->addSeparator();
0107     }
0108 
0109     /// start position menu
0110     QMenu* positionMenu = menu->addMenu(i18nc("@title:menu", "Tool View Position"));
0111 
0112     auto* g = new QActionGroup(positionMenu);
0113 
0114     auto* left = new QAction(i18nc("@option:radio tool view position", "Left"), g);
0115     auto* bottom = new QAction(i18nc("@option:radio tool view position", "Bottom"), g);
0116     auto* right = new QAction(i18nc("@option:radio tool view position", "Right"), g);
0117     auto* detach = new QAction(i18nc("@option:radio tool view position", "Detached"), g);
0118 
0119     for (auto action : {left, bottom, right, detach}) {
0120         positionMenu->addAction(action);
0121         action->setCheckable(true);
0122     }
0123     if (isFloating()) {
0124         detach->setChecked(true);
0125     } else if (m_docking_area == Qt::BottomDockWidgetArea)
0126         bottom->setChecked(true);
0127     else if (m_docking_area == Qt::LeftDockWidgetArea)
0128         left->setChecked(true);
0129     else if (m_docking_area == Qt::RightDockWidgetArea)
0130         right->setChecked(true);
0131     /// end position menu
0132 
0133     menu->addSeparator();
0134 
0135     QAction *setShortcut = menu->addAction(QIcon::fromTheme(QStringLiteral("configure-shortcuts")), i18nc("@action:inmenu", "Assign Shortcut..."));
0136     setShortcut->setToolTip(i18nc("@info:tooltip", "Use this shortcut to trigger visibility of the tool view."));
0137 
0138     menu->addSeparator();
0139     QAction* remove = menu->addAction(QIcon::fromTheme(QStringLiteral("dialog-close")), i18nc("@action:inmenu", "Remove Tool View"));
0140 
0141     QAction* triggered = menu->exec(senderWidget->mapToGlobal(point));
0142 
0143     if (triggered)
0144     {
0145         if ( triggered == remove ) {
0146             slotRemove();
0147             return;
0148         } else if ( triggered == setShortcut ) {
0149             auto* dialog = new QDialog(this);
0150             dialog->setWindowTitle(i18nc("@title:window", "Assign Shortcut For '%1' Tool View", m_view->document()->title()));
0151             auto *w = new KShortcutWidget(dialog);
0152             w->setShortcut(m_controller->actionForView(m_view)->shortcuts());
0153             auto* dialogLayout = new QVBoxLayout(dialog);
0154             dialogLayout->addWidget(w);
0155             auto* buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
0156             dialogLayout->addWidget(buttonBox);
0157             connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
0158             connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
0159 
0160             if (dialog->exec() == QDialog::Accepted) {
0161                 m_controller->actionForView(m_view)->setShortcuts(w->shortcut());
0162 
0163                 //save shortcut config
0164                 KConfigGroup config = KSharedConfig::openConfig()->group("UI");
0165                 const QStringList shortcuts{
0166                     w->shortcut().value(0).toString(),
0167                     w->shortcut().value(1).toString(),
0168                 };
0169                 config.writeEntry(QStringLiteral("Shortcut for %1").arg(m_view->document()->title()), shortcuts);
0170                 config.sync();
0171             }
0172             delete dialog;
0173 
0174             return;
0175         } else if ( triggered == detach ) {
0176             setFloating(true);
0177             m_area->raiseToolView(m_view);
0178             return;
0179         }
0180 
0181         if (isFloating()) {
0182             setFloating(false);
0183         }
0184 
0185         Sublime::Position pos;
0186         if (triggered == left)
0187             pos = Sublime::Left;
0188         else if (triggered == bottom)
0189             pos = Sublime::Bottom;
0190         else if (triggered == right)
0191             pos = Sublime::Right;
0192         else
0193             return;
0194 
0195         Area *area = m_area;
0196         View *view = m_view;
0197         /* This call will delete *this, so we no longer
0198            can access member variables. */
0199         m_area->moveToolView(m_view, pos);
0200         area->raiseToolView(view);
0201     }
0202 }
0203 
0204 #include "moc_idealdockwidget.cpp"