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

0001 /*
0002     SPDX-FileCopyrightText: 2007 Alexander Dymo <adymo@kdevelop.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "uicontroller.h"
0008 
0009 #include <QAction>
0010 #include <QApplication>
0011 #include <QDialog>
0012 #include <QListWidget>
0013 #include <QPushButton>
0014 #include <QMap>
0015 #include <QPointer>
0016 #include <QTimer>
0017 #include <QVBoxLayout>
0018 
0019 #include <KLocalizedString>
0020 #include <KXMLGUIClient>
0021 
0022 #include <sublime/area.h>
0023 #include <sublime/view.h>
0024 #include <sublime/tooldocument.h>
0025 #include <sublime/holdupdates.h>
0026 
0027 #include <interfaces/itoolviewactionlistener.h>
0028 #include <sublime/message.h>
0029 #include <util/scopeddialog.h>
0030 
0031 #include "core.h"
0032 #include "configpage.h"
0033 #include "configdialog.h"
0034 #include "debug.h"
0035 #include "editorconfigpage.h"
0036 #include "shellextension.h"
0037 #include "plugincontroller.h"
0038 #include "session.h"
0039 #include "mainwindow.h"
0040 #include "workingsetcontroller.h"
0041 #include "workingsets/workingset.h"
0042 #include "settings/bgpreferences.h"
0043 #include "settings/languagepreferences.h"
0044 #include "settings/environmentpreferences.h"
0045 #include "settings/pluginpreferences.h"
0046 #include "settings/projectpreferences.h"
0047 #include "settings/sourceformattersettings.h"
0048 #include "settings/uipreferences.h"
0049 #include "settings/templateconfig.h"
0050 #include "settings/analyzerspreferences.h"
0051 #include "settings/documentationpreferences.h"
0052 #include "settings/runtimespreferences.h"
0053 
0054 namespace KDevelop {
0055 
0056 class UiControllerPrivate {
0057 public:
0058     UiControllerPrivate(Core* core, UiController* controller)
0059         : core(core)
0060         , areasRestored(false)
0061         , m_controller(controller)
0062     {
0063         if (Core::self()->workingSetControllerInternal())
0064             Core::self()->workingSetControllerInternal()->initializeController(m_controller);
0065 
0066         m_controller->connect(m_controller, &Sublime::Controller::mainWindowAdded, m_controller, &UiController::mainWindowAdded);
0067 
0068         QMap<QString, Sublime::Position> desired;
0069 
0070         desired[QStringLiteral("org.kdevelop.ClassBrowserView")] = Sublime::Left;
0071         desired[QStringLiteral("org.kdevelop.DocumentsView")] = Sublime::Left;
0072         desired[QStringLiteral("org.kdevelop.ProjectsView")] = Sublime::Left;
0073         desired[QStringLiteral("org.kdevelop.FileManagerView")] = Sublime::Left;
0074         desired[QStringLiteral("org.kdevelop.ProblemReporterView")] = Sublime::Bottom;
0075         desired[QStringLiteral("org.kdevelop.OutputView")] = Sublime::Bottom;
0076         desired[QStringLiteral("org.kdevelop.ContextBrowser")] = Sublime::Bottom;
0077         desired[QStringLiteral("org.kdevelop.KonsoleView")] = Sublime::Bottom;
0078         desired[QStringLiteral("org.kdevelop.SnippetView")] = Sublime::Right;
0079         desired[QStringLiteral("org.kdevelop.ExternalScriptView")] = Sublime::Right;
0080         desired[QStringLiteral("org.kdevelop.ScratchpadView")] = Sublime::Left;
0081         auto* a = new Sublime::Area(m_controller, QStringLiteral("code"), i18nc("area", "Code"));
0082         a->setDesiredToolViews(desired);
0083         a->setIconName(QStringLiteral("document-edit"));
0084         m_controller->addDefaultArea(a);
0085 
0086         desired.clear();
0087         desired[QStringLiteral("org.kdevelop.debugger.VariablesView")] = Sublime::Left;
0088         desired[QStringLiteral("org.kdevelop.debugger.BreakpointsView")] = Sublime::Bottom;
0089         desired[QStringLiteral("org.kdevelop.debugger.StackView")] = Sublime::Bottom;
0090         desired[QStringLiteral("org.kdevelop.debugger.ConsoleView")] = Sublime::Bottom;
0091         desired[QStringLiteral("org.kdevelop.KonsoleView")] = Sublime::Bottom;
0092         a = new Sublime::Area(m_controller, QStringLiteral("debug"), i18nc("area", "Debug"));
0093         a->setDesiredToolViews(desired);
0094         a->setIconName(QStringLiteral("debug-run"));
0095         m_controller->addDefaultArea(a);
0096 
0097         desired.clear();
0098         desired[QStringLiteral("org.kdevelop.ProjectsView")] = Sublime::Left;
0099         desired[QStringLiteral("org.kdevelop.PatchReview")] = Sublime::Bottom;
0100 
0101         a = new Sublime::Area(m_controller, QStringLiteral("review"), i18nc("area", "Review"));
0102         a->setDesiredToolViews(desired);
0103         a->setIconName(QStringLiteral("text-x-patch"));
0104         m_controller->addDefaultArea(a);
0105 
0106         if(!(Core::self()->setupFlags() & Core::NoUi))
0107         {
0108             defaultMainWindow = new MainWindow(m_controller);
0109             m_controller->addMainWindow(defaultMainWindow);
0110             activeSublimeWindow = defaultMainWindow;
0111         }
0112         else
0113         {
0114             activeSublimeWindow = defaultMainWindow = nullptr;
0115         }
0116 
0117         m_assistantTimer.setSingleShot(true);
0118         m_assistantTimer.setInterval(100);
0119     }
0120 
0121     void widgetChanged(QWidget*, QWidget* now)
0122     {
0123         if (now) {
0124             auto* win = qobject_cast<Sublime::MainWindow*>(now->window());
0125             if( win )
0126             {
0127                 activeSublimeWindow = win;
0128             }
0129         }
0130     }
0131 
0132     Core* const core;
0133     QPointer<MainWindow> defaultMainWindow;
0134 
0135     QHash<IToolViewFactory*, Sublime::ToolDocument*> factoryDocuments;
0136 
0137     QPointer<Sublime::MainWindow> activeSublimeWindow;
0138     bool areasRestored;
0139 
0140     /// QWidget implementing IToolViewActionListener interface, or null
0141     QPointer<QWidget> activeActionListener;
0142     QTimer m_assistantTimer;
0143 
0144 private:
0145     UiController *m_controller;
0146 };
0147 
0148 
0149 class UiToolViewFactory: public Sublime::ToolFactory {
0150 public:
0151     explicit UiToolViewFactory(IToolViewFactory *factory): m_factory(factory) {}
0152     ~UiToolViewFactory() override { delete m_factory; }
0153     QWidget* create(Sublime::ToolDocument *doc, QWidget *parent = nullptr) override
0154     {
0155         Q_UNUSED( doc );
0156         return m_factory->create(parent);
0157     }
0158 
0159     QList< QAction* > contextMenuActions(QWidget* viewWidget) const override
0160     {
0161         return m_factory->contextMenuActions( viewWidget );
0162     }
0163 
0164     QList<QAction*> toolBarActions( QWidget* viewWidget ) const override
0165     {
0166         return m_factory->toolBarActions( viewWidget );
0167     }
0168 
0169     QString id() const override { return m_factory->id(); }
0170 private:
0171     IToolViewFactory* const m_factory;
0172 };
0173 
0174 
0175 class ViewSelectorItem: public QListWidgetItem {
0176 public:
0177     explicit ViewSelectorItem(const QString& text, IToolViewFactory* factory, QListWidget* parent = nullptr, int type = Type)
0178         : QListWidgetItem(text, parent, type)
0179         , factory(factory)
0180     {}
0181     IToolViewFactory* const factory;
0182 };
0183 
0184 
0185 class NewToolViewListWidget: public QListWidget {
0186     Q_OBJECT
0187 
0188 public:
0189     explicit NewToolViewListWidget(MainWindow *mw, QWidget* parent = nullptr)
0190         :QListWidget(parent), m_mw(mw)
0191     {
0192         connect(this, &NewToolViewListWidget::doubleClicked, this, &NewToolViewListWidget::addNewToolViewByDoubleClick);
0193     }
0194 
0195 Q_SIGNALS:
0196     void addNewToolView(MainWindow *mw, QListWidgetItem *item);
0197 
0198 private Q_SLOTS:
0199     void addNewToolViewByDoubleClick(const QModelIndex& index)
0200     {
0201         QListWidgetItem *item = itemFromIndex(index);
0202         // Disable item so that the tool view can not be added again.
0203         item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
0204         emit addNewToolView(m_mw, item);
0205     }
0206 
0207 private:
0208     MainWindow* const m_mw;
0209 };
0210 
0211 UiController::UiController(Core *core)
0212     : Sublime::Controller(nullptr), IUiController()
0213     , d_ptr(new UiControllerPrivate(core, this))
0214 {
0215     setObjectName(QStringLiteral("UiController"));
0216 
0217     if (!defaultMainWindow() || (Core::self()->setupFlags() & Core::NoUi))
0218         return;
0219 
0220     setupActions();
0221 }
0222 
0223 UiController::~UiController() = default;
0224 
0225 void UiController::setupActions()
0226 {
0227 }
0228 
0229 void UiController::mainWindowAdded(Sublime::MainWindow* mainWindow)
0230 {
0231     connect(mainWindow, &MainWindow::activeToolViewChanged, this, &UiController::slotActiveToolViewChanged);
0232     connect(mainWindow, &MainWindow::areaChanged, this, &UiController::slotAreaChanged); // also check after area reconstruction
0233     connect(mainWindow, &MainWindow::areaCleared, Core::self()->workingSetControllerInternal(), &WorkingSetController::saveArea);
0234 }
0235 
0236 // FIXME: currently, this always create new window. Probably,
0237 // should just rename it.
0238 void UiController::switchToArea(const QString &areaName, SwitchMode switchMode)
0239 {
0240     if (switchMode == ThisWindow) {
0241         showArea(areaName, activeSublimeWindow());
0242         return;
0243     }
0244 
0245     auto *main = new MainWindow(this);
0246 
0247     addMainWindow(main);
0248     showArea(areaName, main);
0249     main->initialize();
0250 
0251     // WTF? First, enabling this code causes crashes since we
0252     // try to disconnect some already-deleted action, or something.
0253     // Second, this code will disconnection the clients from guiFactory
0254     // of the previous main window. Ick!
0255 #if 0
0256     //we need to add all existing guiclients to the new mainwindow
0257     //@todo adymo: add only ones that belong to the area (when the area code is there)
0258     const auto clients = oldMain->guiFactory()->clients();
0259     for (KXMLGUIClient *client : clients) {
0260         main->guiFactory()->addClient(client);
0261     }
0262 #endif
0263 
0264     main->show();
0265 }
0266 
0267 
0268 QWidget* UiController::findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags)
0269 {
0270     Q_D(UiController);
0271 
0272     if(!d->areasRestored || !activeArea())
0273         return nullptr;
0274 
0275     const QList<Sublime::View*> views = activeArea()->toolViews();
0276     for (Sublime::View* view : views) {
0277         auto* doc = qobject_cast<Sublime::ToolDocument*>(view->document());
0278         if(doc && doc->title() == name && view->widget()) {
0279             if(flags & Raise)
0280                 view->requestRaise();
0281             return view->widget();
0282         }
0283     }
0284 
0285     QWidget* ret = nullptr;
0286 
0287     if(flags & Create)
0288     {
0289         Sublime::ToolDocument* doc = d->factoryDocuments.value(factory);
0290         if(!doc) {
0291             doc = new Sublime::ToolDocument(name, this, new UiToolViewFactory(factory));
0292             d->factoryDocuments.insert(factory, doc);
0293         }
0294 
0295         Sublime::View* view = addToolViewToArea(factory, doc, activeArea());
0296         if(view)
0297             ret = view->widget();
0298 
0299         if(flags & Raise)
0300             findToolView(name, factory, Raise);
0301     }
0302 
0303     return ret;
0304 }
0305 
0306 void UiController::raiseToolView(QWidget* toolViewWidget)
0307 {
0308     Q_D(UiController);
0309 
0310     if(!d->areasRestored)
0311         return;
0312 
0313     const QList<Sublime::View*> views = activeArea()->toolViews();
0314     for (Sublime::View* view : views) {
0315         if(view->widget() == toolViewWidget) {
0316             view->requestRaise();
0317             return;
0318         }
0319     }
0320 }
0321 
0322 void UiController::addToolView(const QString & name, IToolViewFactory *factory, FindFlags state)
0323 {
0324     Q_D(UiController);
0325 
0326     if (!factory)
0327         return;
0328 
0329     qCDebug(SHELL) ;
0330     auto *doc = new Sublime::ToolDocument(name, this, new UiToolViewFactory(factory));
0331     d->factoryDocuments[factory] = doc;
0332 
0333     /* Until areas are restored, we don't know which views should be really
0334        added, and which not, so we just record view availability.  */
0335     if (d->areasRestored && state != None) {
0336          const auto areas = allAreas();
0337          for (Sublime::Area* area : areas) {
0338              addToolViewToArea(factory, doc, area);
0339          }
0340     }
0341 }
0342 
0343 void KDevelop::UiController::raiseToolView(Sublime::View * view)
0344 {
0345     const auto areas = allAreas();
0346     for (Sublime::Area* area : areas) {
0347         if( area->toolViews().contains( view ) )
0348             area->raiseToolView( view );
0349     }
0350 
0351     slotActiveToolViewChanged(view);
0352 }
0353 
0354 void UiController::slotAreaChanged(Sublime::Area*)
0355 {
0356     // this slot gets call if an area in *any* MainWindow changed
0357     // so let's first get the "active area"
0358     const auto area = activeSublimeWindow()->area();
0359     if (area) {
0360         // walk through shown tool views and maku sure the
0361         const auto shownIds = area->shownToolViews(Sublime::AllPositions);
0362         for (Sublime::View* toolView : qAsConst(area->toolViews())) {
0363             if (shownIds.contains(toolView->document()->documentSpecifier())) {
0364                 slotActiveToolViewChanged(toolView);
0365             }
0366         }
0367     }
0368 }
0369 
0370 void UiController::slotActiveToolViewChanged(Sublime::View* view)
0371 {
0372     Q_D(UiController);
0373 
0374     if (!view) {
0375         return;
0376     }
0377 
0378     // record the last active tool view action listener
0379     if (qobject_cast<IToolViewActionListener*>(view->widget())) {
0380         d->activeActionListener = view->widget();
0381     }
0382 }
0383 
0384 void KDevelop::UiController::removeToolView(IToolViewFactory *factory)
0385 {
0386     Q_D(UiController);
0387 
0388     if (!factory)
0389         return;
0390 
0391     qCDebug(SHELL) ;
0392     //delete the tooldocument
0393     Sublime::ToolDocument *doc = d->factoryDocuments.value(factory);
0394 
0395     ///@todo adymo: on document deletion all its views shall be also deleted
0396     for (Sublime::View* view : doc->views()) {
0397         const auto areas = allAreas();
0398         for (Sublime::Area *area : areas) {
0399             if (area->removeToolView(view))
0400                 view->deleteLater();
0401         }
0402     }
0403 
0404     d->factoryDocuments.remove(factory);
0405     delete doc;
0406 }
0407 
0408 Sublime::Area *UiController::activeArea()
0409 {
0410     Sublime::MainWindow *m = activeSublimeWindow();
0411     if (m)
0412         return activeSublimeWindow()->area();
0413     return nullptr;
0414 }
0415 
0416 Sublime::MainWindow *UiController::activeSublimeWindow()
0417 {
0418     Q_D(UiController);
0419 
0420     return d->activeSublimeWindow;
0421 }
0422 
0423 MainWindow *UiController::defaultMainWindow()
0424 {
0425     Q_D(UiController);
0426 
0427     return d->defaultMainWindow;
0428 }
0429 
0430 void UiController::initialize()
0431 {
0432     defaultMainWindow()->initialize();
0433 
0434     connect(qApp, &QApplication::focusChanged, this, [this](QWidget* old, QWidget* now) {
0435         Q_D(UiController);
0436         d->widgetChanged(old, now);
0437     });
0438 }
0439 
0440 void UiController::cleanup()
0441 {
0442     for (Sublime::MainWindow* w : mainWindows()) {
0443         w->saveSettings();
0444     }
0445     saveAllAreas(KSharedConfig::openConfig());
0446 
0447     // disconnect early to prevent UB due to accessing partially destroyed UiController
0448     // in the focusChanged handler above
0449     disconnect(qApp, nullptr, this, nullptr);
0450 }
0451 
0452 void UiController::selectNewToolViewToAdd(MainWindow *mw)
0453 {
0454     Q_D(UiController);
0455 
0456     if (!mw || !mw->area())
0457         return;
0458 
0459     ScopedDialog<QDialog> dia(mw);
0460     dia->setWindowTitle(i18nc("@title:window", "Select Tool View to Add"));
0461 
0462     auto mainLayout = new QVBoxLayout(dia);
0463 
0464     auto *list = new NewToolViewListWidget(mw, dia);
0465 
0466     list->setSelectionMode(QAbstractItemView::ExtendedSelection);
0467     list->setSortingEnabled(true);
0468     for (QHash<IToolViewFactory*, Sublime::ToolDocument*>::const_iterator it = d->factoryDocuments.constBegin();
0469         it != d->factoryDocuments.constEnd(); ++it)
0470     {
0471         auto* item = new ViewSelectorItem(it.value()->title(), it.key(), list);
0472         if (!item->factory->allowMultiple() && toolViewPresent(it.value(), mw->area())) {
0473             // Disable item if the tool view is already present.
0474             item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
0475         }
0476         list->addItem(item);
0477     }
0478 
0479     list->setFocus();
0480     connect(list, &NewToolViewListWidget::addNewToolView, this, &UiController::addNewToolView);
0481     mainLayout->addWidget(list);
0482 
0483     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
0484     auto okButton = buttonBox->button(QDialogButtonBox::Ok);
0485     okButton->setDefault(true);
0486     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0487     dia->connect(buttonBox, &QDialogButtonBox::accepted, dia.data(), &QDialog::accept);
0488     dia->connect(buttonBox, &QDialogButtonBox::rejected, dia.data(), &QDialog::reject);
0489     mainLayout->addWidget(buttonBox);
0490 
0491     if (dia->exec() == QDialog::Accepted)
0492     {
0493         const auto items = list->selectedItems();
0494         for (QListWidgetItem* item : items) {
0495             addNewToolView(mw, item);
0496         }
0497     }
0498 }
0499 
0500 void UiController::addNewToolView(MainWindow *mw, QListWidgetItem* item)
0501 {
0502     Q_D(UiController);
0503 
0504     auto *current = static_cast<ViewSelectorItem*>(item);
0505     Sublime::ToolDocument *doc = d->factoryDocuments[current->factory];
0506     Sublime::View *view = doc->createView();
0507     mw->area()->addToolView(view,
0508         Sublime::dockAreaToPosition(current->factory->defaultPosition()));
0509     current->factory->viewCreated(view);
0510 }
0511 
0512 void UiController::showSettingsDialog()
0513 {
0514     ConfigDialog cfgDlg(activeMainWindow());
0515 
0516     auto editorConfigPage = new EditorConfigPage(&cfgDlg);
0517     auto languageConfigPage = new LanguagePreferences(&cfgDlg);
0518     auto analyzersPreferences = new AnalyzersPreferences(&cfgDlg);
0519     auto documentationPreferences = new DocumentationPreferences(&cfgDlg);
0520     auto runtimesPreferences = new RuntimesPreferences(&cfgDlg);
0521     auto templateConfig = new TemplateConfig(&cfgDlg);
0522 
0523     const auto configPages = QVector<KDevelop::ConfigPage*> {
0524         new UiPreferences(&cfgDlg),
0525         new PluginPreferences(&cfgDlg),
0526         new SourceFormatterSettings(&cfgDlg),
0527         new ProjectPreferences(&cfgDlg),
0528         new EnvironmentPreferences(QString(), &cfgDlg),
0529         templateConfig,
0530         documentationPreferences,
0531         analyzersPreferences,
0532         runtimesPreferences,
0533         languageConfigPage,
0534         editorConfigPage
0535     };
0536 
0537     for (auto page : configPages) {
0538         cfgDlg.appendConfigPage(page);
0539     }
0540 
0541     cfgDlg.appendSubConfigPage(languageConfigPage, new BGPreferences(&cfgDlg));
0542 
0543     auto addPluginPages = [&](IPlugin* plugin) {
0544         for (int i = 0, numPages = plugin->configPages(); i < numPages; ++i) {
0545             auto page = plugin->configPage(i, &cfgDlg);
0546             if (!page)
0547                 continue;
0548 
0549             if (page->configPageType() == ConfigPage::LanguageConfigPage) {
0550                 cfgDlg.appendSubConfigPage(languageConfigPage, page);
0551             } else if (page->configPageType() == ConfigPage::AnalyzerConfigPage) {
0552                 cfgDlg.appendSubConfigPage(analyzersPreferences, page);
0553             } else if (page->configPageType() == ConfigPage::RuntimeConfigPage) {
0554                 cfgDlg.appendSubConfigPage(runtimesPreferences, page);
0555             } else if (page->configPageType() == ConfigPage::DocumentationConfigPage) {
0556                 cfgDlg.appendSubConfigPage(documentationPreferences, page);
0557             } else {
0558                 cfgDlg.insertConfigPage(editorConfigPage, page);
0559             }
0560         }
0561     };
0562 
0563     auto plugins = ICore::self()->pluginController()->loadedPlugins();
0564     std::sort(plugins.begin(), plugins.end());
0565 
0566     for (IPlugin* plugin : qAsConst(plugins)) {
0567         addPluginPages(plugin);
0568     }
0569 
0570     // make sure that pages get added whenever a new plugin is loaded (probably from the plugin selection dialog)
0571     // removal on plugin unload is already handled in ConfigDialog
0572     connect(ICore::self()->pluginController(), &IPluginController::pluginLoaded, &cfgDlg, addPluginPages);
0573     cfgDlg.exec();
0574 }
0575 
0576 Sublime::Controller* UiController::controller()
0577 {
0578     return this;
0579 }
0580 
0581 KParts::MainWindow *UiController::activeMainWindow()
0582 {
0583     return activeSublimeWindow();
0584 }
0585 
0586 void UiController::saveArea(Sublime::Area * area, KConfigGroup & group)
0587 {
0588     area->save(group);
0589     auto workingSet = area->workingSet();
0590     if (!workingSet.isEmpty()) {
0591         WorkingSet* set = Core::self()->workingSetControllerInternal()->workingSet(workingSet);
0592         set->saveFromArea(area);
0593     }
0594     for (auto w : mainWindows()) {
0595         if (area == w->area()) {
0596             Core::self()->activeSession()->config()->group("Working File Sets").writeEntry("Active Working Set", workingSet);
0597         }
0598     }
0599 }
0600 
0601 void UiController::loadArea(Sublime::Area * area, const KConfigGroup & group)
0602 {
0603     area->load(group);
0604     if (!area->workingSet().isEmpty()) {
0605         WorkingSet* set = Core::self()->workingSetControllerInternal()->workingSet(area->workingSet());
0606         Q_ASSERT(set->isConnected(area));
0607         Q_UNUSED(set);
0608     }
0609 }
0610 
0611 void UiController::saveAllAreas(const KSharedConfigPtr& config)
0612 {
0613     KConfigGroup uiConfig(config, "User Interface");
0614     int wc = mainWindows().size();
0615     uiConfig.writeEntry("Main Windows Count", wc);
0616     for (int w = 0; w < wc; ++w)
0617     {
0618         KConfigGroup mainWindowConfig(&uiConfig,
0619                                       QStringLiteral("Main Window %1").arg(w));
0620 
0621         for (Sublime::Area* defaultArea : defaultAreas()) {
0622             // FIXME: using object name seems ugly.
0623             QString type = defaultArea->objectName();
0624             Sublime::Area* area = this->area(w, type);
0625             KConfigGroup areaConfig(&mainWindowConfig, QLatin1String("Area ") + type);
0626 
0627             areaConfig.deleteGroup();
0628             areaConfig.writeEntry("id", type);
0629             saveArea(area, areaConfig);
0630             areaConfig.sync();
0631         }
0632     }
0633     uiConfig.sync();
0634 }
0635 
0636 void UiController::loadAllAreas(const KSharedConfigPtr& config)
0637 {
0638     Q_D(UiController);
0639 
0640     KConfigGroup uiConfig(config, "User Interface");
0641     int wc = uiConfig.readEntry("Main Windows Count", 1);
0642 
0643     /* It is expected the main windows are restored before
0644        restoring areas.  */
0645     if (wc > mainWindows().size())
0646         wc = mainWindows().size();
0647 
0648     /* Offer all tool views to the default areas.  */
0649     for (Sublime::Area* area : defaultAreas()) {
0650         QHash<IToolViewFactory*, Sublime::ToolDocument*>::const_iterator i, e;
0651         for (i = d->factoryDocuments.constBegin(),
0652                  e = d->factoryDocuments.constEnd(); i != e; ++i)
0653         {
0654             addToolViewIfWanted(i.key(), i.value(), area);
0655         }
0656     }
0657 
0658     /* Restore per-windows areas.  */
0659     for (int w = 0; w < wc; ++w)
0660     {
0661         KConfigGroup mainWindowConfig(&uiConfig,
0662                                       QStringLiteral("Main Window %1").arg(w));
0663 
0664         Sublime::MainWindow *mw = mainWindows()[w];
0665 
0666         /* We loop over default areas.  This means that if
0667            the config file has an area of some type that is not
0668            in default set, we'd just ignore it.  I think it's fine --
0669            the model were a given mainwindow can has it's own
0670            area types not represented in the default set is way
0671            too complex.  */
0672         for (Sublime::Area* defaultArea : defaultAreas()) {
0673             QString type = defaultArea->objectName();
0674             Sublime::Area* area = this->area(w, type);
0675 
0676             KConfigGroup areaConfig(&mainWindowConfig, QLatin1String("Area ") + type);
0677 
0678             qCDebug(SHELL) << "Trying to restore area " << type;
0679 
0680             /* This is just an easy check that a group exists, to
0681                avoid "restoring" area from empty config group, wiping
0682                away programmatically installed defaults.  */
0683             if (areaConfig.readEntry("id", "") == type)
0684             {
0685                 qCDebug(SHELL) << "Restoring area " << type;
0686                 loadArea(area, areaConfig);
0687             }
0688 
0689             // At this point we know which tool views the area wants.
0690             // Tender all tool views we have.
0691             QHash<IToolViewFactory*, Sublime::ToolDocument*>::const_iterator i, e;
0692             for (i = d->factoryDocuments.constBegin(),
0693                      e = d->factoryDocuments.constEnd(); i != e; ++i)
0694             {
0695                 addToolViewIfWanted(i.key(), i.value(), area);
0696             }
0697         }
0698 
0699         // Force reload of the changes.
0700         showAreaInternal(mw->area(), mw);
0701         mw->area()->setWorkingSet(Core::self()->activeSession()->config()->group("Working File Sets").readEntry("Active Working Set", QString()));
0702 
0703         mw->enableAreaSettingsSave();
0704     }
0705 
0706     d->areasRestored = true;
0707 }
0708 
0709 void UiController::addToolViewToDockArea(IToolViewFactory* factory, Qt::DockWidgetArea area)
0710 {
0711     Q_D(UiController);
0712 
0713     addToolViewToArea(factory, d->factoryDocuments.value(factory), activeArea(), Sublime::dockAreaToPosition(area));
0714 }
0715 
0716 bool UiController::toolViewPresent(Sublime::ToolDocument* doc, Sublime::Area* area)
0717 {
0718     for (Sublime::View *view : doc->views()) {
0719         if( area->toolViews().contains( view ) )
0720             return true;
0721     }
0722     return false;
0723 }
0724 
0725 void UiController::addToolViewIfWanted(IToolViewFactory* factory,
0726                            Sublime::ToolDocument* doc,
0727                            Sublime::Area* area)
0728 {
0729     if (area->wantToolView(factory->id()))
0730     {
0731         addToolViewToArea(factory, doc, area);
0732     }
0733 }
0734 
0735 Sublime::View* UiController::addToolViewToArea(IToolViewFactory* factory,
0736                                      Sublime::ToolDocument* doc,
0737                                      Sublime::Area* area, Sublime::Position p)
0738 {
0739     Sublime::View* view = doc->createView();
0740     area->addToolView(
0741         view,
0742         p == Sublime::AllPositions ? Sublime::dockAreaToPosition(factory->defaultPosition()) : p);
0743 
0744     connect(view, &Sublime::View::raise,
0745             this, QOverload<Sublime::View*>::of(&UiController::raiseToolView));
0746 
0747     factory->viewCreated(view);
0748     return view;
0749 }
0750 
0751 void UiController::registerStatus(QObject* status)
0752 {
0753     Sublime::MainWindow* w = activeSublimeWindow();
0754     if (!w) return;
0755     auto* mw = qobject_cast<KDevelop::MainWindow*>(w);
0756     if (!mw) return;
0757     mw->registerStatus(status);
0758 }
0759 
0760 void UiController::showErrorMessage(const QString& message, int timeout)
0761 {
0762     Sublime::MainWindow* w = activeSublimeWindow();
0763     if (!w) return;
0764     auto* mw = qobject_cast<KDevelop::MainWindow*>(w);
0765     if (!mw) return;
0766     QMetaObject::invokeMethod(mw, "showErrorMessage", Q_ARG(QString, message), Q_ARG(int, timeout));
0767 }
0768 
0769 void UiController::postMessage(Sublime::Message* message)
0770 {
0771     // if Core has flag Core::NoUi there also is no window, so caught as well here
0772     Sublime::MainWindow* window = activeSublimeWindow();
0773     if (!window) {
0774         delete message;
0775         return;
0776     }
0777     QMetaObject::invokeMethod(window, "postMessage", Q_ARG(Sublime::Message*, message));
0778 }
0779 
0780 const QHash< IToolViewFactory*, Sublime::ToolDocument* >& UiController::factoryDocuments() const
0781 {
0782     Q_D(const UiController);
0783 
0784     return d->factoryDocuments;
0785 }
0786 
0787 QWidget* UiController::activeToolViewActionListener() const
0788 {
0789     Q_D(const UiController);
0790 
0791     return d->activeActionListener;
0792 }
0793 
0794 QList<Sublime::Area*> UiController::allAreas() const
0795 {
0796     return Sublime::Controller::allAreas();
0797 }
0798 
0799 }
0800 
0801 #include "uicontroller.moc"
0802 #include "moc_uicontroller.cpp"