File indexing completed on 2024-05-19 12:22:34
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 "controller.h" 0008 0009 #include <QMap> 0010 #include <QList> 0011 #include <QEvent> 0012 #include <QMouseEvent> 0013 #include <QCoreApplication> 0014 0015 #include <KSharedConfig> 0016 0017 #include "area.h" 0018 #include "view.h" 0019 #include "document.h" 0020 #include "mainwindow.h" 0021 #include <debug.h> 0022 0023 namespace Sublime { 0024 0025 struct WidgetFinder { 0026 explicit WidgetFinder(QWidget *_w) :w(_w), view(nullptr) {} 0027 Area::WalkerMode operator()(AreaIndex *index) 0028 { 0029 for (View* v : qAsConst(index->views())) { 0030 if (v->hasWidget() && (v->widget() == w)) 0031 { 0032 view = v; 0033 return Area::StopWalker; 0034 } 0035 } 0036 return Area::ContinueWalker; 0037 } 0038 0039 QWidget* const w; 0040 View *view; 0041 }; 0042 0043 struct ToolWidgetFinder { 0044 explicit ToolWidgetFinder(QWidget *_w) :w(_w), view(nullptr) {} 0045 Area::WalkerMode operator()(View *v, Sublime::Position /*position*/) 0046 { 0047 if (v->hasWidget() && (v->widget() == w)) 0048 { 0049 view = v; 0050 return Area::StopWalker; 0051 } 0052 return Area::ContinueWalker; 0053 } 0054 0055 QWidget* const w; 0056 View *view; 0057 }; 0058 0059 0060 // class ControllerPrivate 0061 0062 class ControllerPrivate 0063 { 0064 public: 0065 ControllerPrivate() 0066 { 0067 } 0068 0069 QList<Document*> documents; 0070 QList<Area*> areas; 0071 QList<Area*> allAreas; 0072 QMap<QString, Area*> namedAreas; 0073 // FIXME: remove this. 0074 QMap<Area*, MainWindow*> shownAreas; 0075 QList<MainWindow*> controlledWindows; 0076 QVector< QList<Area*> > mainWindowAreas; 0077 bool openAfterCurrent; 0078 bool arrangeBuddies; 0079 }; 0080 0081 0082 0083 // class Controller 0084 0085 Controller::Controller(QObject *parent) 0086 : QObject(parent) 0087 , MainWindowOperator() 0088 , d_ptr(new ControllerPrivate()) 0089 { 0090 init(); 0091 } 0092 0093 void Controller::init() 0094 { 0095 loadSettings(); 0096 QCoreApplication::instance()->installEventFilter(this); 0097 } 0098 0099 Controller::~Controller() 0100 { 0101 Q_D(Controller); 0102 0103 qDeleteAll(d->controlledWindows); 0104 } 0105 0106 void Controller::showArea(Area *area, MainWindow *mainWindow) 0107 { 0108 Q_D(Controller); 0109 0110 Area *areaToShow = nullptr; 0111 const auto windowIt = d->shownAreas.find(area); 0112 //if the area is already shown in another mainwindow then we need to clone it 0113 if (windowIt != d->shownAreas.end() && (mainWindow != *windowIt)) 0114 areaToShow = new Area(*area); 0115 else 0116 areaToShow = area; 0117 d->shownAreas[areaToShow] = mainWindow; 0118 0119 showAreaInternal(areaToShow, mainWindow); 0120 } 0121 0122 void Controller::showAreaInternal(Area* area, MainWindow *mainWindow) 0123 { 0124 /* Disconnect the previous area. We really don't want to mess with 0125 main window if an area not visible now is modified. Further, 0126 if showAreaInternal is called with the same area as is current 0127 now, we don't want to connect the same signals twice. */ 0128 MainWindowOperator::setArea(mainWindow, area); 0129 } 0130 0131 0132 void Controller::removeArea(Area *obj) 0133 { 0134 Q_D(Controller); 0135 0136 d->areas.removeAll(obj); 0137 } 0138 0139 void Controller::removeDocument(Document *obj) 0140 { 0141 Q_D(Controller); 0142 0143 d->documents.removeAll(obj); 0144 } 0145 0146 void Controller::showArea(const QString& areaTypeId, MainWindow *mainWindow) 0147 { 0148 Q_D(Controller); 0149 0150 int index = d->controlledWindows.indexOf(mainWindow); 0151 Q_ASSERT(index != -1); 0152 0153 Area* area = nullptr; 0154 for (Area* a : qAsConst(d->mainWindowAreas[index])) { 0155 qCDebug(SUBLIME) << "Object name: " << a->objectName() << " id " 0156 << areaTypeId; 0157 if (a->objectName() == areaTypeId) 0158 { 0159 area = a; 0160 break; 0161 } 0162 } 0163 Q_ASSERT (area); 0164 0165 showAreaInternal(area, mainWindow); 0166 } 0167 0168 void Controller::resetCurrentArea(MainWindow *mainWindow) 0169 { 0170 Q_D(Controller); 0171 0172 QString id = mainWindow->area()->objectName(); 0173 0174 int areaIndex = 0; 0175 Area* def = nullptr; 0176 for (Area* a : qAsConst(d->areas)) { 0177 if (a->objectName() == id) 0178 { 0179 def = a; 0180 break; 0181 } 0182 ++areaIndex; 0183 } 0184 Q_ASSERT(def); 0185 0186 int index = d->controlledWindows.indexOf(mainWindow); 0187 Q_ASSERT(index != -1); 0188 0189 Area* prev = d->mainWindowAreas[index][areaIndex]; 0190 d->mainWindowAreas[index][areaIndex] = new Area(*def); 0191 showAreaInternal(d->mainWindowAreas[index][areaIndex], mainWindow); 0192 delete prev; 0193 } 0194 0195 const QList<Area*> &Controller::defaultAreas() const 0196 { 0197 Q_D(const Controller); 0198 0199 return d->areas; 0200 } 0201 0202 0203 const QList< Area* >& Controller::areas(MainWindow* mainWindow) const 0204 { 0205 Q_D(const Controller); 0206 0207 int index = d->controlledWindows.indexOf(mainWindow); 0208 Q_ASSERT(index != -1); 0209 return areas(index); 0210 } 0211 0212 const QList<Area*> &Controller::areas(int mainWindow) const 0213 { 0214 Q_D(const Controller); 0215 0216 return d->mainWindowAreas[mainWindow]; 0217 } 0218 0219 const QList<Area*> &Controller::allAreas() const 0220 { 0221 Q_D(const Controller); 0222 0223 return d->allAreas; 0224 } 0225 0226 const QList<Document*> &Controller::documents() const 0227 { 0228 Q_D(const Controller); 0229 0230 return d->documents; 0231 } 0232 0233 void Controller::addDefaultArea(Area *area) 0234 { 0235 Q_D(Controller); 0236 0237 d->areas.append(area); 0238 d->allAreas.append(area); 0239 d->namedAreas[area->objectName()] = area; 0240 emit areaCreated(area); 0241 } 0242 0243 void Controller::addMainWindow(MainWindow* mainWindow) 0244 { 0245 Q_D(Controller); 0246 0247 Q_ASSERT(mainWindow); 0248 0249 Q_ASSERT (!d->controlledWindows.contains(mainWindow)); 0250 d->controlledWindows << mainWindow; 0251 d->mainWindowAreas.resize(d->controlledWindows.size()); 0252 int index = d->controlledWindows.size()-1; 0253 0254 auto& mainWindowAreas = d->mainWindowAreas[index]; 0255 const auto& defaultAreas = this->defaultAreas(); 0256 d->allAreas.reserve(d->allAreas.size() + defaultAreas.size()); 0257 mainWindowAreas.reserve(defaultAreas.size()); 0258 0259 for (const auto* area : defaultAreas) { 0260 Area *na = new Area(*area); 0261 d->allAreas.append(na); 0262 mainWindowAreas.append(na); 0263 emit areaCreated(na); 0264 } 0265 showAreaInternal(d->mainWindowAreas[index][0], mainWindow); 0266 emit mainWindowAdded( mainWindow ); 0267 } 0268 0269 void Controller::addDocument(Document *document) 0270 { 0271 Q_D(Controller); 0272 0273 d->documents.append(document); 0274 } 0275 0276 void Controller::areaReleased() 0277 { 0278 Q_D(Controller); 0279 0280 auto *w = reinterpret_cast<Sublime::MainWindow*>(sender()); 0281 qCDebug(SUBLIME) << "marking areas as mainwindow-free" << w << d->controlledWindows.contains(w) << d->shownAreas.keys(w); 0282 const auto areas = d->shownAreas.keys(w); 0283 for (Area* area : areas) { 0284 qCDebug(SUBLIME) << "" << area->objectName(); 0285 areaReleased(area); 0286 disconnect(area, nullptr, w, nullptr); 0287 } 0288 0289 d->controlledWindows.removeAll(w); 0290 } 0291 0292 void Controller::areaReleased(Sublime::Area *area) 0293 { 0294 Q_D(Controller); 0295 0296 d->shownAreas.remove(area); 0297 d->namedAreas.remove(area->objectName()); 0298 } 0299 0300 Area *Controller::defaultArea(const QString &id) const 0301 { 0302 Q_D(const Controller); 0303 0304 return d->namedAreas[id]; 0305 } 0306 0307 Area *Controller::area(int mainWindow, const QString& id) const 0308 { 0309 for (Area* area : areas(mainWindow)) { 0310 if (area->objectName() == id) 0311 return area; 0312 } 0313 return nullptr; 0314 } 0315 0316 Area* Controller::areaForView(View* view) const 0317 { 0318 for (Area* area : allAreas()) { 0319 if(area->views().contains(view)) 0320 return area; 0321 } 0322 0323 return nullptr; 0324 } 0325 0326 /*We need this to catch activation of views and tool views 0327 so that we can always tell what view and tool view is active. 0328 "Active" doesn't mean focused. It means that it is focused now 0329 or was focused before and no other view/tool view wasn't focused 0330 after that."*/ 0331 //implementation is based upon KParts::PartManager::eventFilter 0332 bool Controller::eventFilter(QObject *obj, QEvent *ev) 0333 { 0334 Q_D(Controller); 0335 0336 if (ev->type() != QEvent::MouseButtonPress && 0337 ev->type() != QEvent::MouseButtonDblClick && 0338 ev->type() != QEvent::FocusIn) 0339 return false; 0340 0341 //not a widget? - return 0342 if (!obj->isWidgetType()) 0343 return false; 0344 0345 //is dialog or popup? - return 0346 auto *w = static_cast<QWidget*>(obj); 0347 if (((w->windowFlags().testFlag(Qt::Dialog)) && w->isModal()) || 0348 (w->windowFlags().testFlag(Qt::Popup)) || (w->windowFlags().testFlag(Qt::Tool))) 0349 return false; 0350 0351 //not a mouse button that should activate the widget? - return 0352 if (ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick) 0353 { 0354 auto* mev = static_cast<QMouseEvent*>(ev); 0355 int activationButtonMask = Qt::LeftButton | Qt::MiddleButton | Qt::RightButton; 0356 if ((mev->button() & activationButtonMask) == 0) 0357 return false; 0358 } 0359 0360 while (w) 0361 { 0362 //not inside sublime mainwindow 0363 auto *mw = qobject_cast<Sublime::MainWindow*>(w->topLevelWidget()); 0364 if (!mw || !d->controlledWindows.contains(mw)) 0365 return false; 0366 0367 Area *area = mw->area(); 0368 0369 ///@todo adymo: this is extra slow - optimize 0370 //find this widget in views 0371 WidgetFinder widgetFinder(w); 0372 area->walkViews(widgetFinder, area->rootIndex()); 0373 if (widgetFinder.view && widgetFinder.view != mw->activeView()) 0374 { 0375 setActiveView(mw, widgetFinder.view); 0376 ///@todo adymo: shall we filter out the event? 0377 return false; 0378 } 0379 0380 //find this widget in tool views 0381 ToolWidgetFinder toolFinder(w); 0382 area->walkToolViews(toolFinder, Sublime::AllPositions); 0383 if (toolFinder.view && toolFinder.view != mw->activeToolView()) 0384 { 0385 setActiveToolView(mw, toolFinder.view); 0386 ///@todo adymo: shall we filter out the event? 0387 return false; 0388 } 0389 0390 w = w->parentWidget(); 0391 } 0392 0393 return false; 0394 } 0395 0396 const QList< MainWindow * > & Controller::mainWindows() const 0397 { 0398 Q_D(const Controller); 0399 0400 return d->controlledWindows; 0401 } 0402 0403 0404 void Controller::notifyToolViewRemoved(Sublime::View *view, Sublime::Position) 0405 { 0406 emit aboutToRemoveToolView(view); 0407 } 0408 0409 void Controller::notifyToolViewAdded(Sublime::View *view, Sublime::Position) 0410 { 0411 emit toolViewAdded(view); 0412 } 0413 0414 void Controller::notifyViewRemoved(Sublime::AreaIndex*, Sublime::View *view) 0415 { 0416 emit aboutToRemoveView(view); 0417 } 0418 0419 void Controller::notifyViewAdded(Sublime::AreaIndex*, Sublime::View *view) 0420 { 0421 emit viewAdded(view); 0422 } 0423 0424 void Controller::loadSettings() 0425 { 0426 Q_D(Controller); 0427 0428 KConfigGroup uiGroup = KSharedConfig::openConfig()->group("UiSettings"); 0429 d->openAfterCurrent = (uiGroup.readEntry("TabBarOpenAfterCurrent", 1) == 1); 0430 d->arrangeBuddies = (uiGroup.readEntry("TabBarArrangeBuddies", 1) == 1); 0431 } 0432 0433 bool Controller::openAfterCurrent() const 0434 { 0435 Q_D(const Controller); 0436 0437 return d->openAfterCurrent; 0438 } 0439 0440 bool Controller::arrangeBuddies() const 0441 { 0442 Q_D(const Controller); 0443 0444 return d->arrangeBuddies; 0445 } 0446 0447 } 0448 0449 #include "moc_controller.cpp"