2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2020 Uwe Kindler
|
2023-01-04 08:19:47 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
#include "dockmanager.h"
|
|
|
|
|
|
|
|
|
|
#include "ads_globals.h"
|
|
|
|
|
#include "dockareawidget.h"
|
2020-06-22 16:46:25 +02:00
|
|
|
#include "dockfocuscontroller.h"
|
2020-01-24 17:13:32 +01:00
|
|
|
#include "dockingstatereader.h"
|
|
|
|
|
#include "dockoverlay.h"
|
|
|
|
|
#include "dockwidget.h"
|
|
|
|
|
#include "floatingdockcontainer.h"
|
|
|
|
|
#include "iconprovider.h"
|
|
|
|
|
|
|
|
|
|
#include "workspacedialog.h"
|
|
|
|
|
|
2020-02-21 10:45:31 +01:00
|
|
|
#include <utils/algorithm.h>
|
2022-05-24 12:14:24 +02:00
|
|
|
#include <utils/fileutils.h>
|
2020-01-24 17:13:32 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
#include <QAction>
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QDateTime>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QList>
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
#include <QMainWindow>
|
|
|
|
|
#include <QMap>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QSettings>
|
|
|
|
|
#include <QVariant>
|
|
|
|
|
#include <QXmlStreamWriter>
|
|
|
|
|
|
2023-01-11 13:45:33 +01:00
|
|
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg);
|
|
|
|
|
|
|
|
|
|
using namespace Utils;
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
namespace ADS
|
|
|
|
|
{
|
2020-06-22 16:46:25 +02:00
|
|
|
/**
|
|
|
|
|
* Internal file version in case the structure changes internally
|
|
|
|
|
*/
|
|
|
|
|
enum eStateFileVersion {
|
|
|
|
|
InitialVersion = 0, //!< InitialVersion
|
|
|
|
|
Version1 = 1, //!< Version1
|
|
|
|
|
CurrentVersion = Version1 //!< CurrentVersion
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Private data class of DockManager class (pimpl)
|
|
|
|
|
*/
|
2020-03-02 10:43:54 +01:00
|
|
|
class DockManagerPrivate
|
2020-01-24 17:13:32 +01:00
|
|
|
{
|
2020-03-02 10:43:54 +01:00
|
|
|
public:
|
2020-01-24 17:13:32 +01:00
|
|
|
DockManager *q;
|
2020-12-17 12:00:12 +01:00
|
|
|
QList<QPointer<FloatingDockContainer>> m_floatingWidgets;
|
2020-01-24 17:13:32 +01:00
|
|
|
QList<DockContainerWidget *> m_containers;
|
2020-03-02 10:45:42 +01:00
|
|
|
DockOverlay *m_containerOverlay = nullptr;
|
|
|
|
|
DockOverlay *m_dockAreaOverlay = nullptr;
|
2020-01-24 17:13:32 +01:00
|
|
|
QMap<QString, DockWidget *> m_dockWidgetsMap;
|
|
|
|
|
bool m_restoringState = false;
|
|
|
|
|
QVector<FloatingDockContainer *> m_uninitializedFloatingWidgets;
|
2020-06-22 16:46:25 +02:00
|
|
|
DockFocusController *m_focusController = nullptr;
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
QString m_workspaceName;
|
|
|
|
|
bool m_workspaceListDirty = true;
|
|
|
|
|
QStringList m_workspaces;
|
2020-03-02 15:13:20 +01:00
|
|
|
QSet<QString> m_workspacePresets;
|
2020-01-24 17:13:32 +01:00
|
|
|
QHash<QString, QDateTime> m_workspaceDateTimes;
|
|
|
|
|
QString m_workspaceToRestoreAtStartup;
|
|
|
|
|
bool m_autorestoreLastWorkspace; // This option is set in the Workspace Manager!
|
2020-03-02 10:45:42 +01:00
|
|
|
QSettings *m_settings = nullptr;
|
2020-03-02 15:13:20 +01:00
|
|
|
QString m_workspacePresetsPath;
|
2020-06-25 11:04:06 +02:00
|
|
|
bool m_modeChangeState = false;
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Private data constructor
|
|
|
|
|
*/
|
|
|
|
|
DockManagerPrivate(DockManager *parent);
|
|
|
|
|
|
|
|
|
|
/**
|
2020-05-06 12:45:15 +02:00
|
|
|
* Restores the state. If testing is set to true it will check if
|
|
|
|
|
* the given data stream is a valid docking system state file.
|
2020-01-24 17:13:32 +01:00
|
|
|
*/
|
|
|
|
|
bool restoreStateFromXml(const QByteArray &state,
|
|
|
|
|
int version,
|
2020-05-06 12:45:15 +02:00
|
|
|
bool testing = false);
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Restore state
|
|
|
|
|
*/
|
|
|
|
|
bool restoreState(const QByteArray &state, int version);
|
|
|
|
|
|
|
|
|
|
void restoreDockWidgetsOpenState();
|
|
|
|
|
void restoreDockAreasIndices();
|
|
|
|
|
void emitTopLevelEvents();
|
|
|
|
|
|
|
|
|
|
void hideFloatingWidgets()
|
|
|
|
|
{
|
|
|
|
|
// Hide updates of floating widgets from user
|
2022-11-30 08:49:07 +01:00
|
|
|
for (const auto &floatingWidget : std::as_const(m_floatingWidgets)) {
|
|
|
|
|
if (floatingWidget)
|
|
|
|
|
floatingWidget->hide();
|
|
|
|
|
}
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void markDockWidgetsDirty()
|
|
|
|
|
{
|
2022-11-30 08:49:07 +01:00
|
|
|
for (const auto &dockWidget : std::as_const(m_dockWidgetsMap))
|
2020-01-24 17:13:32 +01:00
|
|
|
dockWidget->setProperty("dirty", true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Restores the container with the given index
|
|
|
|
|
*/
|
|
|
|
|
bool restoreContainer(int index, DockingStateReader &stream, bool testing);
|
|
|
|
|
|
|
|
|
|
void workspaceLoadingProgress();
|
2020-03-02 10:43:54 +01:00
|
|
|
}; // class DockManagerPrivate
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
DockManagerPrivate::DockManagerPrivate(DockManager *parent)
|
|
|
|
|
: q(parent)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
bool DockManagerPrivate::restoreContainer(int index, DockingStateReader &stream, bool testing)
|
|
|
|
|
{
|
2020-03-02 15:13:20 +01:00
|
|
|
if (testing)
|
2020-01-24 17:13:32 +01:00
|
|
|
index = 0;
|
|
|
|
|
|
|
|
|
|
bool result = false;
|
|
|
|
|
if (index >= m_containers.count()) {
|
|
|
|
|
FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
|
|
|
|
|
result = floatingWidget->restoreState(stream, testing);
|
|
|
|
|
} else {
|
|
|
|
|
qCInfo(adsLog) << "d->m_containers[i]->restoreState ";
|
|
|
|
|
auto container = m_containers[index];
|
2020-06-22 16:46:25 +02:00
|
|
|
if (container->isFloating())
|
2020-01-24 17:13:32 +01:00
|
|
|
result = container->floatingWidget()->restoreState(stream, testing);
|
2020-06-22 16:46:25 +02:00
|
|
|
else
|
2020-01-24 17:13:32 +01:00
|
|
|
result = container->restoreState(stream, testing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int version, bool testing)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(version) // TODO version is not needed, why is it in here in the first place?
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
if (state.isEmpty())
|
2020-01-24 17:13:32 +01:00
|
|
|
return false;
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
DockingStateReader stateReader(state);
|
2020-03-03 16:51:35 +01:00
|
|
|
if (!stateReader.readNextStartElement())
|
|
|
|
|
return false;
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-09-28 17:29:50 +02:00
|
|
|
if (stateReader.name() != QLatin1String("QtAdvancedDockingSystem"))
|
2020-01-24 17:13:32 +01:00
|
|
|
return false;
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
qCInfo(adsLog) << stateReader.attributes().value("version");
|
|
|
|
|
bool ok;
|
|
|
|
|
int v = stateReader.attributes().value("version").toInt(&ok);
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!ok || v > CurrentVersion)
|
2020-01-24 17:13:32 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
stateReader.setFileVersion(v);
|
2020-06-22 16:46:25 +02:00
|
|
|
|
|
|
|
|
qCInfo(adsLog) << stateReader.attributes().value("userVersion");
|
|
|
|
|
// Older files do not support UserVersion but we still want to load them so
|
|
|
|
|
// we first test if the attribute exists
|
|
|
|
|
if (!stateReader.attributes().value("userVersion").isEmpty())
|
|
|
|
|
{
|
|
|
|
|
v = stateReader.attributes().value("userVersion").toInt(&ok);
|
|
|
|
|
if (!ok || v != version)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
bool result = true;
|
|
|
|
|
#ifdef ADS_DEBUG_PRINT
|
|
|
|
|
int dockContainers = stateReader.attributes().value("containers").toInt();
|
|
|
|
|
qCInfo(adsLog) << dockContainers;
|
|
|
|
|
#endif
|
|
|
|
|
int dockContainerCount = 0;
|
|
|
|
|
while (stateReader.readNextStartElement()) {
|
2020-09-28 17:29:50 +02:00
|
|
|
if (stateReader.name() == QLatin1String("container")) {
|
2020-01-24 17:13:32 +01:00
|
|
|
result = restoreContainer(dockContainerCount, stateReader, testing);
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!result)
|
2020-01-24 17:13:32 +01:00
|
|
|
break;
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
dockContainerCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!testing) {
|
|
|
|
|
// Delete remaining empty floating widgets
|
|
|
|
|
int floatingWidgetIndex = dockContainerCount - 1;
|
|
|
|
|
int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex;
|
|
|
|
|
for (int i = 0; i < deleteCount; ++i) {
|
|
|
|
|
m_floatingWidgets[floatingWidgetIndex + i]->deleteLater();
|
|
|
|
|
q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->dockContainer());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManagerPrivate::restoreDockWidgetsOpenState()
|
|
|
|
|
{
|
|
|
|
|
// All dock widgets, that have not been processed in the restore state
|
|
|
|
|
// function are invisible to the user now and have no assigned dock area
|
|
|
|
|
// They do not belong to any dock container, until the user toggles the
|
|
|
|
|
// toggle view action the next time
|
2022-10-07 14:46:06 +02:00
|
|
|
for (auto dockWidget : std::as_const(m_dockWidgetsMap)) {
|
2020-01-24 17:13:32 +01:00
|
|
|
if (dockWidget->property(internal::dirtyProperty).toBool()) {
|
|
|
|
|
dockWidget->flagAsUnassigned();
|
|
|
|
|
emit dockWidget->viewToggled(false);
|
|
|
|
|
} else {
|
|
|
|
|
dockWidget->toggleViewInternal(
|
|
|
|
|
!dockWidget->property(internal::closedProperty).toBool());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManagerPrivate::restoreDockAreasIndices()
|
|
|
|
|
{
|
|
|
|
|
// Now all dock areas are properly restored and we setup the index of
|
|
|
|
|
// The dock areas because the previous toggleView() action has changed
|
|
|
|
|
// the dock area index
|
|
|
|
|
int count = 0;
|
2022-10-07 14:46:06 +02:00
|
|
|
for (auto dockContainer : std::as_const(m_containers)) {
|
2020-01-24 17:13:32 +01:00
|
|
|
count++;
|
|
|
|
|
for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
|
|
|
|
|
DockAreaWidget *dockArea = dockContainer->dockArea(i);
|
|
|
|
|
QString dockWidgetName = dockArea->property("currentDockWidget").toString();
|
|
|
|
|
DockWidget *dockWidget = nullptr;
|
2020-06-22 16:46:25 +02:00
|
|
|
if (!dockWidgetName.isEmpty())
|
2020-01-24 17:13:32 +01:00
|
|
|
dockWidget = q->findDockWidget(dockWidgetName);
|
|
|
|
|
|
|
|
|
|
if (!dockWidget || dockWidget->isClosed()) {
|
|
|
|
|
int index = dockArea->indexOfFirstOpenDockWidget();
|
2020-03-02 15:13:20 +01:00
|
|
|
if (index < 0)
|
2020-01-24 17:13:32 +01:00
|
|
|
continue;
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
dockArea->setCurrentIndex(index);
|
|
|
|
|
} else {
|
|
|
|
|
dockArea->internalSetCurrentDockWidget(dockWidget);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManagerPrivate::emitTopLevelEvents()
|
|
|
|
|
{
|
|
|
|
|
// Finally we need to send the topLevelChanged() signals for all dock
|
|
|
|
|
// widgets if top level changed
|
2022-10-07 14:46:06 +02:00
|
|
|
for (auto dockContainer : std::as_const(m_containers)) {
|
2020-01-24 17:13:32 +01:00
|
|
|
DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget();
|
|
|
|
|
if (topLevelDockWidget) {
|
|
|
|
|
topLevelDockWidget->emitTopLevelChanged(true);
|
|
|
|
|
} else {
|
|
|
|
|
for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
|
|
|
|
|
auto dockArea = dockContainer->dockArea(i);
|
2020-06-22 16:46:25 +02:00
|
|
|
for (auto dockWidget : dockArea->dockWidgets())
|
2020-01-24 17:13:32 +01:00
|
|
|
dockWidget->emitTopLevelChanged(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
|
|
|
|
|
{
|
|
|
|
|
QByteArray currentState = state.startsWith("<?xml") ? state : qUncompress(state);
|
2020-05-06 12:45:15 +02:00
|
|
|
// Check the format of the given data stream
|
|
|
|
|
if (!restoreStateFromXml(currentState, version, true)) {
|
2020-01-24 17:13:32 +01:00
|
|
|
qCInfo(adsLog) << "checkFormat: Error checking format!!!";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hide updates of floating widgets from use
|
|
|
|
|
hideFloatingWidgets();
|
|
|
|
|
markDockWidgetsDirty();
|
|
|
|
|
|
|
|
|
|
if (!restoreStateFromXml(currentState, version)) {
|
|
|
|
|
qCInfo(adsLog) << "restoreState: Error restoring state!!!";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
restoreDockWidgetsOpenState();
|
|
|
|
|
restoreDockAreasIndices();
|
|
|
|
|
emitTopLevelEvents();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DockManager::DockManager(QWidget *parent)
|
|
|
|
|
: DockContainerWidget(this, parent)
|
|
|
|
|
, d(new DockManagerPrivate(this))
|
|
|
|
|
{
|
|
|
|
|
connect(this, &DockManager::workspaceListChanged, this, [=] {
|
|
|
|
|
d->m_workspaceListDirty = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createRootSplitter();
|
|
|
|
|
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent);
|
|
|
|
|
if (mainWindow) {
|
|
|
|
|
mainWindow->setCentralWidget(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d->m_dockAreaOverlay = new DockOverlay(this, DockOverlay::ModeDockAreaOverlay);
|
|
|
|
|
d->m_containerOverlay = new DockOverlay(this, DockOverlay::ModeContainerOverlay);
|
|
|
|
|
d->m_containers.append(this);
|
2020-06-22 16:46:25 +02:00
|
|
|
|
|
|
|
|
if (DockManager::configFlags().testFlag(DockManager::FocusHighlighting))
|
|
|
|
|
d->m_focusController = new DockFocusController(this);
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DockManager::~DockManager()
|
|
|
|
|
{
|
|
|
|
|
emit aboutToUnloadWorkspace(d->m_workspaceName);
|
|
|
|
|
save();
|
2020-03-02 15:13:20 +01:00
|
|
|
saveStartupWorkspace();
|
2020-01-24 17:13:32 +01:00
|
|
|
|
2021-06-09 16:00:51 +02:00
|
|
|
// Using a temporal vector since the destructor of
|
|
|
|
|
// FloatingDockWidgetContainer alters d->m_floatingWidgets.
|
2022-11-30 08:49:07 +01:00
|
|
|
const auto copy = d->m_floatingWidgets;
|
|
|
|
|
for (const auto &floatingWidget : copy) {
|
2021-06-09 16:00:51 +02:00
|
|
|
if (floatingWidget)
|
2022-11-30 08:49:07 +01:00
|
|
|
delete floatingWidget.get();
|
2021-06-09 16:00:51 +02:00
|
|
|
}
|
2020-01-24 17:13:32 +01:00
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DockManager::ConfigFlags DockManager::configFlags() { return g_staticConfigFlags; }
|
|
|
|
|
|
|
|
|
|
void DockManager::setConfigFlags(const ConfigFlags flags) { g_staticConfigFlags = flags; }
|
|
|
|
|
|
|
|
|
|
void DockManager::setConfigFlag(eConfigFlag flag, bool on)
|
|
|
|
|
{
|
|
|
|
|
internal::setFlag(g_staticConfigFlags, flag, on);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::testConfigFlag(eConfigFlag flag)
|
|
|
|
|
{
|
|
|
|
|
return configFlags().testFlag(flag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IconProvider &DockManager::iconProvider()
|
|
|
|
|
{
|
|
|
|
|
static IconProvider instance;
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int DockManager::startDragDistance()
|
|
|
|
|
{
|
|
|
|
|
return static_cast<int>(QApplication::startDragDistance() * 1.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::setSettings(QSettings *settings) { d->m_settings = settings; }
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
void DockManager::setWorkspacePresetsPath(const QString &path) { d->m_workspacePresetsPath = path; }
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area,
|
|
|
|
|
DockWidget *dockWidget,
|
|
|
|
|
DockAreaWidget *dockAreaWidget)
|
|
|
|
|
{
|
|
|
|
|
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
|
|
|
|
return DockContainerWidget::addDockWidget(area, dockWidget, dockAreaWidget);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
void DockManager::initialize()
|
|
|
|
|
{
|
|
|
|
|
syncWorkspacePresets();
|
|
|
|
|
|
|
|
|
|
QString workspace = ADS::Constants::DEFAULT_WORKSPACE;
|
|
|
|
|
|
|
|
|
|
// Determine workspace to restore at startup
|
|
|
|
|
if (autoRestorLastWorkspace()) {
|
|
|
|
|
QString lastWS = lastWorkspace();
|
|
|
|
|
if (!lastWS.isEmpty() && workspaces().contains(lastWS))
|
|
|
|
|
workspace = lastWS;
|
|
|
|
|
else
|
|
|
|
|
qDebug() << "Couldn't restore last workspace!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
openWorkspace(workspace);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget)
|
|
|
|
|
{
|
|
|
|
|
DockAreaWidget *areaWidget = lastAddedDockAreaWidget(area);
|
2020-06-22 16:46:25 +02:00
|
|
|
if (areaWidget)
|
2020-01-24 17:13:32 +01:00
|
|
|
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget);
|
2020-06-22 16:46:25 +02:00
|
|
|
else if (!openedDockAreas().isEmpty())
|
2020-11-16 21:58:53 +01:00
|
|
|
return addDockWidget(area, dockWidget, openedDockAreas().constLast());
|
2020-06-22 16:46:25 +02:00
|
|
|
else
|
2020-01-24 17:13:32 +01:00
|
|
|
return addDockWidget(area, dockWidget, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget,
|
|
|
|
|
DockAreaWidget *dockAreaWidget)
|
|
|
|
|
{
|
|
|
|
|
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget)
|
|
|
|
|
{
|
|
|
|
|
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
|
|
|
|
DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget();
|
2020-03-02 15:13:20 +01:00
|
|
|
if (oldDockArea)
|
2020-01-24 17:13:32 +01:00
|
|
|
oldDockArea->removeDockWidget(dockWidget);
|
|
|
|
|
|
|
|
|
|
dockWidget->setDockManager(this);
|
|
|
|
|
FloatingDockContainer *floatingWidget = new FloatingDockContainer(dockWidget);
|
|
|
|
|
floatingWidget->resize(dockWidget->size());
|
2020-06-22 16:46:25 +02:00
|
|
|
if (isVisible())
|
2020-01-24 17:13:32 +01:00
|
|
|
floatingWidget->show();
|
2020-06-22 16:46:25 +02:00
|
|
|
else
|
2020-01-24 17:13:32 +01:00
|
|
|
d->m_uninitializedFloatingWidgets.append(floatingWidget);
|
2020-06-22 16:46:25 +02:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
return floatingWidget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget)
|
|
|
|
|
{
|
|
|
|
|
d->m_floatingWidgets.append(floatingWidget);
|
|
|
|
|
emit floatingWidgetCreated(floatingWidget);
|
|
|
|
|
qCInfo(adsLog) << "d->FloatingWidgets.count() " << d->m_floatingWidgets.count();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::removeFloatingWidget(FloatingDockContainer *floatingWidget)
|
|
|
|
|
{
|
|
|
|
|
d->m_floatingWidgets.removeAll(floatingWidget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::registerDockContainer(DockContainerWidget *dockContainer)
|
|
|
|
|
{
|
|
|
|
|
d->m_containers.append(dockContainer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::removeDockContainer(DockContainerWidget *dockContainer)
|
|
|
|
|
{
|
2020-06-22 16:46:25 +02:00
|
|
|
if (this != dockContainer)
|
2020-01-24 17:13:32 +01:00
|
|
|
d->m_containers.removeAll(dockContainer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DockOverlay *DockManager::containerOverlay() const { return d->m_containerOverlay; }
|
|
|
|
|
|
|
|
|
|
DockOverlay *DockManager::dockAreaOverlay() const { return d->m_dockAreaOverlay; }
|
|
|
|
|
|
|
|
|
|
const QList<DockContainerWidget *> DockManager::dockContainers() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_containers;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-17 12:00:12 +01:00
|
|
|
const QList<QPointer<FloatingDockContainer>> DockManager::floatingWidgets() const
|
2020-01-24 17:13:32 +01:00
|
|
|
{
|
|
|
|
|
return d->m_floatingWidgets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int DockManager::zOrderIndex() const { return 0; }
|
|
|
|
|
|
|
|
|
|
QByteArray DockManager::saveState(int version) const
|
|
|
|
|
{
|
|
|
|
|
QByteArray xmlData;
|
|
|
|
|
QXmlStreamWriter stream(&xmlData);
|
|
|
|
|
auto configFlags = DockManager::configFlags();
|
|
|
|
|
stream.setAutoFormatting(configFlags.testFlag(XmlAutoFormattingEnabled));
|
|
|
|
|
stream.writeStartDocument();
|
|
|
|
|
stream.writeStartElement("QtAdvancedDockingSystem");
|
2020-06-22 16:46:25 +02:00
|
|
|
stream.writeAttribute("version", QString::number(CurrentVersion));
|
|
|
|
|
stream.writeAttribute("userVersion", QString::number(version));
|
2020-01-24 17:13:32 +01:00
|
|
|
stream.writeAttribute("containers", QString::number(d->m_containers.count()));
|
2022-10-07 14:46:06 +02:00
|
|
|
for (auto container : std::as_const(d->m_containers))
|
2020-01-24 17:13:32 +01:00
|
|
|
container->saveState(stream);
|
|
|
|
|
|
|
|
|
|
stream.writeEndElement();
|
|
|
|
|
stream.writeEndDocument();
|
|
|
|
|
return xmlData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::restoreState(const QByteArray &state, int version)
|
|
|
|
|
{
|
|
|
|
|
// Prevent multiple calls as long as state is not restore. This may
|
|
|
|
|
// happen, if QApplication::processEvents() is called somewhere
|
2020-03-02 15:13:20 +01:00
|
|
|
if (d->m_restoringState)
|
2020-01-24 17:13:32 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// We hide the complete dock manager here. Restoring the state means
|
|
|
|
|
// that DockWidgets are removed from the DockArea internal stack layout
|
|
|
|
|
// which in turn means, that each time a widget is removed the stack
|
|
|
|
|
// will show and raise the next available widget which in turn
|
|
|
|
|
// triggers show events for the dock widgets. To avoid this we hide the
|
|
|
|
|
// dock manager. Because there will be no processing of application
|
|
|
|
|
// events until this function is finished, the user will not see this
|
|
|
|
|
// hiding
|
|
|
|
|
bool isHidden = this->isHidden();
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!isHidden)
|
2020-01-24 17:13:32 +01:00
|
|
|
hide();
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
d->m_restoringState = true;
|
|
|
|
|
emit restoringState();
|
|
|
|
|
bool result = d->restoreState(state, version);
|
|
|
|
|
d->m_restoringState = false;
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!isHidden)
|
2020-01-24 17:13:32 +01:00
|
|
|
show();
|
|
|
|
|
|
2020-06-22 16:46:25 +02:00
|
|
|
emit stateRestored();
|
2020-01-24 17:13:32 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::showEvent(QShowEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Super::showEvent(event);
|
2020-03-02 15:13:20 +01:00
|
|
|
if (d->m_uninitializedFloatingWidgets.empty())
|
2020-01-24 17:13:32 +01:00
|
|
|
return;
|
|
|
|
|
|
2022-10-07 14:46:06 +02:00
|
|
|
for (auto floatingWidget : std::as_const(d->m_uninitializedFloatingWidgets))
|
2020-01-24 17:13:32 +01:00
|
|
|
floatingWidget->show();
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
d->m_uninitializedFloatingWidgets.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DockWidget *DockManager::findDockWidget(const QString &objectName) const
|
|
|
|
|
{
|
|
|
|
|
return d->m_dockWidgetsMap.value(objectName, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::removeDockWidget(DockWidget *dockWidget)
|
|
|
|
|
{
|
|
|
|
|
emit dockWidgetAboutToBeRemoved(dockWidget);
|
|
|
|
|
d->m_dockWidgetsMap.remove(dockWidget->objectName());
|
|
|
|
|
DockContainerWidget::removeDockWidget(dockWidget);
|
|
|
|
|
emit dockWidgetRemoved(dockWidget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QMap<QString, DockWidget *> DockManager::dockWidgetsMap() const { return d->m_dockWidgetsMap; }
|
|
|
|
|
|
|
|
|
|
bool DockManager::isRestoringState() const { return d->m_restoringState; }
|
|
|
|
|
|
|
|
|
|
void DockManager::showWorkspaceMananger()
|
|
|
|
|
{
|
2020-03-02 15:13:20 +01:00
|
|
|
save(); // Save current workspace
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
WorkspaceDialog workspaceDialog(this, parentWidget());
|
|
|
|
|
workspaceDialog.setAutoLoadWorkspace(autoRestorLastWorkspace());
|
|
|
|
|
workspaceDialog.exec();
|
|
|
|
|
|
|
|
|
|
QTC_ASSERT(d->m_settings, return );
|
|
|
|
|
d->m_settings->setValue(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY,
|
|
|
|
|
workspaceDialog.autoLoadWorkspace());
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
bool DockManager::isWorkspacePreset(const QString &workspace) const
|
2020-01-24 17:13:32 +01:00
|
|
|
{
|
2020-03-02 15:13:20 +01:00
|
|
|
return d->m_workspacePresets.contains(workspace);
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::save()
|
|
|
|
|
{
|
2020-03-09 08:30:17 +01:00
|
|
|
if (isModeChangeState())
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
emit aboutToSaveWorkspace();
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
bool result = write(activeWorkspace(), saveState(), parentWidget());
|
2020-06-22 16:46:25 +02:00
|
|
|
if (result)
|
2020-01-24 17:13:32 +01:00
|
|
|
d->m_workspaceDateTimes.insert(activeWorkspace(), QDateTime::currentDateTime());
|
2020-06-22 16:46:25 +02:00
|
|
|
else
|
2020-01-24 17:13:32 +01:00
|
|
|
QMessageBox::warning(parentWidget(),
|
2020-03-02 15:13:20 +01:00
|
|
|
tr("Cannot Save Workspace"),
|
|
|
|
|
tr("Could not save workspace to file %1")
|
2020-05-06 12:45:15 +02:00
|
|
|
.arg(workspaceNameToFilePath(d->m_workspaceName)
|
2020-01-24 17:13:32 +01:00
|
|
|
.toUserOutput()));
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DockManager::activeWorkspace() const { return d->m_workspaceName; }
|
|
|
|
|
|
|
|
|
|
QString DockManager::lastWorkspace() const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(d->m_settings, return {});
|
|
|
|
|
return d->m_settings->value(Constants::STARTUP_WORKSPACE_SETTINGS_KEY).toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::autoRestorLastWorkspace() const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(d->m_settings, return false);
|
|
|
|
|
return d->m_settings->value(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY).toBool();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString m_dirName = QLatin1String("workspaces");
|
|
|
|
|
const QString m_fileExt = QLatin1String(".wrk"); // TODO
|
|
|
|
|
|
2020-05-06 12:45:15 +02:00
|
|
|
QString DockManager::workspaceFileExtension() const { return m_fileExt; }
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
QStringList DockManager::workspaces()
|
|
|
|
|
{
|
|
|
|
|
if (d->m_workspaces.isEmpty() || d->m_workspaceListDirty) {
|
2020-02-21 10:45:31 +01:00
|
|
|
auto tmp = Utils::toSet(d->m_workspaces);
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
QTC_ASSERT(d->m_settings, return {});
|
|
|
|
|
QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/')
|
|
|
|
|
+ m_dirName);
|
|
|
|
|
QFileInfoList workspaceFiles
|
2020-05-06 12:45:15 +02:00
|
|
|
= workspaceDir.entryInfoList(QStringList() << QLatin1Char('*') + m_fileExt,
|
2020-01-24 17:13:32 +01:00
|
|
|
QDir::NoFilter,
|
2020-03-02 15:13:20 +01:00
|
|
|
QDir::Time);
|
2020-01-24 17:13:32 +01:00
|
|
|
for (const QFileInfo &fileInfo : workspaceFiles) {
|
2020-05-06 12:45:15 +02:00
|
|
|
QString workspaceName = fileNameToWorkspaceName(fileInfo.completeBaseName());
|
|
|
|
|
d->m_workspaceDateTimes.insert(workspaceName, fileInfo.lastModified());
|
|
|
|
|
tmp.insert(workspaceName);
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d->m_workspaceListDirty = false;
|
2020-02-21 10:45:31 +01:00
|
|
|
d->m_workspaces = Utils::toList(tmp);
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
return d->m_workspaces;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
QSet<QString> DockManager::workspacePresets() const
|
|
|
|
|
{
|
|
|
|
|
if (d->m_workspacePresets.isEmpty()) {
|
|
|
|
|
QDir workspacePresetsDir(d->m_workspacePresetsPath);
|
|
|
|
|
QFileInfoList workspacePresetsFiles
|
2020-05-06 12:45:15 +02:00
|
|
|
= workspacePresetsDir.entryInfoList(QStringList() << QLatin1Char('*') + m_fileExt,
|
2020-03-02 15:13:20 +01:00
|
|
|
QDir::NoFilter,
|
|
|
|
|
QDir::Time);
|
2020-06-22 16:46:25 +02:00
|
|
|
for (const QFileInfo &fileInfo : workspacePresetsFiles)
|
2020-05-06 12:45:15 +02:00
|
|
|
d->m_workspacePresets.insert(fileNameToWorkspaceName(fileInfo.completeBaseName()));
|
2020-03-02 15:13:20 +01:00
|
|
|
}
|
|
|
|
|
return d->m_workspacePresets;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
QDateTime DockManager::workspaceDateTime(const QString &workspace) const
|
|
|
|
|
{
|
|
|
|
|
return d->m_workspaceDateTimes.value(workspace);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-11 13:45:33 +01:00
|
|
|
FilePath DockManager::workspaceNameToFilePath(const QString &workspaceName) const
|
2020-01-24 17:13:32 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(d->m_settings, return {});
|
2023-01-11 13:45:33 +01:00
|
|
|
return FilePath::fromString(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/')
|
|
|
|
|
+ m_dirName + QLatin1Char('/')
|
|
|
|
|
+ workspaceNameToFileName(workspaceName));
|
2020-05-06 12:45:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DockManager::fileNameToWorkspaceName(const QString &fileName) const
|
|
|
|
|
{
|
|
|
|
|
QString copy = QFileInfo(fileName).baseName();
|
|
|
|
|
copy.replace("_", " ");
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DockManager::workspaceNameToFileName(const QString &workspaceName) const
|
|
|
|
|
{
|
|
|
|
|
QString copy = workspaceName;
|
|
|
|
|
copy.replace(" ", "_");
|
|
|
|
|
copy.append(m_fileExt);
|
|
|
|
|
return copy;
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates \a workspace, but does not actually create the file.
|
|
|
|
|
*/
|
|
|
|
|
bool DockManager::createWorkspace(const QString &workspace)
|
|
|
|
|
{
|
|
|
|
|
if (workspaces().contains(workspace))
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
bool result = write(workspace, saveState(), parentWidget());
|
|
|
|
|
if (result) {
|
|
|
|
|
d->m_workspaces.insert(1, workspace);
|
|
|
|
|
d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime());
|
|
|
|
|
emit workspaceListChanged();
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::warning(parentWidget(),
|
|
|
|
|
tr("Cannot Save Workspace"),
|
|
|
|
|
tr("Could not save workspace to file %1")
|
2020-05-06 12:45:15 +02:00
|
|
|
.arg(workspaceNameToFilePath(d->m_workspaceName)
|
2020-03-02 15:13:20 +01:00
|
|
|
.toUserOutput()));
|
|
|
|
|
}
|
2020-01-24 17:13:32 +01:00
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
return result;
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::openWorkspace(const QString &workspace)
|
|
|
|
|
{
|
2020-06-25 11:04:06 +02:00
|
|
|
// Do nothing if we have that workspace already loaded, exception if it is
|
|
|
|
|
// a preset workspace. In this case we still want to be able to load the
|
|
|
|
|
// default workspace to undo potential user changes.
|
2020-03-02 15:13:20 +01:00
|
|
|
if (workspace == d->m_workspaceName && !isWorkspacePreset(workspace))
|
2020-01-24 17:13:32 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (!workspaces().contains(workspace))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Check if the currently active workspace isn't empty and try to save it
|
|
|
|
|
if (!d->m_workspaceName.isEmpty()) {
|
|
|
|
|
// Allow everyone to set something in the workspace and before saving
|
|
|
|
|
emit aboutToUnloadWorkspace(d->m_workspaceName);
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!save())
|
2020-01-24 17:13:32 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try loading the file
|
2020-03-02 15:13:20 +01:00
|
|
|
QByteArray data = loadWorkspace(workspace);
|
|
|
|
|
if (data.isEmpty())
|
|
|
|
|
return false;
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
emit openingWorkspace(workspace);
|
|
|
|
|
// If data was loaded from file try to restore its state
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!data.isNull() && !restoreState(data))
|
2020-01-24 17:13:32 +01:00
|
|
|
return false;
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
d->m_workspaceName = workspace;
|
|
|
|
|
emit workspaceLoaded(workspace);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
bool DockManager::reloadActiveWorkspace()
|
|
|
|
|
{
|
|
|
|
|
if (!workspaces().contains(activeWorkspace()))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Try loading the file
|
|
|
|
|
QByteArray data = loadWorkspace(activeWorkspace());
|
|
|
|
|
if (data.isEmpty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// If data was loaded from file try to restore its state
|
|
|
|
|
if (!data.isNull() && !restoreState(data))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
emit workspaceReloaded(activeWorkspace());
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
/**
|
|
|
|
|
* \brief Shows a dialog asking the user to confirm deleting the workspace \p workspace
|
|
|
|
|
*/
|
|
|
|
|
bool DockManager::confirmWorkspaceDelete(const QStringList &workspace)
|
|
|
|
|
{
|
|
|
|
|
const QString title = workspace.size() == 1 ? tr("Delete Workspace")
|
|
|
|
|
: tr("Delete Workspaces");
|
|
|
|
|
const QString question = workspace.size() == 1
|
|
|
|
|
? tr("Delete workspace %1?").arg(workspace.first())
|
|
|
|
|
: tr("Delete these workspaces?\n %1")
|
|
|
|
|
.arg(workspace.join("\n "));
|
|
|
|
|
return QMessageBox::question(parentWidget(),
|
|
|
|
|
title,
|
|
|
|
|
question,
|
|
|
|
|
QMessageBox::Yes | QMessageBox::No)
|
|
|
|
|
== QMessageBox::Yes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deletes \a workspace name from workspace list and the file from disk.
|
|
|
|
|
*/
|
|
|
|
|
bool DockManager::deleteWorkspace(const QString &workspace)
|
|
|
|
|
{
|
|
|
|
|
// Remove workspace from internal list
|
|
|
|
|
if (!d->m_workspaces.contains(workspace))
|
|
|
|
|
return false;
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
// Remove corresponding workspace file
|
2023-01-11 13:45:33 +01:00
|
|
|
const FilePath file = workspaceNameToFilePath(workspace);
|
2023-01-11 13:43:27 +01:00
|
|
|
if (file.exists()) {
|
|
|
|
|
if (file.removeFile()) {
|
2020-03-13 15:02:50 +01:00
|
|
|
d->m_workspaces.removeOne(workspace);
|
|
|
|
|
emit workspacesRemoved();
|
|
|
|
|
emit workspaceListChanged();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-24 17:13:32 +01:00
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
return false;
|
2020-01-24 17:13:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::deleteWorkspaces(const QStringList &workspaces)
|
|
|
|
|
{
|
|
|
|
|
for (const QString &workspace : workspaces)
|
|
|
|
|
deleteWorkspace(workspace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::cloneWorkspace(const QString &original, const QString &clone)
|
|
|
|
|
{
|
|
|
|
|
if (!d->m_workspaces.contains(original))
|
|
|
|
|
return false;
|
|
|
|
|
|
2023-01-11 13:45:33 +01:00
|
|
|
const FilePath originalPath = workspaceNameToFilePath(original);
|
|
|
|
|
const FilePath clonePath = workspaceNameToFilePath(clone);
|
2023-01-11 13:43:27 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
// If the file does not exist, we can still clone
|
2023-01-11 13:43:27 +01:00
|
|
|
if (!originalPath.exists() || originalPath.copyFile(clonePath)) {
|
2020-01-24 17:13:32 +01:00
|
|
|
d->m_workspaces.insert(1, clone);
|
2023-01-11 13:43:27 +01:00
|
|
|
d->m_workspaceDateTimes.insert(clone, clonePath.lastModified());
|
2020-04-15 18:03:51 +02:00
|
|
|
emit workspaceListChanged();
|
2020-01-24 17:13:32 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::renameWorkspace(const QString &original, const QString &newName)
|
|
|
|
|
{
|
|
|
|
|
if (!cloneWorkspace(original, newName))
|
|
|
|
|
return false;
|
2020-06-22 16:46:25 +02:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
if (original == activeWorkspace())
|
|
|
|
|
openWorkspace(newName);
|
2020-06-22 16:46:25 +02:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
return deleteWorkspace(original);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
bool DockManager::resetWorkspacePreset(const QString &workspace)
|
2020-01-24 17:13:32 +01:00
|
|
|
{
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!isWorkspacePreset(workspace))
|
|
|
|
|
return false;
|
|
|
|
|
|
2023-01-11 13:45:33 +01:00
|
|
|
const FilePath fileName = workspaceNameToFilePath(workspace);
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2023-01-11 13:43:27 +01:00
|
|
|
if (!fileName.removeFile())
|
2020-03-02 15:13:20 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
QDir presetsDir(d->m_workspacePresetsPath);
|
2020-05-06 12:45:15 +02:00
|
|
|
bool result = QFile::copy(presetsDir.filePath(workspaceNameToFileName(workspace)),
|
2023-01-11 13:43:27 +01:00
|
|
|
fileName.toFSPathString());
|
2020-03-02 15:13:20 +01:00
|
|
|
if (result)
|
|
|
|
|
d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime());
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-09 08:30:17 +01:00
|
|
|
void DockManager::setModeChangeState(bool value)
|
|
|
|
|
{
|
|
|
|
|
d->m_modeChangeState = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockManager::isModeChangeState() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_modeChangeState;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 12:45:15 +02:00
|
|
|
void DockManager::importWorkspace(const QString &workspace)
|
|
|
|
|
{
|
|
|
|
|
// Extract workspace name
|
|
|
|
|
QString workspaceName = fileNameToWorkspaceName(workspace);
|
|
|
|
|
|
|
|
|
|
// Check if the workspace is already contained in the list of workspaces. If that is the case
|
|
|
|
|
// add a counter to the workspace name.
|
|
|
|
|
if (workspaces().contains(workspaceName)) {
|
|
|
|
|
int i = 2;
|
|
|
|
|
QString copy;
|
|
|
|
|
do {
|
|
|
|
|
copy = workspaceName + QLatin1String(" (") + QString::number(i) + QLatin1Char(')');
|
|
|
|
|
++i;
|
|
|
|
|
} while (workspaces().contains(copy));
|
|
|
|
|
workspaceName = copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString fileName = workspaceNameToFileName(workspaceName);
|
|
|
|
|
QFile file(workspace);
|
|
|
|
|
if (!file.exists()) {
|
|
|
|
|
qCInfo(adsLog) << QString("File doesn't exist '%1'").arg(workspace);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName);
|
|
|
|
|
|
|
|
|
|
if (!file.copy(workspaceDir.filePath(fileName))) {
|
|
|
|
|
qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3").arg(
|
|
|
|
|
workspace, workspaceDir.filePath(fileName), file.errorString());
|
|
|
|
|
} else {
|
|
|
|
|
d->m_workspaces.insert(1, workspaceName);
|
|
|
|
|
d->m_workspaceDateTimes.insert(workspaceName,
|
2021-06-17 17:25:38 +02:00
|
|
|
workspaceNameToFilePath(workspaceName).lastModified());
|
2020-05-06 12:45:15 +02:00
|
|
|
d->m_workspaceListDirty = true;
|
|
|
|
|
// After importing the workspace, update the workspace list
|
|
|
|
|
workspaces();
|
|
|
|
|
emit workspaceListChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::exportWorkspace(const QString &target, const QString &workspace)
|
|
|
|
|
{
|
|
|
|
|
// If we came this far the user decided that in case the target already exists to overwrite it.
|
|
|
|
|
// We first need to remove the existing file, otherwise QFile::copy() will fail.
|
2023-01-11 13:45:33 +01:00
|
|
|
const FilePath targetFile = FilePath::fromUserInput(target);
|
2020-05-06 12:45:15 +02:00
|
|
|
|
|
|
|
|
// Remove the file which supposed to be overwritten
|
2023-01-11 13:43:27 +01:00
|
|
|
if (targetFile.exists()) {
|
|
|
|
|
if (!targetFile.removeFile()) {
|
|
|
|
|
qCInfo(adsLog) << QString("Couldn't remove '%1'").arg(targetFile.toUserOutput());
|
2020-05-06 12:45:15 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the target directory exists
|
2023-01-11 13:43:27 +01:00
|
|
|
if (!targetFile.parentDir().exists()) {
|
|
|
|
|
qCInfo(adsLog) << QString("Directory doesn't exist '%1'")
|
|
|
|
|
.arg(targetFile.parentDir().toUserOutput());
|
2020-05-06 12:45:15 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the workspace exists
|
2023-01-11 13:45:33 +01:00
|
|
|
FilePath workspaceFile = workspaceNameToFilePath(workspace);
|
2023-01-11 13:43:27 +01:00
|
|
|
if (!workspaceFile.exists()) {
|
|
|
|
|
qCInfo(adsLog) << QString("Workspace doesn't exist '%1'")
|
|
|
|
|
.arg(workspaceFile.toUserOutput());
|
|
|
|
|
return;
|
2020-05-06 12:45:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finally copy the workspace to the target
|
2023-01-11 13:45:33 +01:00
|
|
|
const expected_str<void> copyResult = workspaceFile.copyFile(targetFile);
|
2023-01-11 13:43:27 +01:00
|
|
|
if (!copyResult) {
|
|
|
|
|
qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3")
|
|
|
|
|
.arg(workspace, workspaceFile.toUserOutput(), copyResult.error());
|
2020-05-06 12:45:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
bool DockManager::write(const QString &workspace, const QByteArray &data, QString *errorString) const
|
|
|
|
|
{
|
2023-01-11 13:45:33 +01:00
|
|
|
const FilePath fileName = workspaceNameToFilePath(workspace);
|
2020-01-24 17:13:32 +01:00
|
|
|
|
|
|
|
|
QDir tmp;
|
2020-05-06 12:45:15 +02:00
|
|
|
tmp.mkpath(fileName.toFileInfo().path());
|
2023-01-11 13:45:33 +01:00
|
|
|
FileSaver fileSaver(fileName, QIODevice::Text);
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!fileSaver.hasError())
|
2020-01-24 17:13:32 +01:00
|
|
|
fileSaver.write(data);
|
2020-03-02 15:13:20 +01:00
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
bool ok = fileSaver.finalize();
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
if (!ok && errorString)
|
2020-01-24 17:13:32 +01:00
|
|
|
*errorString = fileSaver.errorString();
|
|
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
bool DockManager::write(const QString &workspace, const QByteArray &data, QWidget *parent) const
|
2020-01-24 17:13:32 +01:00
|
|
|
{
|
|
|
|
|
QString errorString;
|
2020-03-02 15:13:20 +01:00
|
|
|
const bool success = write(workspace, data, &errorString);
|
2020-01-24 17:13:32 +01:00
|
|
|
if (!success)
|
|
|
|
|
QMessageBox::critical(parent,
|
2023-02-07 22:46:35 +01:00
|
|
|
QCoreApplication::translate("::Utils", "File Error"),
|
2020-01-24 17:13:32 +01:00
|
|
|
errorString);
|
|
|
|
|
return success;
|
|
|
|
|
}
|
2020-03-02 15:13:20 +01:00
|
|
|
|
|
|
|
|
QByteArray DockManager::loadWorkspace(const QString &workspace) const
|
|
|
|
|
{
|
2023-01-11 13:45:33 +01:00
|
|
|
const FilePath fileName = workspaceNameToFilePath(workspace);
|
2020-03-02 15:13:20 +01:00
|
|
|
if (fileName.exists()) {
|
2023-01-11 13:45:33 +01:00
|
|
|
const expected_str<QByteArray> data = fileName.fileContents();
|
2023-01-11 13:43:27 +01:00
|
|
|
|
|
|
|
|
if (!data) {
|
2020-03-02 15:13:20 +01:00
|
|
|
QMessageBox::warning(parentWidget(),
|
|
|
|
|
tr("Cannot Restore Workspace"),
|
|
|
|
|
tr("Could not restore workspace %1")
|
|
|
|
|
.arg(fileName.toUserOutput()));
|
2023-01-11 13:43:27 +01:00
|
|
|
|
|
|
|
|
qCWarning(adsLog) << QString("Could not restore workspace %1: %2")
|
|
|
|
|
.arg(fileName.toUserOutput())
|
|
|
|
|
.arg(data.error());
|
|
|
|
|
|
|
|
|
|
return {};
|
2020-03-02 15:13:20 +01:00
|
|
|
}
|
2023-01-11 13:43:27 +01:00
|
|
|
return data.value();
|
2020-03-02 15:13:20 +01:00
|
|
|
}
|
2023-01-11 13:43:27 +01:00
|
|
|
return {};
|
2020-03-02 15:13:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::syncWorkspacePresets()
|
|
|
|
|
{
|
|
|
|
|
// Get a list of all workspace presets
|
|
|
|
|
QSet<QString> presets = workspacePresets();
|
|
|
|
|
|
|
|
|
|
// Get a list of all available workspaces
|
|
|
|
|
QSet<QString> availableWorkspaces = Utils::toSet(workspaces());
|
|
|
|
|
presets.subtract(availableWorkspaces);
|
|
|
|
|
|
|
|
|
|
// Copy all missing workspace presets over to the local workspace folder
|
|
|
|
|
QDir presetsDir(d->m_workspacePresetsPath);
|
2020-03-09 15:05:13 +01:00
|
|
|
QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName);
|
|
|
|
|
// Try do create the 'workspaces' directory if it doesn't exist already
|
|
|
|
|
workspaceDir.mkpath(workspaceDir.absolutePath());
|
|
|
|
|
if (!workspaceDir.exists()) {
|
|
|
|
|
qCInfo(adsLog) << QString("Could not make directory '%1')").arg(workspaceDir.absolutePath());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-03-02 15:13:20 +01:00
|
|
|
|
|
|
|
|
for (const auto &preset : presets) {
|
2020-05-06 12:45:15 +02:00
|
|
|
QString fileName = workspaceNameToFileName(preset);
|
|
|
|
|
QString filePath = presetsDir.filePath(fileName);
|
2020-03-02 15:13:20 +01:00
|
|
|
QFile file(filePath);
|
|
|
|
|
|
|
|
|
|
if (file.exists()) {
|
2020-06-22 16:46:25 +02:00
|
|
|
if (!file.copy(workspaceDir.filePath(fileName)))
|
2020-03-09 15:05:13 +01:00
|
|
|
qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3").arg(
|
2020-05-06 12:45:15 +02:00
|
|
|
filePath, workspaceDir.filePath(fileName), file.errorString());
|
2020-06-22 16:46:25 +02:00
|
|
|
|
2020-03-02 15:13:20 +01:00
|
|
|
d->m_workspaceListDirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// After copying over missing workspace presets, update the workspace list
|
|
|
|
|
workspaces();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::saveStartupWorkspace()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(d->m_settings, return );
|
|
|
|
|
d->m_settings->setValue(Constants::STARTUP_WORKSPACE_SETTINGS_KEY, activeWorkspace());
|
|
|
|
|
}
|
2020-01-24 17:13:32 +01:00
|
|
|
|
2020-06-22 16:46:25 +02:00
|
|
|
void DockManager::notifyWidgetOrAreaRelocation(QWidget *droppedWidget)
|
|
|
|
|
{
|
|
|
|
|
if (d->m_focusController)
|
|
|
|
|
d->m_focusController->notifyWidgetOrAreaRelocation(droppedWidget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget)
|
|
|
|
|
{
|
|
|
|
|
if (d->m_focusController)
|
|
|
|
|
d->m_focusController->notifyFloatingWidgetDrop(floatingWidget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockManager::setDockWidgetFocused(DockWidget *dockWidget)
|
|
|
|
|
{
|
|
|
|
|
if (d->m_focusController)
|
|
|
|
|
d->m_focusController->setDockWidgetFocused(dockWidget);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-24 17:13:32 +01:00
|
|
|
} // namespace ADS
|