File indexing completed on 2024-04-28 04:37:28
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 "workingsetcontroller.h" 0008 0009 #include <QTimer> 0010 #include <QVBoxLayout> 0011 #include <QRandomGenerator> 0012 0013 #include "mainwindow.h" 0014 #include "partdocument.h" 0015 #include "uicontroller.h" 0016 0017 #include <interfaces/iuicontroller.h> 0018 #include <interfaces/isession.h> 0019 0020 #include <sublime/view.h> 0021 #include <sublime/area.h> 0022 0023 #include <util/activetooltip.h> 0024 0025 #include "workingsets/workingset.h" 0026 #include "workingsets/workingsettooltipwidget.h" 0027 #include "workingsets/workingsetwidget.h" 0028 #include "workingsets/closedworkingsetswidget.h" 0029 #include "core.h" 0030 #include "debug_workingset.h" 0031 0032 using namespace KDevelop; 0033 0034 const int toolTipTimeout = 2000; 0035 0036 WorkingSetController::WorkingSetController() 0037 { 0038 m_hideToolTipTimer = new QTimer(this); 0039 m_hideToolTipTimer->setInterval(toolTipTimeout); 0040 m_hideToolTipTimer->setSingleShot(true); 0041 } 0042 0043 void WorkingSetController::initialize() 0044 { 0045 //Load all working-sets 0046 KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); 0047 QMap<QString, QStringList> areaConfigs; 0048 const auto sets = setConfig.groupList(); 0049 for (const QString& set : sets) { 0050 // do not load working set if the id contains an '|', because it then belongs to an area. 0051 // this is functionally equivalent to the if ( ! config->icon ) stuff which was there before. 0052 if (set.contains(QLatin1Char('|'))) { 0053 areaConfigs[set.left(set.indexOf(QLatin1Char('|')))] << set; 0054 } else if (!setConfig.group(set).hasKey("Orientation") && setConfig.group(set).readEntry("View Count", 0) == 0) { 0055 areaConfigs[set] << set; 0056 } else { 0057 workingSet(set); 0058 } 0059 } 0060 0061 // Clean up config 0062 for (auto it = areaConfigs.constBegin(); it != areaConfigs.constEnd(); ++it) { 0063 if (m_workingSets.contains(it.key())) { 0064 continue; 0065 } 0066 for (auto &areaConfig : it.value()) { 0067 setConfig.deleteGroup(areaConfig); 0068 } 0069 } 0070 0071 m_emptyWorkingSet = new WorkingSet(QStringLiteral("empty")); 0072 0073 if(!(Core::self()->setupFlags() & Core::NoUi)) { 0074 setupActions(); 0075 } 0076 } 0077 0078 void WorkingSetController::cleanup() 0079 { 0080 auto area = Core::self()->uiControllerInternal()->activeArea(); 0081 if (area && !area->workingSet().isEmpty()) { 0082 Q_ASSERT(m_workingSets.contains(area->workingSet())); 0083 m_workingSets[area->workingSet()]->saveFromArea(area); 0084 } 0085 0086 const auto oldWorkingSet = m_workingSets; 0087 for (WorkingSet* set : oldWorkingSet) { 0088 qCDebug(WORKINGSET) << "set" << set->id() << "persistent" << set->isPersistent() << "has areas:" << set->hasConnectedAreas() << "files" << set->fileList(); 0089 if(!set->isPersistent() && !set->hasConnectedAreas()) { 0090 qCDebug(WORKINGSET) << "deleting"; 0091 set->deleteSet(true, true); 0092 } 0093 delete set; 0094 } 0095 0096 m_workingSets.clear(); 0097 0098 delete m_emptyWorkingSet; 0099 m_emptyWorkingSet = nullptr; 0100 } 0101 0102 const QString WorkingSetController::makeSetId(const QString& prefix) const 0103 { 0104 QString newId; 0105 const uint maxRetries = 10; 0106 auto* randomGenerator = QRandomGenerator::global(); 0107 for (uint retry = 2; retry <= maxRetries; retry++) { 0108 const auto random = randomGenerator->bounded(10000000); 0109 newId = QStringLiteral("%1_%2").arg(prefix).arg(random); 0110 WorkingSetIconParameters params(newId); 0111 for (WorkingSet* set : m_workingSets) { 0112 if(set->isEmpty()) { 0113 continue; 0114 } 0115 // The last retry will always generate a valid set 0116 if(retry != maxRetries && WorkingSetIconParameters(set->id()).similarity(params) >= retry*8) { 0117 newId = QString(); 0118 break; 0119 } 0120 } 0121 if(! newId.isEmpty()) { 0122 break; 0123 } 0124 } 0125 return newId; 0126 } 0127 0128 WorkingSet* WorkingSetController::newWorkingSet(const QString& prefix) 0129 { 0130 return workingSet(makeSetId(prefix)); 0131 } 0132 0133 WorkingSet* WorkingSetController::workingSet(const QString& id) 0134 { 0135 if(id.isEmpty()) 0136 return m_emptyWorkingSet; 0137 0138 auto workingSetIt = m_workingSets.find(id); 0139 if (workingSetIt == m_workingSets.end()) { 0140 auto* set = new WorkingSet(id); 0141 connect(set, &WorkingSet::aboutToRemove, 0142 this, &WorkingSetController::aboutToRemoveWorkingSet); 0143 workingSetIt= m_workingSets.insert(id, set); 0144 emit workingSetAdded(set); 0145 } 0146 0147 return *workingSetIt; 0148 } 0149 0150 QWidget* WorkingSetController::createSetManagerWidget(MainWindow* parent, Sublime::Area* fixedArea) { 0151 if (fixedArea) { 0152 return new WorkingSetWidget(fixedArea, parent); 0153 } else { 0154 return new ClosedWorkingSetsWidget(parent); 0155 } 0156 } 0157 0158 void WorkingSetController::setupActions() 0159 { 0160 } 0161 0162 ActiveToolTip* WorkingSetController::tooltip() const 0163 { 0164 return m_tooltip; 0165 } 0166 0167 void WorkingSetController::showToolTip(WorkingSet* set, const QPoint& pos) 0168 { 0169 delete m_tooltip; 0170 0171 auto* window = static_cast<KDevelop::MainWindow*>(Core::self()->uiControllerInternal()->activeMainWindow()); 0172 0173 m_tooltip = new KDevelop::ActiveToolTip(window, pos); 0174 auto* layout = new QVBoxLayout(m_tooltip); 0175 layout->setContentsMargins(0, 0, 0, 0); 0176 auto* widget = new WorkingSetToolTipWidget(m_tooltip, set, window); 0177 layout->addWidget(widget); 0178 m_tooltip->resize( m_tooltip->sizeHint() ); 0179 0180 connect(widget, &WorkingSetToolTipWidget::shouldClose, m_tooltip.data(), &ActiveToolTip::close); 0181 0182 ActiveToolTip::showToolTip(m_tooltip); 0183 } 0184 0185 void WorkingSetController::showGlobalToolTip() 0186 { 0187 auto* window = static_cast<KDevelop::MainWindow*>(Core::self()->uiControllerInternal()->activeMainWindow()); 0188 0189 showToolTip(workingSet(window->area()->workingSet()), 0190 window->mapToGlobal(window->geometry().topRight())); 0191 0192 connect(m_hideToolTipTimer, &QTimer::timeout, m_tooltip.data(), &ActiveToolTip::deleteLater); 0193 m_hideToolTipTimer->start(); 0194 connect(m_tooltip.data(), &ActiveToolTip::mouseIn, m_hideToolTipTimer, &QTimer::stop); 0195 connect(m_tooltip.data(), &ActiveToolTip::mouseOut, m_hideToolTipTimer, QOverload<>::of(&QTimer::start)); 0196 } 0197 0198 WorkingSetToolTipWidget* WorkingSetController::workingSetToolTip() 0199 { 0200 if(!m_tooltip) 0201 showGlobalToolTip(); 0202 0203 m_hideToolTipTimer->start(); 0204 0205 if(m_tooltip) 0206 { 0207 auto* widget = m_tooltip->findChild<WorkingSetToolTipWidget*>(); 0208 Q_ASSERT(widget); 0209 return widget; 0210 } 0211 return nullptr; 0212 } 0213 0214 void WorkingSetController::nextDocument() 0215 { 0216 auto widget = workingSetToolTip(); 0217 if (widget) { 0218 widget->nextDocument(); 0219 } 0220 } 0221 0222 void WorkingSetController::previousDocument() 0223 { 0224 auto widget = workingSetToolTip(); 0225 if (widget) { 0226 widget->previousDocument(); 0227 } 0228 } 0229 0230 void WorkingSetController::initializeController( UiController* controller ) 0231 { 0232 connect( controller, &UiController::areaCreated, this, &WorkingSetController::areaCreated ); 0233 } 0234 0235 QList< WorkingSet* > WorkingSetController::allWorkingSets() const 0236 { 0237 return m_workingSets.values(); 0238 } 0239 0240 void WorkingSetController::areaCreated( Sublime::Area* area ) 0241 { 0242 if (!area->workingSet().isEmpty()) { 0243 WorkingSet* set = workingSet( area->workingSet() ); 0244 set->connectArea( area ); 0245 } 0246 0247 connect(area, &Sublime::Area::changingWorkingSet, 0248 this, &WorkingSetController::changingWorkingSet); 0249 connect(area, &Sublime::Area::changedWorkingSet, 0250 this, &WorkingSetController::changedWorkingSet); 0251 connect(area, &Sublime::Area::viewAdded, 0252 this, &WorkingSetController::viewAdded); 0253 connect(area, &Sublime::Area::clearWorkingSet, 0254 this, &WorkingSetController::clearWorkingSet); 0255 } 0256 0257 void WorkingSetController::saveArea(Sublime::Area* area) 0258 { 0259 if (area && !area->workingSet().isEmpty()) { 0260 workingSet(area->workingSet())->saveFromArea(area); 0261 } 0262 } 0263 0264 void WorkingSetController::changingWorkingSet(Sublime::Area *area, Sublime::Area *oldArea, const QString &from, const QString &to) 0265 { 0266 qCDebug(WORKINGSET) << "changing working-set from" << from << oldArea << "to" << to << area; 0267 if (from == to) 0268 return; 0269 0270 if (!from.isEmpty()) { 0271 WorkingSet* oldSet = workingSet(from); 0272 oldSet->disconnectArea(area); 0273 if (!oldSet->id().isEmpty() && area == oldArea) { 0274 oldSet->saveFromArea(area); 0275 } 0276 } 0277 } 0278 0279 void WorkingSetController::changedWorkingSet(Sublime::Area *area, Sublime::Area *oldArea, const QString &from, const QString &to) 0280 { 0281 qCDebug(WORKINGSET) << "changed working-set from" << from << "to" << to << "area" << area; 0282 if ((oldArea == area && from == to) || m_changingWorkingSet) { 0283 return; 0284 } 0285 0286 if (!to.isEmpty()) { 0287 WorkingSet* newSet = workingSet(to); 0288 newSet->connectArea(area); 0289 newSet->loadToArea(area); 0290 } else { 0291 // Clear silently, any user-interaction should have happened before 0292 area->clearViews(true); 0293 } 0294 0295 emit workingSetSwitched(); 0296 } 0297 0298 void WorkingSetController::viewAdded( Sublime::AreaIndex* , Sublime::View* ) 0299 { 0300 auto* area = qobject_cast< Sublime::Area* >(sender()); 0301 Q_ASSERT(area); 0302 0303 if (area->workingSet().isEmpty()) { 0304 //Spawn a new working-set 0305 m_changingWorkingSet = true; 0306 WorkingSet* set = Core::self()->workingSetControllerInternal()->newWorkingSet(area->objectName()); 0307 qCDebug(WORKINGSET) << "Spawned new working-set" << set->id() << "because a view was added"; 0308 set->setPersistent(area->workingSetPersistent()); 0309 set->connectArea(area); 0310 set->saveFromArea(area); 0311 area->setWorkingSet(set->id(), area->workingSetPersistent()); 0312 m_changingWorkingSet = false; 0313 } 0314 } 0315 0316 void WorkingSetController::clearWorkingSet(Sublime::Area * area) 0317 { 0318 const QString workingSetId = area->workingSet(); 0319 if (workingSetId.isEmpty()) { 0320 // Nothing to do - area has no working set 0321 return; 0322 } 0323 0324 WorkingSet* set = workingSet(workingSetId); 0325 set->deleteSet(true); 0326 0327 WorkingSet* newSet = workingSet(workingSetId); 0328 newSet->connectArea(area); 0329 newSet->loadToArea(area); 0330 Q_ASSERT(newSet->fileList().isEmpty()); 0331 } 0332 0333 #include "moc_workingsetcontroller.cpp"