File indexing completed on 2024-05-12 04:38:23

0001 /*
0002     SPDX-FileCopyrightText: David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "workingset.h"
0008 #include "debug_workingset.h"
0009 
0010 #include <sublime/area.h>
0011 #include <sublime/document.h>
0012 #include <sublime/mainwindow.h>
0013 #include <sublime/view.h>
0014 
0015 #include <KProtocolInfo>
0016 #include <KTextEditor/View>
0017 
0018 #include <textdocument.h>
0019 #include <core.h>
0020 #include <interfaces/isession.h>
0021 #include <uicontroller.h>
0022 #include <util/pushvalue.h>
0023 #include <documentcontroller.h>
0024 
0025 #include <QFileInfo>
0026 #include <QPainter>
0027 #include <QSplitter>
0028 
0029 #define SYNC_OFTEN
0030 
0031 using namespace KDevelop;
0032 
0033 bool WorkingSet::m_loading = false;
0034 
0035 namespace {
0036 
0037 QIcon generateIcon(const WorkingSetIconParameters& params)
0038 {
0039     QImage pixmap(16, 16, QImage::Format_ARGB32);
0040     // fill the background with a transparent color
0041     pixmap.fill(QColor::fromRgba(qRgba(0, 0, 0, 0)));
0042     const uint coloredCount = params.coloredCount;
0043     // coordinates of the rectangles to draw, for 16x16 icons specifically
0044     QList<QRect> rects{
0045         {1, 1, 5, 5},
0046         {1, 9, 5, 5},
0047         {9, 1, 5, 5},
0048         {9, 9, 5, 5},
0049     };
0050     if (params.swapDiagonal) {
0051         rects.swapItemsAt(1, 2);
0052     }
0053 
0054     QPainter painter(&pixmap);
0055     // color for non-colored squares, paint them brighter if the working set is the active one
0056     const int inact = 40;
0057     QColor darkColor = QColor::fromRgb(inact, inact, inact);
0058     // color for colored squares
0059     // this code is not fragile, you can just tune the magic formulas at random and see what looks good.
0060     // just make sure to keep it within the 0-360 / 0-255 / 0-255 space of the HSV model
0061     QColor brightColor = QColor::fromHsv(params.hue, qMin<uint>(255, 215 + (params.setId*5) % 150),
0062                                          205 + (params.setId*11) % 50);
0063     // Y'UV "Y" value, the approximate "lightness" of the color
0064     // If it is above 0.6, then making the color darker a bit is okay,
0065     // if it is below 0.35, then the color should be a bit brighter.
0066     float brightY = 0.299 * brightColor.redF() + 0.587 * brightColor.greenF() + 0.114 * brightColor.blueF();
0067     if ( brightY > 0.6 ) {
0068         if ( params.setId % 7 < 2 ) {
0069             // 2/7 chance to make the color significantly darker
0070             brightColor = brightColor.darker(120 + (params.setId*7) % 35);
0071         }
0072         else if ( params.setId % 5 == 0 ) {
0073             // 1/5 chance to make it a bit darker
0074             brightColor = brightColor.darker(110 + (params.setId*3) % 10);
0075         }
0076     }
0077     if ( brightY < 0.35 ) {
0078         // always make the color brighter to avoid very dark colors (like rgb(0, 0, 255))
0079         brightColor = brightColor.lighter(120 + (params.setId*13) % 55);
0080     }
0081     int at = 0;
0082     for (const QRect& rect : qAsConst(rects)) {
0083         QColor currentColor;
0084         // pick the colored squares; you can get different patterns by re-ordering the "rects" list
0085         if ( (at + params.setId*7) % 4 < coloredCount ) {
0086             currentColor = brightColor;
0087         }
0088         else {
0089             currentColor = darkColor;
0090         }
0091         // draw the filling of the square
0092         painter.setPen(QColor(currentColor));
0093         painter.setBrush(QBrush(currentColor));
0094         painter.drawRect(rect);
0095         // draw a slight set-in shadow for the square -- it's barely recognizeable,
0096         // but it looks way better than without
0097         painter.setBrush(Qt::NoBrush);
0098         painter.setPen(QColor(0, 0, 0, 50));
0099         painter.drawRect(rect);
0100         painter.setPen(QColor(0, 0, 0, 25));
0101         painter.drawRect(rect.x() + 1, rect.y() + 1, rect.width() - 2, rect.height() - 2);
0102         at += 1;
0103     }
0104     return QIcon(QPixmap::fromImage(pixmap));
0105 }
0106 
0107 QSplitter* loadToAreaPrivate(Sublime::Area *area, Sublime::AreaIndex *areaIndex, const KConfigGroup &setGroup, QMultiMap<QString, Sublime::View*> &recycle)
0108 {
0109     Q_ASSERT(!areaIndex->isSplit());
0110 
0111     QSplitter *parentSplitter = nullptr;
0112 
0113     if (setGroup.hasKey("Orientation")) {
0114         QStringList subgroups = setGroup.groupList();
0115         if (!subgroups.contains(QStringLiteral("0"))) {
0116             if (subgroups.contains(QStringLiteral("1"))) {
0117                 parentSplitter = loadToAreaPrivate(area, areaIndex, KConfigGroup(&setGroup, "1"), recycle);
0118             }
0119         } else if (!subgroups.contains(QStringLiteral("1"))) {
0120             parentSplitter = loadToAreaPrivate(area, areaIndex, KConfigGroup(&setGroup, "0"), recycle);
0121         } else {
0122             areaIndex->split(setGroup.readEntry("Orientation", "Horizontal") == QLatin1String("Vertical") ? Qt::Vertical : Qt::Horizontal);
0123 
0124             parentSplitter = loadToAreaPrivate(area, areaIndex->first(), KConfigGroup(&setGroup, "0"), recycle);
0125             if (!parentSplitter) {
0126                 areaIndex->unsplit(areaIndex->first());
0127                 parentSplitter = loadToAreaPrivate(area, areaIndex, KConfigGroup(&setGroup, "1"), recycle);
0128             } else if (auto *splitter = loadToAreaPrivate(area, areaIndex->second(), KConfigGroup(&setGroup, "1"), recycle)) {
0129                 splitter->setSizes(setGroup.readEntry("Sizes", QList<int>({1, 1})));
0130                 parentSplitter = qobject_cast<QSplitter*>(splitter->parent());
0131             } else {
0132                 areaIndex->unsplit(areaIndex->second());
0133                 emit area->viewAdded(areaIndex, nullptr);
0134             }
0135         }
0136     } else {
0137         //Load all documents from the workingset into this areaIndex
0138         int viewCount = setGroup.readEntry("View Count", 0);
0139         QVector<Sublime::View*> createdViews(viewCount, nullptr);
0140         for (int i = 0; i < viewCount; ++i) {
0141             QString specifier = setGroup.readEntry(QStringLiteral("View %1").arg(i), QString());
0142             if (specifier.isEmpty()) {
0143                 continue;
0144             }
0145 
0146             Sublime::View* previousView = areaIndex->views().empty() ? nullptr : areaIndex->views().back();
0147 
0148             QMultiMap<QString, Sublime::View*>::iterator it = recycle.find(specifier);
0149             if (it != recycle.end()) {
0150                 area->addView(*it, areaIndex, previousView);
0151                 createdViews[i] = *it;
0152                 recycle.erase(it);
0153                 continue;
0154             }
0155             auto url = QUrl::fromUserInput(specifier);
0156             if (url.isLocalFile() && !QFileInfo::exists(url.path())) {
0157                 qCWarning(WORKINGSET) << "Unable to find file" << specifier;
0158                 continue;
0159             }
0160             IDocument* doc = Core::self()->documentControllerInternal()->openDocument(url,
0161                              KTextEditor::Cursor::invalid(), IDocumentController::DoNotActivate | IDocumentController::DoNotCreateView);
0162             if (auto document = dynamic_cast<Sublime::Document*>(doc)) {
0163                 Sublime::View *view = document->createView();
0164                 area->addView(view, areaIndex, previousView);
0165                 createdViews[i] = view;
0166             } else {
0167                 qCWarning(WORKINGSET) << "Unable to create view" << specifier;
0168             }
0169         }
0170 
0171         //Load state
0172         int activeIndex = setGroup.readEntry(QStringLiteral("Active View"), -1);
0173         for (int i = 0; i < viewCount; ++i) {
0174             if (!createdViews[i]) {
0175                 continue;
0176             }
0177             KConfigGroup viewGroup(&setGroup, QStringLiteral("View %1 Config").arg(i));
0178             if (viewGroup.exists()) {
0179                 createdViews[i]->readSessionConfig(viewGroup);
0180                 if (auto textView = qobject_cast<TextView*>(createdViews[i])) {
0181                     if (auto kateView = textView->textView()) {
0182                         auto cursors = viewGroup.readEntry("Selection", QList<int>());
0183                         if (cursors.size() == 4) {
0184                             kateView->setSelection(KTextEditor::Range(cursors.at(0), cursors.at(1), cursors.at(2), cursors.at(3)));
0185                         }
0186                     }
0187                 }
0188             }
0189             if (i == activeIndex) {
0190                 area->setActiveView(createdViews[i]);
0191             }
0192             if (!parentSplitter) {
0193                 auto p = createdViews[i]->widget()->parentWidget();
0194                 while (p && !(parentSplitter = qobject_cast<QSplitter*>(p))) {
0195                     p = p->parentWidget();
0196                 }
0197                 if (parentSplitter) {
0198                     parentSplitter = qobject_cast<QSplitter*>(parentSplitter->parent());
0199                 }
0200             }
0201         }
0202     }
0203 
0204     return parentSplitter;
0205 }
0206 
0207 QSplitter* saveFromAreaPrivate(Sublime::AreaIndex *area, KConfigGroup setGroup, const Sublime::View *activeView)
0208 {
0209     QSplitter *parentSplitter = nullptr;
0210 
0211     if (area->isSplit()) {
0212         if (!area->first()) {
0213             parentSplitter = saveFromAreaPrivate(area->second(), setGroup, activeView);
0214         } else if (!area->second()) {
0215             parentSplitter = saveFromAreaPrivate(area->first(), setGroup, activeView);
0216         } else {
0217             parentSplitter = saveFromAreaPrivate(area->first(), KConfigGroup(&setGroup, "0"), activeView);
0218             if (!parentSplitter) {
0219                 parentSplitter = saveFromAreaPrivate(area->second(), setGroup, activeView);
0220             } else if (saveFromAreaPrivate(area->second(), KConfigGroup(&setGroup, "1"), activeView)) {
0221                 setGroup.writeEntry("Orientation", area->orientation() == Qt::Horizontal ? "Horizontal" : "Vertical");
0222                 setGroup.writeEntry("Sizes", parentSplitter->sizes());
0223             } else {
0224                 // move up settings of group "0"
0225                 KConfigGroup(&setGroup, "0").copyTo(&setGroup);
0226                 setGroup.deleteGroup("0");
0227             }
0228         }
0229     } else {
0230         int index = 0;
0231         int activeIndex = -1;
0232         const auto views = area->views();
0233         for (Sublime::View *view : views) {
0234             //The working set config gets an updated list of files
0235             QString docSpec = view->document()->documentSpecifier();
0236 
0237             if (view == activeView) {
0238                 activeIndex = index;
0239             }
0240 
0241             //only save the documents from protocols KIO understands
0242             //otherwise we try to load kdev:// too early
0243             if (!KProtocolInfo::isKnownProtocol(QUrl(docSpec))) {
0244                 continue;
0245             }
0246 
0247             setGroup.writeEntry(QStringLiteral("View %1").arg(index), docSpec);
0248             KConfigGroup viewGroup(&setGroup, QStringLiteral("View %1 Config").arg(index));
0249             view->writeSessionConfig(viewGroup);
0250             if (auto textView = qobject_cast<TextView*>(view)) {
0251                 if (auto kateView = textView->textView()) {
0252                     auto range = kateView->selectionRange();
0253                     if (range.isValid()) {
0254                         viewGroup.writeEntry("Selection", QList<int>({range.start().line(), range.start().column(),
0255                                                                       range.end().line(), range.end().column()}));
0256                     }
0257                 }
0258             }
0259             ++index;
0260 
0261             if (!parentSplitter && view->hasWidget()) {
0262                 auto p = view->widget()->parentWidget();
0263                 while (p && !(parentSplitter = qobject_cast<QSplitter*>(p))) {
0264                     p = p->parentWidget();
0265                 }
0266             }
0267         }
0268         if (index > 0) {
0269             setGroup.writeEntry("View Count", index);
0270             if (activeIndex >= 0) {
0271                 setGroup.writeEntry(QStringLiteral("Active View"), std::min(activeIndex, index - 1));
0272             }
0273         }
0274     }
0275 
0276     return parentSplitter ? qobject_cast<QSplitter*>(parentSplitter->parent()) : nullptr;
0277 }
0278 
0279 
0280 }
0281 
0282 WorkingSet::WorkingSet(const QString& id)
0283     : QObject()
0284     , m_id(id)
0285     , m_icon(generateIcon(WorkingSetIconParameters(id)))
0286 {
0287 }
0288 
0289 bool WorkingSet::isEmpty() const
0290 {
0291     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
0292     KConfigGroup group = setConfig.group(m_id);
0293     return !group.hasKey("Orientation") && group.readEntry("View Count", 0) == 0;
0294 }
0295 
0296 struct DisableMainWindowUpdatesFromArea
0297 {
0298     explicit DisableMainWindowUpdatesFromArea(Sublime::Area* area) : m_area(area) {
0299         if(area) {
0300             const auto windows = Core::self()->uiControllerInternal()->mainWindows();
0301             for (Sublime::MainWindow* window : windows) {
0302                 if(window->area() == area) {
0303                     if(window->updatesEnabled()) {
0304                         wasUpdatesEnabled.insert(window);
0305                         window->setUpdatesEnabled(false);
0306                     }
0307                 }
0308             }
0309         }
0310     }
0311 
0312     ~DisableMainWindowUpdatesFromArea() {
0313         if(m_area) {
0314             for (Sublime::MainWindow* window : qAsConst(wasUpdatesEnabled)) {
0315                 window->setUpdatesEnabled(wasUpdatesEnabled.contains(window));
0316             }
0317         }
0318     }
0319 
0320 private:
0321     Q_DISABLE_COPY(DisableMainWindowUpdatesFromArea)
0322 
0323     Sublime::Area* m_area;
0324     QSet<Sublime::MainWindow*> wasUpdatesEnabled;
0325 };
0326 
0327 void loadFileList(QStringList& ret, const KConfigGroup& group)
0328 {
0329     if (group.hasKey("Orientation")) {
0330         QStringList subgroups = group.groupList();
0331 
0332         if (subgroups.contains(QStringLiteral("0"))) {
0333 
0334             {
0335                 KConfigGroup subgroup(&group, "0");
0336                 loadFileList(ret, subgroup);
0337             }
0338 
0339             if (subgroups.contains(QStringLiteral("1"))) {
0340                 KConfigGroup subgroup(&group, "1");
0341                 loadFileList(ret, subgroup);
0342             }
0343         }
0344 
0345     } else {
0346         int viewCount = group.readEntry("View Count", 0);
0347         ret.reserve(ret.size() + viewCount);
0348         for (int i = 0; i < viewCount; ++i) {
0349             QString specifier = group.readEntry(QStringLiteral("View %1").arg(i), QString());
0350             auto url = QUrl::fromUserInput(specifier);
0351             if (url.isLocalFile() && !QFileInfo::exists(url.path())) {
0352                 continue;
0353             }
0354             ret << specifier;
0355         }
0356     }
0357 }
0358 
0359 QStringList WorkingSet::fileList() const
0360 {
0361     QStringList ret;
0362     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
0363     KConfigGroup group = setConfig.group(m_id);
0364 
0365     loadFileList(ret, group);
0366     return ret;
0367 }
0368 
0369 QSet<QString> WorkingSet::fileSet() const
0370 {
0371     const QStringList fileList = this->fileList();
0372     return QSet<QString>(fileList.begin(), fileList.end());
0373 }
0374 
0375 void WorkingSet::loadToArea(Sublime::Area* area) {
0376     PushValue<bool> enableLoading(m_loading, true);
0377 
0378     /// We cannot disable the updates here, because (probably) due to a bug in Qt,
0379     /// which causes the updates to stay disabled forever after some complex operations
0380     /// on the sub-views. This could be reproduced by creating two working-sets with complex
0381     /// split-view configurations and switching between them. Re-enabling the updates doesn't help.
0382 //     DisableMainWindowUpdatesFromArea updatesDisabler(area);
0383 
0384     qCDebug(WORKINGSET) << "loading working-set" << m_id << "into area" << area;
0385 
0386     QMultiMap<QString, Sublime::View*> recycle;
0387 
0388     const auto viewsBefore = area->views();
0389     for (Sublime::View* view : viewsBefore) {
0390         recycle.insert( view->document()->documentSpecifier(), area->removeView(view) );
0391     }
0392 
0393     qCDebug(WORKINGSET) << "recycling up to" << recycle.size() << "old views";
0394 
0395     Q_ASSERT( area->views().empty() );
0396 
0397     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
0398     KConfigGroup setGroup = setConfig.group(m_id);
0399 
0400     // Migrate from former by-area configs to a shared config
0401     KConfigGroup areaGroup = setConfig.group(m_id + QLatin1Char('|') + area->title());
0402     if (areaGroup.exists()) {
0403         if (setGroup.readEntry("Active View", QString()).isEmpty()) {
0404             setGroup.writeEntry("Active View", areaGroup.readEntry("Active View", QString()));
0405         }
0406         int viewCount = setGroup.readEntry("View Count", 0);
0407         QMultiMap<QString, KConfigGroup> oldViewConfigs;
0408         for (int i = 0; i < viewCount; ++i) {
0409             KConfigGroup viewGroup(&setGroup, QStringLiteral("View %1 Config").arg(i));
0410             auto specifier = setGroup.readEntry(QStringLiteral("View %1").arg(i), QString());
0411             if (viewGroup.exists() || specifier.isEmpty()) {
0412                 continue;
0413             }
0414             if (oldViewConfigs.empty()) { // cache all view configs from the old area config
0415                 int oldViewCount = areaGroup.readEntry("View Count", 0);
0416                 for (int j = 0; j < oldViewCount; ++j) {
0417                     auto oldSpecifier = areaGroup.readEntry(QStringLiteral("View %1").arg(j), QString());
0418                     if (!oldSpecifier.isEmpty()) {
0419                         oldViewConfigs.insert(oldSpecifier, KConfigGroup(&areaGroup, QStringLiteral("View %1 Config").arg(j)));
0420                     }
0421                 }
0422             }
0423             auto it = oldViewConfigs.find(specifier);
0424             if (it != oldViewConfigs.end()) {
0425                 viewGroup.copyTo(&(*it));
0426                 oldViewConfigs.erase(it);
0427             }
0428             if (oldViewConfigs.empty()) {
0429                 break;
0430             }
0431         }
0432         setConfig.deleteGroup(areaGroup.name());
0433     }
0434 
0435     loadToAreaPrivate(area, area->rootIndex(), setGroup, recycle);
0436 
0437     // Delete views which were not recycled
0438     qCDebug(WORKINGSET) << "deleting" << recycle.size() << "old views";
0439     qDeleteAll(recycle);
0440 
0441     if (area->views().isEmpty()) {
0442         return;
0443     }
0444 
0445     if (!area->activeView()) {
0446         area->setActiveView(area->views().at(0));
0447     }
0448 
0449     const auto windows = Core::self()->uiControllerInternal()->mainWindows();
0450     for (Sublime::MainWindow* window : windows) {
0451         if (window->area() == area) {
0452             window->activateView(area->activeView());
0453         }
0454     }
0455 }
0456 
0457 void WorkingSet::deleteSet(bool force, bool silent)
0458 {
0459     if(m_areas.isEmpty() || force) {
0460         emit aboutToRemove(this);
0461 
0462         KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
0463         KConfigGroup group = setConfig.group(m_id);
0464         group.deleteGroup();
0465 #ifdef SYNC_OFTEN
0466         setConfig.sync();
0467 #endif
0468 
0469         if(!silent)
0470             emit setChangedSignificantly();
0471     }
0472 }
0473 
0474 void WorkingSet::saveFromArea(Sublime::Area* area)
0475 {
0476     qCDebug(WORKINGSET) << "saving" << m_id << "from area";
0477 
0478     bool wasPersistent = isPersistent();
0479 
0480     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
0481 
0482     KConfigGroup setGroup = setConfig.group(m_id);
0483     setGroup.deleteGroup();
0484 
0485     saveFromAreaPrivate(area->rootIndex(), setGroup, area->activeView());
0486 
0487     if (isEmpty()) {
0488         setGroup.deleteGroup();
0489     } else {
0490         setPersistent(wasPersistent);
0491     }
0492 
0493 #ifdef SYNC_OFTEN
0494     setConfig.sync();
0495 #endif
0496 
0497     emit setChangedSignificantly();
0498 }
0499 
0500 void WorkingSet::areaViewAdded(Sublime::AreaIndex*, Sublime::View*) {
0501     auto* area = qobject_cast<Sublime::Area*>(sender());
0502     Q_ASSERT(area);
0503     Q_ASSERT(area->workingSet() == m_id);
0504 
0505     qCDebug(WORKINGSET) << "added view in" << area << ", id" << m_id;
0506     if (m_loading) {
0507         qCDebug(WORKINGSET) << "doing nothing because loading";
0508         return;
0509     }
0510 
0511     changed(area);
0512 }
0513 
0514 void WorkingSet::areaViewRemoved(Sublime::AreaIndex*, Sublime::View* view) {
0515     auto* area = qobject_cast<Sublime::Area*>(sender());
0516     Q_ASSERT(area);
0517     Q_ASSERT(area->workingSet() == m_id);
0518 
0519     qCDebug(WORKINGSET) << "removed view in" << area << ", id" << m_id;
0520     if (m_loading) {
0521         qCDebug(WORKINGSET) << "doing nothing because loading";
0522         return;
0523     }
0524 
0525     const auto areasBefore = m_areas; // TODO: check if areas could be changed, otherwise use m_areas directly
0526     for (Sublime::Area* otherArea : areasBefore) {
0527         if(otherArea == area)
0528             continue;
0529         const auto otherAreaViews = otherArea->views();
0530         bool hadDocument = std::any_of(otherAreaViews.begin(), otherAreaViews.end(), [&](Sublime::View* otherView) {
0531             return (view->document() == otherView->document());
0532         });
0533 
0534         if(!hadDocument)
0535         {
0536             // We do this to prevent UI flicker. The view has already been removed from
0537             // one of the connected areas, so the working-set has already recorded the change.
0538             return;
0539         }
0540 
0541     }
0542 
0543     changed(area);
0544 }
0545 
0546 void WorkingSet::setPersistent(bool persistent) {
0547     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
0548     KConfigGroup group = setConfig.group(m_id);
0549     group.writeEntry("persistent", persistent);
0550 #ifdef SYNC_OFTEN
0551     group.sync();
0552 #endif
0553     qCDebug(WORKINGSET) << "setting" << m_id << "persistent:" << persistent;
0554 }
0555 
0556 bool WorkingSet::isPersistent() const {
0557     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
0558     KConfigGroup group = setConfig.group(m_id);
0559     return group.readEntry("persistent", false);
0560 }
0561 
0562 QIcon WorkingSet::icon() const
0563 {
0564     return m_icon;
0565 }
0566 
0567 bool WorkingSet::isConnected( Sublime::Area* area )
0568 {
0569   return m_areas.contains( area );
0570 }
0571 
0572 QString WorkingSet::id() const
0573 {
0574   return m_id;
0575 }
0576 
0577 bool WorkingSet::hasConnectedAreas() const
0578 {
0579   return !m_areas.isEmpty();
0580 }
0581 
0582 bool WorkingSet::hasConnectedArea(Sublime::Area *area) const
0583 {
0584   return m_areas.contains(area);
0585 }
0586 
0587 void WorkingSet::connectArea( Sublime::Area* area )
0588 {
0589   if ( m_areas.contains( area ) ) {
0590     qCDebug(WORKINGSET) << "tried to double-connect area";
0591     return;
0592   }
0593 
0594   qCDebug(WORKINGSET) << "connecting" << m_id << "to area" << area;
0595 
0596 //         Q_ASSERT(area->workingSet() == m_id);
0597 
0598   m_areas.push_back( area );
0599   connect( area, &Sublime::Area::viewAdded, this, &WorkingSet::areaViewAdded );
0600   connect( area, &Sublime::Area::viewRemoved, this, &WorkingSet::areaViewRemoved );
0601 }
0602 
0603 void WorkingSet::disconnectArea( Sublime::Area* area )
0604 {
0605   if ( !m_areas.contains( area ) ) {
0606     qCDebug(WORKINGSET) << "tried to disconnect not connected area";
0607     return;
0608   }
0609 
0610   qCDebug(WORKINGSET) << "disconnecting" << m_id << "from area" << area;
0611 
0612 //         Q_ASSERT(area->workingSet() == m_id);
0613 
0614   disconnect( area, &Sublime::Area::viewAdded, this, &WorkingSet::areaViewAdded );
0615   disconnect( area, &Sublime::Area::viewRemoved, this, &WorkingSet::areaViewRemoved );
0616   m_areas.removeAll( area );
0617 }
0618 
0619 void WorkingSet::changed( Sublime::Area* area )
0620 {
0621   if ( m_loading ) {
0622     return;
0623   }
0624 
0625   {
0626     //Do not capture changes done while loading
0627     PushValue<bool> enableLoading( m_loading, true );
0628 
0629     qCDebug(WORKINGSET) << "recording change done to" << m_id;
0630     saveFromArea(area);
0631   }
0632 
0633   emit setChangedSignificantly();
0634 }
0635 
0636 #include "moc_workingset.cpp"