File indexing completed on 2024-05-19 12:22:32
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 "area.h" 0008 0009 #include <QMap> 0010 #include <QList> 0011 #include <QStringList> 0012 #include <QAction> 0013 #include <QPointer> 0014 0015 #include "view.h" 0016 #include "document.h" 0017 #include "areaindex.h" 0018 #include "controller.h" 0019 #include <debug.h> 0020 0021 namespace Sublime { 0022 0023 // class AreaPrivate 0024 0025 class AreaPrivate 0026 { 0027 public: 0028 AreaPrivate() 0029 : rootIndex(new RootAreaIndex) 0030 , currentIndex(rootIndex.data()) 0031 { 0032 } 0033 0034 AreaPrivate(const AreaPrivate &p) 0035 : title(p.title) 0036 , rootIndex(new RootAreaIndex(*(p.rootIndex))) 0037 , currentIndex(rootIndex.data()) 0038 , controller(p.controller) 0039 , toolViewPositions() 0040 , desiredToolViews(p.desiredToolViews) 0041 , shownToolViews(p.shownToolViews) 0042 , iconName(p.iconName) 0043 , workingSet(p.workingSet) 0044 , workingSetPersists(p.workingSetPersists) 0045 , m_actions(p.m_actions) 0046 { 0047 } 0048 0049 ~AreaPrivate() 0050 { 0051 } 0052 0053 struct ViewFinder { 0054 explicit ViewFinder(View *_view): view(_view), index(nullptr) {} 0055 Area::WalkerMode operator() (AreaIndex *idx) { 0056 if (idx->hasView(view)) 0057 { 0058 index = idx; 0059 return Area::StopWalker; 0060 } 0061 return Area::ContinueWalker; 0062 } 0063 View *view; 0064 AreaIndex *index; 0065 }; 0066 0067 struct ViewLister { 0068 Area::WalkerMode operator()(AreaIndex *idx) { 0069 views += idx->views(); 0070 return Area::ContinueWalker; 0071 } 0072 QList<View*> views; 0073 }; 0074 0075 QString title; 0076 0077 QScopedPointer<RootAreaIndex> rootIndex; 0078 AreaIndex *currentIndex; 0079 Controller *controller = nullptr; 0080 0081 QList<View*> toolViews; 0082 QMap<View *, Sublime::Position> toolViewPositions; 0083 QMap<QString, Sublime::Position> desiredToolViews; 0084 QMap<Sublime::Position, QStringList> shownToolViews; 0085 QString iconName; 0086 QString workingSet; 0087 bool workingSetPersists = true; 0088 QPointer<View> activeView; 0089 QList<QAction*> m_actions; 0090 }; 0091 0092 // class Area 0093 0094 Area::Area(Controller *controller, const QString &name, const QString &title) 0095 : QObject(controller) 0096 , d_ptr(new AreaPrivate()) 0097 { 0098 Q_D(Area); 0099 0100 // FIXME: using objectName seems fishy. Introduce areaType method, 0101 // or some such. 0102 setObjectName(name); 0103 d->title = title; 0104 d->controller = controller; 0105 d->iconName = QStringLiteral("kdevelop"); 0106 initialize(); 0107 } 0108 0109 Area::Area(const Area &area) 0110 : QObject(area.controller()) 0111 , d_ptr(new AreaPrivate(*(area.d_ptr))) 0112 { 0113 Q_D(Area); 0114 0115 setObjectName(area.objectName()); 0116 0117 //clone tool views 0118 d->toolViews.clear(); 0119 for (View* view : qAsConst(area.toolViews())) { 0120 addToolView(view->document()->createView(), area.toolViewPosition(view)); 0121 } 0122 initialize(); 0123 } 0124 0125 void Area::initialize() 0126 { 0127 Q_D(Area); 0128 0129 connect(this, &Area::viewAdded, 0130 d->controller, &Controller::notifyViewAdded); 0131 connect(this, &Area::aboutToRemoveView, 0132 d->controller, &Controller::notifyViewRemoved); 0133 connect(this, &Area::toolViewAdded, 0134 d->controller, &Controller::notifyToolViewAdded); 0135 connect(this, &Area::aboutToRemoveToolView, 0136 d->controller, &Controller::notifyToolViewRemoved); 0137 connect(this, &Area::toolViewMoved, 0138 d->controller, &Controller::toolViewMoved); 0139 0140 /* In theory, ownership is passed to us, so should not bother detecting 0141 deletion outside. */ 0142 // Functor will be called after destructor has run -> capture controller pointer by value 0143 // otherwise we crash because we access the already freed pointer this->d 0144 auto controller = d->controller; 0145 connect(this, &Area::destroyed, controller, [this, controller](QObject* obj) { 0146 Q_ASSERT(obj == this); 0147 controller->removeArea(this); 0148 }); 0149 } 0150 0151 Area::~Area() = default; 0152 0153 View* Area::activeView() const 0154 { 0155 Q_D(const Area); 0156 0157 return d->activeView.data(); 0158 } 0159 0160 void Area::setActiveView(View* view) 0161 { 0162 Q_D(Area); 0163 0164 d->activeView = view; 0165 } 0166 0167 void Area::addView(View *view, AreaIndex *index, View *after) 0168 { 0169 //View *after = 0; 0170 if (!after && controller()->openAfterCurrent()) { 0171 after = activeView(); 0172 } 0173 index->add(view, after); 0174 connect(view, &View::positionChanged, this, &Area::positionChanged); 0175 qCDebug(SUBLIME) << "view added in" << this; 0176 connect(this, &Area::destroyed, view, &View::deleteLater); 0177 emit viewAdded(index, view); 0178 } 0179 0180 void Area::addView(View *view, View *after) 0181 { 0182 Q_D(Area); 0183 0184 AreaIndex *index = d->currentIndex; 0185 if (after) 0186 { 0187 AreaIndex *i = indexOf(after); 0188 if (i) 0189 index = i; 0190 } 0191 addView(view, index); 0192 } 0193 0194 void Area::addView(View *view, View *viewToSplit, Qt::Orientation orientation) 0195 { 0196 AreaIndex *indexToSplit = indexOf(viewToSplit); 0197 addView(view, indexToSplit, orientation); 0198 } 0199 0200 void Area::addView(View* view, AreaIndex* indexToSplit, Qt::Orientation orientation) 0201 { 0202 indexToSplit->split(view, orientation); 0203 emit viewAdded(indexToSplit, view); 0204 connect(this, &Area::destroyed, view, &View::deleteLater); 0205 } 0206 0207 View* Area::removeView(View *view) 0208 { 0209 AreaIndex *index = indexOf(view); 0210 if (!index) 0211 return nullptr; 0212 0213 emit aboutToRemoveView(index, view); 0214 index->remove(view); 0215 emit viewRemoved(index, view); 0216 0217 return view; 0218 } 0219 0220 AreaIndex *Area::indexOf(View *view) 0221 { 0222 Q_D(Area); 0223 0224 AreaPrivate::ViewFinder f(view); 0225 walkViews(f, d->rootIndex.data()); 0226 return f.index; 0227 } 0228 0229 RootAreaIndex *Area::rootIndex() const 0230 { 0231 Q_D(const Area); 0232 0233 return d->rootIndex.data(); 0234 } 0235 0236 void Area::addToolView(View *view, Position defaultPosition) 0237 { 0238 Q_D(Area); 0239 0240 d->toolViews.append(view); 0241 const QString id = view->document()->documentSpecifier(); 0242 const Position position = d->desiredToolViews.value(id, defaultPosition); 0243 d->desiredToolViews[id] = position; 0244 d->toolViewPositions[view] = position; 0245 emit toolViewAdded(view, position); 0246 } 0247 0248 void Sublime::Area::raiseToolView(View * toolView) 0249 { 0250 emit requestToolViewRaise(toolView); 0251 } 0252 0253 View* Area::removeToolView(View *view) 0254 { 0255 Q_D(Area); 0256 0257 if (!d->toolViews.contains(view)) 0258 return nullptr; 0259 0260 emit aboutToRemoveToolView(view, d->toolViewPositions[view]); 0261 QString id = view->document()->documentSpecifier(); 0262 qCDebug(SUBLIME) << this << "removed tool view " << id; 0263 d->desiredToolViews.remove(id); 0264 d->toolViews.removeAll(view); 0265 d->toolViewPositions.remove(view); 0266 return view; 0267 } 0268 0269 void Area::moveToolView(View *toolView, Position newPosition) 0270 { 0271 Q_D(Area); 0272 0273 if (!d->toolViews.contains(toolView)) 0274 return; 0275 0276 QString id = toolView->document()->documentSpecifier(); 0277 d->desiredToolViews[id] = newPosition; 0278 d->toolViewPositions[toolView] = newPosition; 0279 emit toolViewMoved(toolView, newPosition); 0280 } 0281 0282 const QList<View*> &Area::toolViews() const 0283 { 0284 Q_D(const Area); 0285 0286 return d->toolViews; 0287 } 0288 0289 Position Area::toolViewPosition(View *toolView) const 0290 { 0291 Q_D(const Area); 0292 0293 return d->toolViewPositions[toolView]; 0294 } 0295 0296 Controller *Area::controller() const 0297 { 0298 Q_D(const Area); 0299 0300 return d->controller; 0301 } 0302 0303 QList<View*> Sublime::Area::views() 0304 { 0305 Q_D(Area); 0306 0307 AreaPrivate::ViewLister lister; 0308 walkViews(lister, d->rootIndex.data()); 0309 return lister.views; 0310 } 0311 0312 QString Area::title() const 0313 { 0314 Q_D(const Area); 0315 0316 return d->title; 0317 } 0318 0319 void Area::setTitle(const QString &title) 0320 { 0321 Q_D(Area); 0322 0323 d->title = title; 0324 } 0325 0326 void Area::save(KConfigGroup& group) const 0327 { 0328 Q_D(const Area); 0329 0330 QStringList desired; 0331 desired.reserve(d->desiredToolViews.size()); 0332 for (auto i = d->desiredToolViews.begin(), e = d->desiredToolViews.end(); i != e; ++i) { 0333 desired << i.key() + QLatin1Char(':') + QString::number(static_cast<int>(i.value())); 0334 } 0335 group.writeEntry("desired views", desired); 0336 qCDebug(SUBLIME) << "save " << this << "wrote" << group.readEntry("desired views", ""); 0337 group.writeEntry("view on left", shownToolViews(Sublime::Left)); 0338 group.writeEntry("view on right", shownToolViews(Sublime::Right)); 0339 group.writeEntry("view on top", shownToolViews(Sublime::Top)); 0340 group.writeEntry("view on bottom", shownToolViews(Sublime::Bottom)); 0341 } 0342 0343 void Area::load(const KConfigGroup& group) 0344 { 0345 Q_D(Area); 0346 0347 qCDebug(SUBLIME) << "loading areas config"; 0348 d->desiredToolViews.clear(); 0349 const QStringList desired = group.readEntry("desired views", QStringList()); 0350 for (const QString& s : desired) { 0351 int i = s.indexOf(QLatin1Char(':')); 0352 if (i != -1) 0353 { 0354 QString id = s.left(i); 0355 int pos_i = s.midRef(i+1).toInt(); 0356 auto pos = static_cast<Sublime::Position>(pos_i); 0357 if (pos != Sublime::Left && pos != Sublime::Right && pos != Sublime::Top && pos != Sublime::Bottom) 0358 { 0359 pos = Sublime::Bottom; 0360 } 0361 0362 d->desiredToolViews[id] = pos; 0363 } 0364 } 0365 setShownToolViews(Sublime::Left, group.readEntry("view on left", QStringList())); 0366 setShownToolViews(Sublime::Right, 0367 group.readEntry("view on right", QStringList())); 0368 setShownToolViews(Sublime::Top, group.readEntry("view on top", QStringList())); 0369 setShownToolViews(Sublime::Bottom, 0370 group.readEntry("view on bottom", QStringList())); 0371 } 0372 0373 bool Area::wantToolView(const QString& id) 0374 { 0375 Q_D(Area); 0376 0377 return (d->desiredToolViews.contains(id)); 0378 } 0379 0380 void Area::setShownToolViews(Sublime::Position pos, const QStringList& ids) 0381 { 0382 Q_D(Area); 0383 0384 d->shownToolViews[pos] = ids; 0385 } 0386 0387 QStringList Area::shownToolViews(Sublime::Position pos) const 0388 { 0389 Q_D(const Area); 0390 0391 if (pos == Sublime::AllPositions) { 0392 QStringList allIds; 0393 allIds.reserve(d->shownToolViews.size()); 0394 std::for_each(d->shownToolViews.constBegin(), d->shownToolViews.constEnd(), [&](const QStringList& ids) { 0395 allIds << ids; 0396 }); 0397 return allIds; 0398 } 0399 0400 return d->shownToolViews[pos]; 0401 } 0402 0403 void Area::setDesiredToolViews( 0404 const QMap<QString, Sublime::Position>& desiredToolViews) 0405 { 0406 Q_D(Area); 0407 0408 d->desiredToolViews = desiredToolViews; 0409 } 0410 0411 QString Area::iconName() const 0412 { 0413 Q_D(const Area); 0414 0415 return d->iconName; 0416 } 0417 0418 void Area::setIconName(const QString& iconName) 0419 { 0420 Q_D(Area); 0421 0422 d->iconName = iconName; 0423 } 0424 0425 void Area::positionChanged(View *view, int newPos) 0426 { 0427 qCDebug(SUBLIME) << view << newPos; 0428 AreaIndex *index = indexOf(view); 0429 index->moveViewPosition(view, newPos); 0430 } 0431 0432 0433 QString Area::workingSet() const 0434 { 0435 Q_D(const Area); 0436 0437 return d->workingSet; 0438 } 0439 0440 bool Area::workingSetPersistent() const 0441 { 0442 Q_D(const Area); 0443 0444 return d->workingSetPersists; 0445 } 0446 0447 void Area::setWorkingSet(const QString &name, bool persistent, Area *oldArea) 0448 { 0449 Q_D(Area); 0450 0451 oldArea = oldArea ? oldArea : this; 0452 if (oldArea != this || name != d->workingSet) { 0453 qCDebug(SUBLIME) << this << "setting new working-set" << name; 0454 QString oldName = d->workingSet; 0455 emit changingWorkingSet(this, oldArea, oldName, name); 0456 d->workingSet = name; 0457 d->workingSetPersists = persistent; 0458 emit changedWorkingSet(this, oldArea, oldName, name); 0459 } else if (name.isEmpty()) { 0460 d->workingSetPersists = persistent; 0461 } 0462 } 0463 0464 bool Area::closeView(View* view, bool silent) 0465 { 0466 QPointer<Document> doc = view->document(); 0467 0468 // We don't just delete the view, because if silent is false, we might need to ask the user. 0469 if(doc && !silent) 0470 { 0471 // Do some counting to check whether we need to ask the user for feedback 0472 qCDebug(SUBLIME) << "Closing view for" << view->document()->documentSpecifier() << "views" << view->document()->views().size() << "in area" << this; 0473 int viewsInCurrentArea = 0; // Number of views for the same document in the current area 0474 int viewsInOtherAreas = 0; // Number of views for the same document in other areas 0475 int viewsInOtherWorkingSets = 0; // Number of views for the same document in areas with different working-set 0476 0477 for (View* otherView : qAsConst(doc.data()->views())) { 0478 Area* area = controller()->areaForView(otherView); 0479 if(area == this) 0480 viewsInCurrentArea += 1; 0481 if(!area || (area != this)) 0482 viewsInOtherAreas += 1; 0483 if(area && area != this && area->workingSet() != workingSet()) 0484 viewsInOtherWorkingSets += 1; 0485 } 0486 0487 if(viewsInCurrentArea == 1 && (viewsInOtherAreas == 0 || viewsInOtherWorkingSets == 0)) 0488 { 0489 // Time to ask the user for feedback, because the document will be completely closed 0490 // due to working-set synchronization 0491 if( !doc.data()->askForCloseFeedback() ) 0492 return false; 0493 } 0494 } 0495 0496 // otherwise we can silently close the view, 0497 // the document will still have an opened view somewhere 0498 delete removeView(view); 0499 0500 return true; 0501 } 0502 0503 void Area::clearViews(bool silent) 0504 { 0505 const auto views = this->views(); 0506 for (Sublime::View* view : views) { 0507 closeView(view, silent); 0508 } 0509 } 0510 0511 void Area::clearDocuments() 0512 { 0513 if (views().isEmpty()) 0514 emit clearWorkingSet(this); 0515 else 0516 clearViews(true); 0517 } 0518 0519 QList<QAction*> Area::actions() const 0520 { 0521 Q_D(const Area); 0522 0523 return d->m_actions; 0524 } 0525 0526 void Area::addAction(QAction* action) 0527 { 0528 Q_D(Area); 0529 0530 Q_ASSERT(!d->m_actions.contains(action)); 0531 connect(action, &QAction::destroyed, this, &Area::actionDestroyed); 0532 d->m_actions.append(action); 0533 } 0534 0535 void Area::actionDestroyed(QObject* action) 0536 { 0537 Q_D(Area); 0538 0539 d->m_actions.removeAll(qobject_cast<QAction*>(action)); 0540 } 0541 0542 } 0543 0544 #include "moc_area.cpp"