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"