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"