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

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"