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

0001 /*
0002     SPDX-FileCopyrightText: 2006-2007 Alexander Dymo <adymo@kdevelop.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "mainwindow.h"
0008 #include "mainwindow_p.h"
0009 
0010 #include <QApplication>
0011 #include <QDesktopWidget>
0012 #include <QMenuBar>
0013 #include <QStatusBar>
0014 #include <QScreen>
0015 
0016 #include <KConfigGroup>
0017 #include <KLocalizedString>
0018 #include <KSharedConfig>
0019 #include <KToolBar>
0020 
0021 #include "area.h"
0022 #include "view.h"
0023 #include "controller.h"
0024 #include "container.h"
0025 #include "idealbuttonbarwidget.h"
0026 #include "idealcontroller.h"
0027 #include "holdupdates.h"
0028 #include <debug.h>
0029 
0030 namespace Sublime {
0031 
0032 MainWindow::MainWindow(Controller *controller, Qt::WindowFlags flags)
0033     : KParts::MainWindow(nullptr, flags)
0034     , d_ptr(new MainWindowPrivate(this, controller))
0035 {
0036     connect(this, &MainWindow::destroyed, controller, QOverload<>::of(&Controller::areaReleased));
0037 
0038     loadGeometry(KSharedConfig::openConfig()->group("Main Window"));
0039 
0040     // don't allow AllowTabbedDocks - that doesn't make sense for "ideal" UI
0041     setDockOptions(QMainWindow::AnimatedDocks);
0042 }
0043 
0044 bool MainWindow::containsView(View* view) const
0045 {
0046     const auto areas = this->areas();
0047 
0048     return std::any_of(areas.begin(), areas.end(), [view](Area* area) {
0049         return area->views().contains(view);
0050     });
0051 }
0052 
0053 QList< Area* > MainWindow::areas() const
0054 {
0055     QList< Area* > areas = controller()->areas(const_cast<MainWindow*>(this));
0056     if(areas.isEmpty())
0057         areas = controller()->defaultAreas();
0058 
0059     return areas;
0060 }
0061 
0062 MainWindow::~MainWindow()
0063 {
0064     qCDebug(SUBLIME) << "destroying mainwindow";
0065 }
0066 
0067 void MainWindow::reconstructViews(const QList<View*>& topViews)
0068 {
0069     Q_D(MainWindow);
0070 
0071     d->reconstructViews(topViews);
0072 }
0073 
0074 QList<View*> MainWindow::topViews() const
0075 {
0076     Q_D(const MainWindow);
0077 
0078     QList<View*> topViews;
0079     const auto views = d->area->views();
0080     for (View* view : views) {
0081         if(view->hasWidget())
0082         {
0083             QWidget* widget = view->widget();
0084             if(widget->parent() && widget->parent()->parent())
0085             {
0086                 auto* container = qobject_cast<Container*>(widget->parent()->parent());
0087                 if(container->currentWidget() == widget)
0088                     topViews << view;
0089             }
0090         }
0091     }
0092     return topViews;
0093 }
0094 
0095 QSet<Container*> MainWindow::containers() const
0096 {
0097     Q_D(const MainWindow);
0098 
0099     return QSet<Container*>(d->viewContainers.cbegin(), d->viewContainers.cend());
0100 }
0101 
0102 void MainWindow::setArea(Area *area)
0103 {
0104     Q_D(MainWindow);
0105 
0106     if (d->area)
0107         disconnect(d->area, nullptr, d, nullptr);
0108 
0109     bool differentArea = (area != d->area);
0110     /* All views will be removed from dock area now.  However, this does
0111        not mean those are removed from area, so prevent slotDockShown
0112        from recording those views as no longer shown in the area.  */
0113     d->ignoreDockShown = true;
0114 
0115     if (d->autoAreaSettingsSave && differentArea)
0116         saveSettings();
0117 
0118     HoldUpdates hu(this);
0119     if (d->area)
0120         clearArea();
0121     d->area = area;
0122     d->reconstruct();
0123 
0124     if(d->area->activeView())
0125         activateView(d->area->activeView());
0126     else
0127         d->activateFirstVisibleView();
0128 
0129     initializeStatusBar();
0130     emit areaChanged(area);
0131     d->ignoreDockShown = false;
0132 
0133     hu.stop();
0134 
0135     loadSettings();
0136     connect(area, &Area::viewAdded, d, &MainWindowPrivate::viewAdded);
0137     connect(area, &Area::viewRemoved, d, &MainWindowPrivate::viewRemovedInternal);
0138     connect(area, &Area::requestToolViewRaise, d, &MainWindowPrivate::raiseToolView);
0139     connect(area, &Area::aboutToRemoveView, d, &MainWindowPrivate::aboutToRemoveView);
0140     connect(area, &Area::toolViewAdded, d, &MainWindowPrivate::toolViewAdded);
0141     connect(area, &Area::aboutToRemoveToolView, d, &MainWindowPrivate::aboutToRemoveToolView);
0142     connect(area, &Area::toolViewMoved, d, &MainWindowPrivate::toolViewMoved);
0143 }
0144 
0145 void MainWindow::initializeStatusBar()
0146 {
0147     //nothing here, reimplement in the subclasses if you want to have status bar
0148     //inside the bottom tool view buttons row
0149 }
0150 
0151 void MainWindow::clearArea()
0152 {
0153     Q_D(MainWindow);
0154 
0155     emit areaCleared(d->area);
0156     d->clearArea();
0157 }
0158 
0159 QList<View*> MainWindow::toolDocks() const
0160 {
0161     Q_D(const MainWindow);
0162 
0163     return d->docks;
0164 }
0165 
0166 Area *Sublime::MainWindow::area() const
0167 {
0168     Q_D(const MainWindow);
0169 
0170     return d->area;
0171 }
0172 
0173 Controller *MainWindow::controller() const
0174 {
0175     Q_D(const MainWindow);
0176 
0177     return d->controller;
0178 }
0179 
0180 View *MainWindow::activeView() const
0181 {
0182     Q_D(const MainWindow);
0183 
0184     return d->activeView;
0185 }
0186 
0187 View *MainWindow::activeToolView() const
0188 {
0189     Q_D(const MainWindow);
0190 
0191     return d->activeToolView;
0192 }
0193 
0194 void MainWindow::activateView(Sublime::View* view, bool focus)
0195 {
0196     Q_D(MainWindow);
0197 
0198     const auto containerIt = d->viewContainers.constFind(view);
0199     if (containerIt == d->viewContainers.constEnd())
0200         return;
0201 
0202     if (d->activeView == view)
0203     {
0204         if (focus && view && !view->widget()->hasFocus())
0205             view->widget()->setFocus();
0206         return;
0207     }
0208 
0209     (*containerIt)->setCurrentWidget(view->widget());
0210 
0211     setActiveView(view, focus);
0212     d->area->setActiveView(view);
0213 }
0214 
0215 void MainWindow::setActiveView(View *view, bool focus)
0216 {
0217     Q_D(MainWindow);
0218 
0219     View* oldActiveView = d->activeView;
0220 
0221     d->activeView = view;
0222 
0223     if (focus && view && !view->widget()->hasFocus())
0224         view->widget()->setFocus();
0225 
0226     if(d->activeView != oldActiveView)
0227         emit activeViewChanged(view);
0228 }
0229 
0230 void Sublime::MainWindow::setActiveToolView(View *view)
0231 {
0232     Q_D(MainWindow);
0233 
0234     d->activeToolView = view;
0235     emit activeToolViewChanged(view);
0236 }
0237 
0238 void MainWindow::saveSettings()
0239 {
0240     Q_D(MainWindow);
0241 
0242     d->disableConcentrationMode();
0243     QString group = QStringLiteral("MainWindow");
0244     if (area())
0245         group += QLatin1Char('_') + area()->objectName();
0246     KConfigGroup cg = KSharedConfig::openConfig()->group(group);
0247     // This will try to save also the window size and the enabled state of the statusbar.
0248     // But it's OK, since we won't use this information when loading.
0249     saveMainWindowSettings(cg);
0250 
0251     //debugToolBar visibility is stored separately to allow a area dependent default value
0252     const auto toolBars = this->toolBars();
0253     for (KToolBar* toolbar : toolBars) {
0254         if (toolbar->objectName() == QLatin1String("debugToolBar")) {
0255             cg.writeEntry("debugToolBarVisibility", toolbar->isVisibleTo(this));
0256         }
0257     }
0258 
0259     d->idealController->leftBarWidget->saveOrderSettings(cg);
0260     d->idealController->bottomBarWidget->saveOrderSettings(cg);
0261     d->idealController->rightBarWidget->saveOrderSettings(cg);
0262 
0263     cg.sync();
0264 }
0265 
0266 void MainWindow::loadSettings()
0267 {
0268     Q_D(MainWindow);
0269 
0270     HoldUpdates hu(this);
0271 
0272     qCDebug(SUBLIME) << "loading settings for " << (area() ? area()->objectName() : QString());
0273     QString group = QStringLiteral("MainWindow");
0274     if (area())
0275         group += QLatin1Char('_') + area()->objectName();
0276     KConfigGroup cg = KSharedConfig::openConfig()->group(group);
0277 
0278     // What follows is copy-paste from applyMainWindowSettings.  Unfortunately,
0279     // we don't really want that one to try restoring window size, and we also
0280     // cannot stop it from doing that in any clean way.
0281     // We also do not want that one do it for the enabled state of the statusbar:
0282     // KMainWindow scans the widget tree for a QStatusBar-inheriting instance and
0283     // set enabled state by the config value stored by the key "StatusBar",
0284     // while the QStatusBar subclass used in sublime should always be enabled.
0285     auto* mb = findChild<QMenuBar *>();
0286     if (mb) {
0287         QString entry = cg.readEntry ("MenuBar", "Enabled");
0288         if ( entry == QLatin1String("Disabled") )
0289            mb->hide();
0290         else
0291            mb->show();
0292     }
0293 
0294     if ( !autoSaveSettings() || cg.name() == autoSaveGroup() ) {
0295         QString entry = cg.readEntry ("ToolBarsMovable", "Enabled");
0296         if ( entry == QLatin1String("Disabled") )
0297             KToolBar::setToolBarsLocked(true);
0298         else
0299             KToolBar::setToolBarsLocked(false);
0300     }
0301 
0302     // Utilise the QMainWindow::restoreState() functionality
0303     // Note that we're fixing KMainWindow bug here -- the original
0304     // code has this fragment above restoring toolbar properties.
0305     // As result, each save/restore would move the toolbar a bit to
0306     // the left.
0307     if (cg.hasKey("State")) {
0308         QByteArray state;
0309         state = cg.readEntry("State", state);
0310         state = QByteArray::fromBase64(state);
0311         // One day will need to load the version number, but for now, assume 0
0312         restoreState(state);
0313     } else {
0314         // If there's no state we use a default size of 870x650
0315         // Resize only when showing "code" area. If we do that for other areas,
0316         // then we'll hit bug https://bugs.kde.org/show_bug.cgi?id=207990
0317         // TODO: adymo: this is more like a hack, we need a proper first-start initialization
0318         if (area() && area()->objectName() == QLatin1String("code"))
0319             resize(870,650);
0320     }
0321 
0322     int n = 1; // Toolbar counter. toolbars are counted from 1,
0323     const auto toolBars = this->toolBars();
0324     for (KToolBar* toolbar : toolBars) {
0325         QString group(QStringLiteral("Toolbar"));
0326         // Give a number to the toolbar, but prefer a name if there is one,
0327         // because there's no real guarantee on the ordering of toolbars
0328         group += (toolbar->objectName().isEmpty() ? QString::number(n) : QLatin1Char(' ')+toolbar->objectName());
0329 
0330         KConfigGroup toolbarGroup(&cg, group);
0331         toolbar->applySettings(toolbarGroup);
0332 
0333         if (toolbar->objectName() == QLatin1String("debugToolBar")) {
0334             //debugToolBar visibility is stored separately to allow a area dependent default value
0335             bool visibility = cg.readEntry("debugToolBarVisibility", area()->objectName() == QLatin1String("debug"));
0336             toolbar->setVisible(visibility);
0337         }
0338         n++;
0339     }
0340 
0341     const bool tabBarHidden = !Container::configTabBarVisible();
0342     const bool closeButtonsOnTabs = Container::configCloseButtonsOnTabs();
0343     for (Container *container : qAsConst(d->viewContainers)) {
0344         container->setTabBarHidden(tabBarHidden);
0345         container->setCloseButtonsOnTabs(closeButtonsOnTabs);
0346     }
0347 
0348     hu.stop();
0349 
0350     d->idealController->leftBarWidget->loadOrderSettings(cg);
0351     d->idealController->bottomBarWidget->loadOrderSettings(cg);
0352     d->idealController->rightBarWidget->loadOrderSettings(cg);
0353 
0354     emit settingsLoaded();
0355 
0356     d->disableConcentrationMode();
0357 }
0358 
0359 bool MainWindow::queryClose()
0360 {
0361 //    saveSettings();
0362     KConfigGroup config(KSharedConfig::openConfig(), "Main Window");
0363     saveGeometry(config);
0364     config.sync();
0365 
0366     return KParts::MainWindow::queryClose();
0367 }
0368 
0369 void MainWindow::postMessage(Message* message)
0370 {
0371     Q_D(MainWindow);
0372 
0373     d->postMessage(message);
0374 }
0375 
0376 QString MainWindow::screenKey() const
0377 {
0378     const int scnum = QApplication::desktop()->screenNumber(parentWidget());
0379     QList<QScreen *> screens = QApplication::screens();
0380     QRect desk = screens[scnum]->geometry();
0381 
0382     // if the desktop is virtual then use virtual screen size
0383     if (QGuiApplication::primaryScreen()->virtualSiblings().size() > 1)
0384         desk = QGuiApplication::primaryScreen()->virtualGeometry();
0385 
0386     return QStringLiteral("Desktop %1 %2")
0387         .arg(desk.width()).arg(desk.height());
0388 }
0389 
0390 void MainWindow::saveGeometry(KConfigGroup &config) const
0391 {
0392     config.writeEntry(screenKey(), geometry());
0393 
0394 }
0395 void MainWindow::loadGeometry(const KConfigGroup &config)
0396 {
0397     // The below code, essentially, is copy-paste from
0398     // KMainWindow::restoreWindowSize.  Right now, that code is buggy,
0399     // as per http://permalink.gmane.org/gmane.comp.kde.devel.core/52423
0400     // so we implement a less theoretically correct, but working, version
0401     // below
0402     QRect g = config.readEntry(screenKey(), QRect());
0403     if (!g.isEmpty())
0404         setGeometry(g);
0405 }
0406 
0407 void MainWindow::enableAreaSettingsSave()
0408 {
0409     Q_D(MainWindow);
0410 
0411     d->autoAreaSettingsSave = true;
0412 }
0413 
0414 QWidget *MainWindow::statusBarLocation() const
0415 {
0416     Q_D(const MainWindow);
0417 
0418     return d->idealController->statusBarLocation();
0419 }
0420 
0421 ViewBarContainer *MainWindow::viewBarContainer() const
0422 {
0423     Q_D(const MainWindow);
0424 
0425     return d->viewBarContainer;
0426 }
0427 
0428 void MainWindow::setTabBarLeftCornerWidget(QWidget* widget)
0429 {
0430     Q_D(MainWindow);
0431 
0432     d->setTabBarLeftCornerWidget(widget);
0433 }
0434 
0435 void MainWindow::tabDoubleClicked(View* view)
0436 {
0437     Q_UNUSED(view);
0438 
0439     Q_D(MainWindow);
0440 
0441     d->toggleDocksShown();
0442 }
0443 
0444 void MainWindow::tabContextMenuRequested(View* , QMenu* )
0445 {
0446     // do nothing
0447 }
0448 
0449 void MainWindow::tabToolTipRequested(View*, Container*, int)
0450 {
0451     // do nothing
0452 }
0453 
0454 void MainWindow::newTabRequested()
0455 {
0456 }
0457 
0458 void MainWindow::dockBarContextMenuRequested(Qt::DockWidgetArea , const QPoint& )
0459 {
0460     // do nothing
0461 }
0462 
0463 View* MainWindow::viewForPosition(const QPoint& globalPos) const
0464 {
0465     Q_D(const MainWindow);
0466 
0467     for (Container* container : qAsConst(d->viewContainers)) {
0468         QRect globalGeom = QRect(container->mapToGlobal(QPoint(0,0)), container->mapToGlobal(QPoint(container->width(), container->height())));
0469        if(globalGeom.contains(globalPos))
0470        {
0471            return d->widgetToView[container->currentWidget()];
0472        }
0473     }
0474 
0475     return nullptr;
0476 }
0477 
0478 void MainWindow::setBackgroundCentralWidget(QWidget* w)
0479 {
0480     Q_D(MainWindow);
0481 
0482     d->setBackgroundCentralWidget(w);
0483 }
0484 
0485 }
0486 
0487 #include "moc_mainwindow.cpp"