dockmanager.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2020 Uwe Kindler
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of Qt Creator.
  7. **
  8. ** Commercial License Usage
  9. ** Licensees holding valid commercial Qt licenses may use this file in
  10. ** accordance with the commercial license agreement provided with the
  11. ** Software or, alternatively, in accordance with the terms contained in
  12. ** a written agreement between you and The Qt Company. For licensing terms
  13. ** and conditions see https://www.qt.io/terms-conditions. For further
  14. ** information use the contact form at https://www.qt.io/contact-us.
  15. **
  16. ** GNU Lesser General Public License Usage
  17. ** Alternatively, this file may be used under the terms of the GNU Lesser
  18. ** General Public License version 2.1 or (at your option) any later version.
  19. ** The licenses are as published by the Free Software Foundation
  20. ** and appearing in the file LICENSE.LGPLv21 included in the packaging
  21. ** of this file. Please review the following information to ensure
  22. ** the GNU Lesser General Public License version 2.1 requirements
  23. ** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 3 or (at your option) any later version
  28. ** approved by the KDE Free Qt Foundation. The licenses are as published by
  29. ** the Free Software Foundation and appearing in the file LICENSE.GPL3
  30. ** included in the packaging of this file. Please review the following
  31. ** information to ensure the GNU General Public License requirements will
  32. ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
  33. **
  34. ****************************************************************************/
  35. #include "dockmanager.h"
  36. #include "ads_globals.h"
  37. #include "dockareawidget.h"
  38. #include "dockingstatereader.h"
  39. #include "dockoverlay.h"
  40. #include "dockwidget.h"
  41. #include "dockwidgettab.h"
  42. #include "floatingdockcontainer.h"
  43. #include "iconprovider.h"
  44. #include "workspacedialog.h"
  45. #include <utils/qtcassert.h>
  46. #include <algorithm>
  47. #include <iostream>
  48. #include <QAction>
  49. #include <QApplication>
  50. #include <QDateTime>
  51. #include <QDir>
  52. #include <QFile>
  53. #include <QFileInfo>
  54. #include <QList>
  55. #include <QLoggingCategory>
  56. #include <QMainWindow>
  57. #include <QMap>
  58. #include <QMenu>
  59. #include <QMessageBox>
  60. #include <QSettings>
  61. #include <QVariant>
  62. #include <QXmlStreamWriter>
  63. static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
  64. namespace ADS
  65. {
  66. static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig;
  67. /**
  68. * Private data class of DockManager class (pimpl)
  69. */
  70. struct DockManagerPrivate
  71. {
  72. DockManager *q;
  73. QList<FloatingDockContainer *> m_floatingWidgets;
  74. QList<DockContainerWidget *> m_containers;
  75. DockOverlay *m_containerOverlay;
  76. DockOverlay *m_dockAreaOverlay;
  77. QMap<QString, DockWidget *> m_dockWidgetsMap;
  78. bool m_restoringState = false;
  79. QVector<FloatingDockContainer *> m_uninitializedFloatingWidgets;
  80. QString m_workspaceName;
  81. bool m_workspaceListDirty = true;
  82. QStringList m_workspaces;
  83. QHash<QString, QDateTime> m_workspaceDateTimes;
  84. QString m_workspaceToRestoreAtStartup;
  85. bool m_autorestoreLastWorkspace; // This option is set in the Workspace Manager!
  86. QSettings *m_settings;
  87. /**
  88. * Private data constructor
  89. */
  90. DockManagerPrivate(DockManager *parent);
  91. /**
  92. * Checks if the given data stream is a valid docking system state
  93. * file.
  94. */
  95. bool checkFormat(const QByteArray &state, int version);
  96. /**
  97. * Restores the state
  98. */
  99. bool restoreStateFromXml(const QByteArray &state,
  100. int version,
  101. bool testing = internal::restore);
  102. /**
  103. * Restore state
  104. */
  105. bool restoreState(const QByteArray &state, int version);
  106. void restoreDockWidgetsOpenState();
  107. void restoreDockAreasIndices();
  108. void emitTopLevelEvents();
  109. void hideFloatingWidgets()
  110. {
  111. // Hide updates of floating widgets from user
  112. for (auto floatingWidget : m_floatingWidgets) { // TODO qAsConst()
  113. floatingWidget->hide();
  114. }
  115. }
  116. void markDockWidgetsDirty()
  117. {
  118. for (auto dockWidget : m_dockWidgetsMap) { // TODO qAsConst()
  119. dockWidget->setProperty("dirty", true);
  120. }
  121. }
  122. /**
  123. * Restores the container with the given index
  124. */
  125. bool restoreContainer(int index, DockingStateReader &stream, bool testing);
  126. void workspaceLoadingProgress();
  127. };
  128. // struct DockManagerPrivate
  129. DockManagerPrivate::DockManagerPrivate(DockManager *parent)
  130. : q(parent)
  131. {}
  132. bool DockManagerPrivate::restoreContainer(int index, DockingStateReader &stream, bool testing)
  133. {
  134. if (testing) {
  135. index = 0;
  136. }
  137. bool result = false;
  138. if (index >= m_containers.count()) {
  139. FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
  140. result = floatingWidget->restoreState(stream, testing);
  141. } else {
  142. qCInfo(adsLog) << "d->m_containers[i]->restoreState ";
  143. auto container = m_containers[index];
  144. if (container->isFloating()) {
  145. result = container->floatingWidget()->restoreState(stream, testing);
  146. } else {
  147. result = container->restoreState(stream, testing);
  148. }
  149. }
  150. return result;
  151. }
  152. bool DockManagerPrivate::checkFormat(const QByteArray &state, int version)
  153. {
  154. return restoreStateFromXml(state, version, internal::restoreTesting);
  155. }
  156. bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int version, bool testing)
  157. {
  158. Q_UNUSED(version) // TODO version is not needed, why is it in here in the first place?
  159. if (state.isEmpty()) {
  160. return false;
  161. }
  162. DockingStateReader stateReader(state);
  163. stateReader.readNextStartElement();
  164. if (stateReader.name() != "QtAdvancedDockingSystem") {
  165. return false;
  166. }
  167. qCInfo(adsLog) << stateReader.attributes().value("version");
  168. bool ok;
  169. int v = stateReader.attributes().value("version").toInt(&ok);
  170. if (!ok || v > CurrentVersion) {
  171. return false;
  172. }
  173. stateReader.setFileVersion(v);
  174. bool result = true;
  175. #ifdef ADS_DEBUG_PRINT
  176. int dockContainers = stateReader.attributes().value("containers").toInt();
  177. qCInfo(adsLog) << dockContainers;
  178. #endif
  179. int dockContainerCount = 0;
  180. while (stateReader.readNextStartElement()) {
  181. if (stateReader.name() == "container") {
  182. result = restoreContainer(dockContainerCount, stateReader, testing);
  183. if (!result) {
  184. break;
  185. }
  186. dockContainerCount++;
  187. }
  188. }
  189. if (!testing) {
  190. // Delete remaining empty floating widgets
  191. int floatingWidgetIndex = dockContainerCount - 1;
  192. int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex;
  193. for (int i = 0; i < deleteCount; ++i) {
  194. m_floatingWidgets[floatingWidgetIndex + i]->deleteLater();
  195. q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->dockContainer());
  196. }
  197. }
  198. return result;
  199. }
  200. void DockManagerPrivate::restoreDockWidgetsOpenState()
  201. {
  202. // All dock widgets, that have not been processed in the restore state
  203. // function are invisible to the user now and have no assigned dock area
  204. // They do not belong to any dock container, until the user toggles the
  205. // toggle view action the next time
  206. for (auto dockWidget : m_dockWidgetsMap) {
  207. if (dockWidget->property(internal::dirtyProperty).toBool()) {
  208. dockWidget->flagAsUnassigned();
  209. emit dockWidget->viewToggled(false);
  210. } else {
  211. dockWidget->toggleViewInternal(
  212. !dockWidget->property(internal::closedProperty).toBool());
  213. }
  214. }
  215. }
  216. void DockManagerPrivate::restoreDockAreasIndices()
  217. {
  218. // Now all dock areas are properly restored and we setup the index of
  219. // The dock areas because the previous toggleView() action has changed
  220. // the dock area index
  221. int count = 0;
  222. for (auto dockContainer : m_containers) {
  223. count++;
  224. for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
  225. DockAreaWidget *dockArea = dockContainer->dockArea(i);
  226. QString dockWidgetName = dockArea->property("currentDockWidget").toString();
  227. DockWidget *dockWidget = nullptr;
  228. if (!dockWidgetName.isEmpty()) {
  229. dockWidget = q->findDockWidget(dockWidgetName);
  230. }
  231. if (!dockWidget || dockWidget->isClosed()) {
  232. int index = dockArea->indexOfFirstOpenDockWidget();
  233. if (index < 0) {
  234. continue;
  235. }
  236. dockArea->setCurrentIndex(index);
  237. } else {
  238. dockArea->internalSetCurrentDockWidget(dockWidget);
  239. }
  240. }
  241. }
  242. }
  243. void DockManagerPrivate::emitTopLevelEvents()
  244. {
  245. // Finally we need to send the topLevelChanged() signals for all dock
  246. // widgets if top level changed
  247. for (auto dockContainer : m_containers) {
  248. DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget();
  249. if (topLevelDockWidget) {
  250. topLevelDockWidget->emitTopLevelChanged(true);
  251. } else {
  252. for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
  253. auto dockArea = dockContainer->dockArea(i);
  254. for (auto dockWidget : dockArea->dockWidgets()) {
  255. dockWidget->emitTopLevelChanged(false);
  256. }
  257. }
  258. }
  259. }
  260. }
  261. bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
  262. {
  263. QByteArray currentState = state.startsWith("<?xml") ? state : qUncompress(state);
  264. if (!checkFormat(currentState, version)) {
  265. qCInfo(adsLog) << "checkFormat: Error checking format!!!";
  266. return false;
  267. }
  268. // Hide updates of floating widgets from use
  269. hideFloatingWidgets();
  270. markDockWidgetsDirty();
  271. if (!restoreStateFromXml(currentState, version)) {
  272. qCInfo(adsLog) << "restoreState: Error restoring state!!!";
  273. return false;
  274. }
  275. restoreDockWidgetsOpenState();
  276. restoreDockAreasIndices();
  277. emitTopLevelEvents();
  278. return true;
  279. }
  280. DockManager::DockManager(QWidget *parent)
  281. : DockContainerWidget(this, parent)
  282. , d(new DockManagerPrivate(this))
  283. {
  284. connect(this, &DockManager::workspaceListChanged, this, [=] {
  285. d->m_workspaceListDirty = true;
  286. });
  287. createRootSplitter();
  288. QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent);
  289. if (mainWindow) {
  290. mainWindow->setCentralWidget(this);
  291. }
  292. d->m_dockAreaOverlay = new DockOverlay(this, DockOverlay::ModeDockAreaOverlay);
  293. d->m_containerOverlay = new DockOverlay(this, DockOverlay::ModeContainerOverlay);
  294. d->m_containers.append(this);
  295. //d->loadStylesheet();
  296. }
  297. DockManager::~DockManager()
  298. {
  299. // If the factory default workspace is still loaded, create a default workspace just in case
  300. // the layout changed as there is no tracking of layout changes.
  301. if (isFactoryDefaultWorkspace(d->m_workspaceName)
  302. && !isDefaultWorkspace(d->m_workspaceName)) {
  303. createWorkspace(Constants::DEFAULT_NAME);
  304. openWorkspace(Constants::DEFAULT_NAME);
  305. }
  306. emit aboutToUnloadWorkspace(d->m_workspaceName);
  307. save();
  308. for (auto floatingWidget : d->m_floatingWidgets) {
  309. delete floatingWidget;
  310. }
  311. delete d;
  312. }
  313. DockManager::ConfigFlags DockManager::configFlags() { return g_staticConfigFlags; }
  314. void DockManager::setConfigFlags(const ConfigFlags flags) { g_staticConfigFlags = flags; }
  315. void DockManager::setConfigFlag(eConfigFlag flag, bool on)
  316. {
  317. internal::setFlag(g_staticConfigFlags, flag, on);
  318. }
  319. bool DockManager::testConfigFlag(eConfigFlag flag)
  320. {
  321. return configFlags().testFlag(flag);
  322. }
  323. IconProvider &DockManager::iconProvider()
  324. {
  325. static IconProvider instance;
  326. return instance;
  327. }
  328. int DockManager::startDragDistance()
  329. {
  330. return static_cast<int>(QApplication::startDragDistance() * 1.5);
  331. }
  332. void DockManager::setSettings(QSettings *settings) { d->m_settings = settings; }
  333. DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area,
  334. DockWidget *dockWidget,
  335. DockAreaWidget *dockAreaWidget)
  336. {
  337. d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
  338. return DockContainerWidget::addDockWidget(area, dockWidget, dockAreaWidget);
  339. }
  340. DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget)
  341. {
  342. DockAreaWidget *areaWidget = lastAddedDockAreaWidget(area);
  343. if (areaWidget) {
  344. return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget);
  345. } else if (!openedDockAreas().isEmpty()) {
  346. return addDockWidget(area, dockWidget, openedDockAreas().last());
  347. } else {
  348. return addDockWidget(area, dockWidget, nullptr);
  349. }
  350. }
  351. DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget,
  352. DockAreaWidget *dockAreaWidget)
  353. {
  354. return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget);
  355. }
  356. FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget)
  357. {
  358. d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
  359. DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget();
  360. if (oldDockArea) {
  361. oldDockArea->removeDockWidget(dockWidget);
  362. }
  363. dockWidget->setDockManager(this);
  364. FloatingDockContainer *floatingWidget = new FloatingDockContainer(dockWidget);
  365. floatingWidget->resize(dockWidget->size());
  366. if (isVisible()) {
  367. floatingWidget->show();
  368. } else {
  369. d->m_uninitializedFloatingWidgets.append(floatingWidget);
  370. }
  371. return floatingWidget;
  372. }
  373. void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget)
  374. {
  375. d->m_floatingWidgets.append(floatingWidget);
  376. emit floatingWidgetCreated(floatingWidget);
  377. qCInfo(adsLog) << "d->FloatingWidgets.count() " << d->m_floatingWidgets.count();
  378. }
  379. void DockManager::removeFloatingWidget(FloatingDockContainer *floatingWidget)
  380. {
  381. d->m_floatingWidgets.removeAll(floatingWidget);
  382. }
  383. void DockManager::registerDockContainer(DockContainerWidget *dockContainer)
  384. {
  385. d->m_containers.append(dockContainer);
  386. }
  387. void DockManager::removeDockContainer(DockContainerWidget *dockContainer)
  388. {
  389. if (this != dockContainer) {
  390. d->m_containers.removeAll(dockContainer);
  391. }
  392. }
  393. DockOverlay *DockManager::containerOverlay() const { return d->m_containerOverlay; }
  394. DockOverlay *DockManager::dockAreaOverlay() const { return d->m_dockAreaOverlay; }
  395. const QList<DockContainerWidget *> DockManager::dockContainers() const
  396. {
  397. return d->m_containers;
  398. }
  399. const QList<FloatingDockContainer *> DockManager::floatingWidgets() const
  400. {
  401. return d->m_floatingWidgets;
  402. }
  403. unsigned int DockManager::zOrderIndex() const { return 0; }
  404. QByteArray DockManager::saveState(int version) const
  405. {
  406. QByteArray xmlData;
  407. QXmlStreamWriter stream(&xmlData);
  408. auto configFlags = DockManager::configFlags();
  409. stream.setAutoFormatting(configFlags.testFlag(XmlAutoFormattingEnabled));
  410. stream.writeStartDocument();
  411. stream.writeStartElement("QtAdvancedDockingSystem");
  412. stream.writeAttribute("version", QString::number(version));
  413. stream.writeAttribute("containers", QString::number(d->m_containers.count()));
  414. for (auto container : d->m_containers) {
  415. container->saveState(stream);
  416. }
  417. stream.writeEndElement();
  418. stream.writeEndDocument();
  419. return xmlData;
  420. }
  421. bool DockManager::restoreState(const QByteArray &state, int version)
  422. {
  423. // Prevent multiple calls as long as state is not restore. This may
  424. // happen, if QApplication::processEvents() is called somewhere
  425. if (d->m_restoringState) {
  426. return false;
  427. }
  428. // We hide the complete dock manager here. Restoring the state means
  429. // that DockWidgets are removed from the DockArea internal stack layout
  430. // which in turn means, that each time a widget is removed the stack
  431. // will show and raise the next available widget which in turn
  432. // triggers show events for the dock widgets. To avoid this we hide the
  433. // dock manager. Because there will be no processing of application
  434. // events until this function is finished, the user will not see this
  435. // hiding
  436. bool isHidden = this->isHidden();
  437. if (!isHidden) {
  438. hide();
  439. }
  440. d->m_restoringState = true;
  441. emit restoringState();
  442. bool result = d->restoreState(state, version);
  443. d->m_restoringState = false;
  444. emit stateRestored();
  445. if (!isHidden) {
  446. show();
  447. }
  448. return result;
  449. }
  450. void DockManager::showEvent(QShowEvent *event)
  451. {
  452. Super::showEvent(event);
  453. if (d->m_uninitializedFloatingWidgets.empty()) {
  454. return;
  455. }
  456. for (auto floatingWidget : d->m_uninitializedFloatingWidgets) {
  457. floatingWidget->show();
  458. }
  459. d->m_uninitializedFloatingWidgets.clear();
  460. }
  461. DockWidget *DockManager::findDockWidget(const QString &objectName) const
  462. {
  463. return d->m_dockWidgetsMap.value(objectName, nullptr);
  464. }
  465. void DockManager::removeDockWidget(DockWidget *dockWidget)
  466. {
  467. emit dockWidgetAboutToBeRemoved(dockWidget);
  468. d->m_dockWidgetsMap.remove(dockWidget->objectName());
  469. DockContainerWidget::removeDockWidget(dockWidget);
  470. emit dockWidgetRemoved(dockWidget);
  471. }
  472. QMap<QString, DockWidget *> DockManager::dockWidgetsMap() const { return d->m_dockWidgetsMap; }
  473. bool DockManager::isRestoringState() const { return d->m_restoringState; }
  474. void DockManager::showWorkspaceMananger()
  475. {
  476. // Save current workspace
  477. save();
  478. WorkspaceDialog workspaceDialog(this, parentWidget());
  479. workspaceDialog.setAutoLoadWorkspace(autoRestorLastWorkspace());
  480. workspaceDialog.exec();
  481. QTC_ASSERT(d->m_settings, return );
  482. d->m_settings->setValue(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY,
  483. workspaceDialog.autoLoadWorkspace());
  484. }
  485. bool DockManager::isFactoryDefaultWorkspace(const QString &workspace) const
  486. {
  487. return workspace == QLatin1String(Constants::FACTORY_DEFAULT_NAME);
  488. }
  489. bool DockManager::isDefaultWorkspace(const QString &workspace) const
  490. {
  491. return workspace == QLatin1String(Constants::DEFAULT_NAME);
  492. }
  493. bool DockManager::save()
  494. {
  495. if (isFactoryDefaultWorkspace(activeWorkspace()))
  496. return true;
  497. emit aboutToSaveWorkspace();
  498. bool result = write(saveState(), parentWidget());
  499. if (result) {
  500. d->m_workspaceDateTimes.insert(activeWorkspace(), QDateTime::currentDateTime());
  501. } else {
  502. QMessageBox::warning(parentWidget(),
  503. tr("Cannot Save Session"),
  504. tr("Could not save session to file %1")
  505. .arg(workspaceNameToFileName(d->m_workspaceName)));
  506. }
  507. return result;
  508. }
  509. QString DockManager::activeWorkspace() const { return d->m_workspaceName; }
  510. QString DockManager::lastWorkspace() const
  511. {
  512. QTC_ASSERT(d->m_settings, return {});
  513. return d->m_settings->value(Constants::STARTUP_WORKSPACE_SETTINGS_KEY).toString();
  514. }
  515. bool DockManager::autoRestorLastWorkspace() const
  516. {
  517. QTC_ASSERT(d->m_settings, return false);
  518. return d->m_settings->value(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY).toBool();
  519. }
  520. const QString m_dirName = QLatin1String("workspaces");
  521. const QString m_fileExt = QLatin1String(".wrk"); // TODO
  522. QStringList DockManager::workspaces()
  523. {
  524. if (d->m_workspaces.isEmpty() || d->m_workspaceListDirty) {
  525. auto tmp = QSet<QString>::fromList(d->m_workspaces);
  526. QTC_ASSERT(d->m_settings, return {});
  527. QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/')
  528. + m_dirName);
  529. QFileInfoList workspaceFiles
  530. = workspaceDir.entryInfoList(QStringList() << QLatin1String("*.wrk"),
  531. QDir::NoFilter,
  532. QDir::Time); // TODO Choose different extension
  533. for (const QFileInfo &fileInfo : workspaceFiles) {
  534. QString filename = fileInfo.completeBaseName();
  535. filename.replace("_", " ");
  536. d->m_workspaceDateTimes.insert(filename, fileInfo.lastModified());
  537. //if (name != QLatin1String(Constants::DEFAULT_NAME))
  538. tmp.insert(filename);
  539. }
  540. //d->m_workspaces.prepend(QLatin1String(Constants::DEFAULT_NAME));
  541. d->m_workspaceListDirty = false;
  542. d->m_workspaces = tmp.toList();
  543. }
  544. return d->m_workspaces;
  545. }
  546. QDateTime DockManager::workspaceDateTime(const QString &workspace) const
  547. {
  548. return d->m_workspaceDateTimes.value(workspace);
  549. }
  550. QString DockManager::workspaceNameToFileName(const QString &workspaceName) const
  551. {
  552. QTC_ASSERT(d->m_settings, return {});
  553. QString workspaceNameCopy = workspaceName;
  554. return (QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName
  555. + QLatin1Char('/') + workspaceNameCopy.replace(" ", "_") + QLatin1String(".wrk"));
  556. }
  557. /**
  558. * Creates \a workspace, but does not actually create the file.
  559. */
  560. bool DockManager::createWorkspace(const QString &workspace)
  561. {
  562. if (workspaces().contains(workspace))
  563. return false;
  564. d->m_workspaces.insert(1, workspace);
  565. d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime());
  566. emit workspaceListChanged();
  567. return true;
  568. }
  569. bool DockManager::openWorkspace(const QString &workspace)
  570. {
  571. // Do nothing if we have that workspace already loaded, exception if the
  572. // workspace is the default virgin workspace we still want to be able to
  573. // load the default workspace.
  574. if (workspace == d->m_workspaceName) // && !isFactoryDefaultWorkspace(workspace))
  575. return true;
  576. if (!workspaces().contains(workspace))
  577. return false;
  578. // Check if the currently active workspace isn't empty and try to save it
  579. if (!d->m_workspaceName.isEmpty()) {
  580. // Allow everyone to set something in the workspace and before saving
  581. emit aboutToUnloadWorkspace(d->m_workspaceName);
  582. if (!save()) {
  583. return false;
  584. }
  585. }
  586. // Try loading the file
  587. QByteArray data;
  588. QString fileName = workspaceNameToFileName(workspace);
  589. if (QFileInfo(fileName).exists()) {
  590. QFile file(fileName);
  591. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  592. QMessageBox::warning(parentWidget(),
  593. tr("Cannot Restore Workspace"),
  594. tr("Could not restore workspace %1").arg(fileName));
  595. return false;
  596. }
  597. data = file.readAll();
  598. file.close();
  599. }
  600. emit openingWorkspace(workspace);
  601. // If data was loaded from file try to restore its state
  602. if (!data.isNull() && !restoreState(data)) {
  603. return false;
  604. }
  605. d->m_workspaceName = workspace;
  606. emit workspaceLoaded(workspace);
  607. return true;
  608. }
  609. /**
  610. * \brief Shows a dialog asking the user to confirm deleting the workspace \p workspace
  611. */
  612. bool DockManager::confirmWorkspaceDelete(const QStringList &workspace)
  613. {
  614. const QString title = workspace.size() == 1 ? tr("Delete Workspace")
  615. : tr("Delete Workspaces");
  616. const QString question = workspace.size() == 1
  617. ? tr("Delete workspace %1?").arg(workspace.first())
  618. : tr("Delete these workspaces?\n %1")
  619. .arg(workspace.join("\n "));
  620. return QMessageBox::question(parentWidget(),
  621. title,
  622. question,
  623. QMessageBox::Yes | QMessageBox::No)
  624. == QMessageBox::Yes;
  625. }
  626. /**
  627. * Deletes \a workspace name from workspace list and the file from disk.
  628. */
  629. bool DockManager::deleteWorkspace(const QString &workspace)
  630. {
  631. // Remove workspace from internal list
  632. if (!d->m_workspaces.contains(workspace))
  633. return false;
  634. d->m_workspaces.removeOne(workspace);
  635. emit workspacesRemoved();
  636. emit workspaceListChanged();
  637. // Remove corresponding workspace file
  638. QFile fi(workspaceNameToFileName(workspace));
  639. if (fi.exists())
  640. return fi.remove();
  641. return false; // TODO If we allow temporary workspaces without writing them to file
  642. // directly, this needs to be true otherwise in all those cases it will return false.
  643. }
  644. void DockManager::deleteWorkspaces(const QStringList &workspaces)
  645. {
  646. for (const QString &workspace : workspaces)
  647. deleteWorkspace(workspace);
  648. }
  649. bool DockManager::cloneWorkspace(const QString &original, const QString &clone)
  650. {
  651. if (!d->m_workspaces.contains(original))
  652. return false;
  653. QFile fi(workspaceNameToFileName(original));
  654. // If the file does not exist, we can still clone
  655. if (!fi.exists() || fi.copy(workspaceNameToFileName(clone))) {
  656. d->m_workspaces.insert(1, clone);
  657. d->m_workspaceDateTimes.insert(clone,
  658. QFileInfo(workspaceNameToFileName(clone)).lastModified());
  659. return true;
  660. }
  661. return false;
  662. }
  663. bool DockManager::renameWorkspace(const QString &original, const QString &newName)
  664. {
  665. if (!cloneWorkspace(original, newName))
  666. return false;
  667. if (original == activeWorkspace())
  668. openWorkspace(newName);
  669. return deleteWorkspace(original);
  670. }
  671. bool DockManager::write(const QByteArray &data, QString *errorString) const
  672. {
  673. QString fileName = workspaceNameToFileName(activeWorkspace());
  674. QDir tmp;
  675. tmp.mkpath(QFileInfo(fileName).path());
  676. QFile fileSaver(fileName);
  677. if (fileSaver.open(QFile::WriteOnly | QIODevice::Text)) {
  678. fileSaver.write(data);
  679. fileSaver.close();
  680. return true;
  681. } else {
  682. *errorString = fileSaver.errorString();
  683. return false;
  684. }
  685. }
  686. #ifdef QT_GUI_LIB
  687. bool DockManager::write(const QByteArray &data, QWidget *parent) const
  688. {
  689. QString errorString;
  690. const bool success = write(data, &errorString);
  691. if (!success)
  692. QMessageBox::critical(parent,
  693. QCoreApplication::translate("Utils::FileSaverBase", "File Error"),
  694. errorString);
  695. return success;
  696. }
  697. #endif // QT_GUI_LIB
  698. } // namespace ADS