QmlDesigner: Add import/export of workspaces

* Add import/export functionality to the workspace manager
* Remove checkFormat functionality
* Add utility functions for workspace and file name conversion
* Use workspace file name extension from DockManager
* Fix a few DockManager comments

Task-number: QDS-1553
Change-Id: I1fa99d3ad85e282b3b11be2425faa4458d8a7778
Reviewed-by: Aleksei German <aleksei.german@qt.io>
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Henning Gruendl
2020-05-06 12:45:15 +02:00
committed by Henning Gründl
parent 6d36db78aa
commit 95475156e3
9 changed files with 226 additions and 74 deletions

View File

@@ -126,8 +126,6 @@ enum eBitwiseOperator
};
namespace internal {
const bool restoreTesting = true;
const bool restore = false;
const char *const closedProperty = "close";
const char *const dirtyProperty = "dirty";

View File

@@ -106,17 +106,12 @@ namespace ADS
DockManagerPrivate(DockManager *parent);
/**
* Checks if the given data stream is a valid docking system state
* file.
*/
bool checkFormat(const QByteArray &state, int version);
/**
* Restores the state
* Restores the state. If testing is set to true it will check if
* the given data stream is a valid docking system state file.
*/
bool restoreStateFromXml(const QByteArray &state,
int version,
bool testing = internal::restore);
bool testing = false);
/**
* Restore state
@@ -174,11 +169,6 @@ namespace ADS
return result;
}
bool DockManagerPrivate::checkFormat(const QByteArray &state, int version)
{
return restoreStateFromXml(state, version, internal::restoreTesting);
}
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?
@@ -297,7 +287,8 @@ namespace ADS
bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
{
QByteArray currentState = state.startsWith("<?xml") ? state : qUncompress(state);
if (!checkFormat(currentState, version)) {
// Check the format of the given data stream
if (!restoreStateFromXml(currentState, version, true)) {
qCInfo(adsLog) << "checkFormat: Error checking format!!!";
return false;
}
@@ -589,7 +580,7 @@ namespace ADS
QMessageBox::warning(parentWidget(),
tr("Cannot Save Workspace"),
tr("Could not save workspace to file %1")
.arg(workspaceNameToFileName(d->m_workspaceName)
.arg(workspaceNameToFilePath(d->m_workspaceName)
.toUserOutput()));
}
@@ -613,6 +604,8 @@ namespace ADS
const QString m_dirName = QLatin1String("workspaces");
const QString m_fileExt = QLatin1String(".wrk"); // TODO
QString DockManager::workspaceFileExtension() const { return m_fileExt; }
QStringList DockManager::workspaces()
{
if (d->m_workspaces.isEmpty() || d->m_workspaceListDirty) {
@@ -622,14 +615,13 @@ namespace ADS
QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/')
+ m_dirName);
QFileInfoList workspaceFiles
= workspaceDir.entryInfoList(QStringList() << QLatin1String("*.wrk"),
= workspaceDir.entryInfoList(QStringList() << QLatin1Char('*') + m_fileExt,
QDir::NoFilter,
QDir::Time);
for (const QFileInfo &fileInfo : workspaceFiles) {
QString filename = fileInfo.completeBaseName();
filename.replace("_", " ");
d->m_workspaceDateTimes.insert(filename, fileInfo.lastModified());
tmp.insert(filename);
QString workspaceName = fileNameToWorkspaceName(fileInfo.completeBaseName());
d->m_workspaceDateTimes.insert(workspaceName, fileInfo.lastModified());
tmp.insert(workspaceName);
}
d->m_workspaceListDirty = false;
@@ -643,13 +635,11 @@ namespace ADS
if (d->m_workspacePresets.isEmpty()) {
QDir workspacePresetsDir(d->m_workspacePresetsPath);
QFileInfoList workspacePresetsFiles
= workspacePresetsDir.entryInfoList(QStringList() << QLatin1String("*.wrk"),
= workspacePresetsDir.entryInfoList(QStringList() << QLatin1Char('*') + m_fileExt,
QDir::NoFilter,
QDir::Time);
for (const QFileInfo &fileInfo : workspacePresetsFiles) {
QString filename = fileInfo.completeBaseName();
filename.replace("_", " ");
d->m_workspacePresets.insert(filename);
d->m_workspacePresets.insert(fileNameToWorkspaceName(fileInfo.completeBaseName()));
}
}
return d->m_workspacePresets;
@@ -660,13 +650,27 @@ namespace ADS
return d->m_workspaceDateTimes.value(workspace);
}
Utils::FilePath DockManager::workspaceNameToFileName(const QString &workspaceName) const
Utils::FilePath DockManager::workspaceNameToFilePath(const QString &workspaceName) const
{
QTC_ASSERT(d->m_settings, return {});
QString workspaceNameCopy = workspaceName;
return Utils::FilePath::fromString(
QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName
+ QLatin1Char('/') + workspaceNameCopy.replace(" ", "_") + QLatin1String(".wrk"));
+ QLatin1Char('/') + workspaceNameToFileName(workspaceName));
}
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;
}
/**
@@ -686,7 +690,7 @@ namespace ADS
QMessageBox::warning(parentWidget(),
tr("Cannot Save Workspace"),
tr("Could not save workspace to file %1")
.arg(workspaceNameToFileName(d->m_workspaceName)
.arg(workspaceNameToFilePath(d->m_workspaceName)
.toUserOutput()));
}
@@ -775,7 +779,7 @@ namespace ADS
return false;
// Remove corresponding workspace file
QFile fi(workspaceNameToFileName(workspace).toString());
QFile fi(workspaceNameToFilePath(workspace).toString());
if (fi.exists()) {
if (fi.remove()) {
d->m_workspaces.removeOne(workspace);
@@ -799,12 +803,12 @@ namespace ADS
if (!d->m_workspaces.contains(original))
return false;
QFile fi(workspaceNameToFileName(original).toString());
QFile fi(workspaceNameToFilePath(original).toString());
// If the file does not exist, we can still clone
if (!fi.exists() || fi.copy(workspaceNameToFileName(clone).toString())) {
if (!fi.exists() || fi.copy(workspaceNameToFilePath(clone).toString())) {
d->m_workspaces.insert(1, clone);
d->m_workspaceDateTimes
.insert(clone, workspaceNameToFileName(clone).toFileInfo().lastModified());
.insert(clone, workspaceNameToFilePath(clone).toFileInfo().lastModified());
emit workspaceListChanged();
return true;
}
@@ -825,17 +829,14 @@ namespace ADS
if (!isWorkspacePreset(workspace))
return false;
Utils::FilePath filename = workspaceNameToFileName(workspace);
Utils::FilePath fileName = workspaceNameToFilePath(workspace);
if (!QFile::remove(filename.toString()))
if (!QFile::remove(fileName.toString()))
return false;
QDir presetsDir(d->m_workspacePresetsPath);
QString presetName = workspace;
presetName.replace(" ", "_");
presetName.append(".wrk");
bool result = QFile::copy(presetsDir.filePath(presetName), filename.toString());
bool result = QFile::copy(presetsDir.filePath(workspaceNameToFileName(workspace)),
fileName.toString());
if (result)
d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime());
@@ -852,13 +853,89 @@ namespace ADS
return d->m_modeChangeState;
}
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,
workspaceNameToFilePath(workspaceName).toFileInfo().lastModified());
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.
QFileInfo targetFileInfo(target);
// Remove the file which supposed to be overwritten
if (targetFileInfo.exists()) {
QFile fi(targetFileInfo.absoluteFilePath());
if (!fi.remove()) {
qCInfo(adsLog) << QString("Couldn't remove '%1'").arg(targetFileInfo.absoluteFilePath());
return;
}
}
// Check if the target directory exists
if (!targetFileInfo.absoluteDir().exists()) {
qCInfo(adsLog) << QString("Directory doesn't exist '%1'").arg(targetFileInfo.dir().dirName());
return;
}
// Check if the workspace exists
Utils::FilePath workspaceFilePath = workspaceNameToFilePath(workspace);
if (!workspaceFilePath.exists()) {
qCInfo(adsLog) << QString("Workspace doesn't exist '%1'").arg(workspaceFilePath.toString());
return;
}
// Finally copy the workspace to the target
QFile workspaceFile(workspaceFilePath.toString());
if (!workspaceFile.copy(targetFileInfo.absoluteFilePath())) {
qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3").arg(
workspace, workspaceFilePath.toString(), workspaceFile.errorString());
}
}
bool DockManager::write(const QString &workspace, const QByteArray &data, QString *errorString) const
{
Utils::FilePath filename = workspaceNameToFileName(workspace);
Utils::FilePath fileName = workspaceNameToFilePath(workspace);
QDir tmp;
tmp.mkpath(filename.toFileInfo().path());
Utils::FileSaver fileSaver(filename.toString(), QIODevice::Text);
tmp.mkpath(fileName.toFileInfo().path());
Utils::FileSaver fileSaver(fileName.toString(), QIODevice::Text);
if (!fileSaver.hasError())
fileSaver.write(data);
@@ -884,7 +961,7 @@ namespace ADS
QByteArray DockManager::loadWorkspace(const QString &workspace) const
{
QByteArray data;
Utils::FilePath fileName = workspaceNameToFileName(workspace);
Utils::FilePath fileName = workspaceNameToFilePath(workspace);
if (fileName.exists()) {
QFile file(fileName.toString());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -920,17 +997,14 @@ namespace ADS
}
for (const auto &preset : presets) {
QString filename = preset;
filename.replace(" ", "_");
filename.append(".wrk");
QString filePath = presetsDir.filePath(filename);
QString fileName = workspaceNameToFileName(preset);
QString filePath = presetsDir.filePath(fileName);
QFile file(filePath);
if (file.exists()) {
if (!file.copy(workspaceDir.filePath(filename))) {
if (!file.copy(workspaceDir.filePath(fileName))) {
qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3").arg(
filePath, workspaceDir.filePath(filename), file.errorString());
filePath, workspaceDir.filePath(fileName), file.errorString());
}
d->m_workspaceListDirty = true;
}

View File

@@ -232,7 +232,7 @@ public:
virtual ~DockManager() override;
/**
* This function returns the global configuration flags
* This function returns the global configuration flags.
*/
static ConfigFlags configFlags();
@@ -244,13 +244,13 @@ public:
static void setConfigFlags(const ConfigFlags flags);
/**
* Set a certain config flag
* Set a certain config flag.
* \see setConfigFlags()
*/
static void setConfigFlag(eConfigFlag flag, bool on = true);
/**
* Returns true if the given config flag is set
* Returns true if the given config flag is set.
*/
static bool testConfigFlag(eConfigFlag flag);
@@ -263,7 +263,7 @@ public:
/**
* The distance the user needs to move the mouse with the left button
* hold down before a dock widget start floating
* hold down before a dock widget start floating.
*/
static int startDragDistance();
@@ -319,35 +319,35 @@ public:
/**
* Searches for a registered doc widget with the given ObjectName
* \return Return the found dock widget or nullptr if a dock widget with the
* given name is not registered
* given name is not registered.
*/
DockWidget *findDockWidget(const QString &objectName) const;
/**
* Remove the given Dock from the dock manager
* Remove the given DockWidget from the dock manager.
*/
void removeDockWidget(DockWidget *dockWidget);
/**
* This function returns a readable reference to the internal dock
* widgets map so that it is possible to iterate over all dock widgets
* widgets map so that it is possible to iterate over all dock widgets.
*/
QMap<QString, DockWidget *> dockWidgetsMap() const;
/**
* Returns the list of all active and visible dock containers
* Dock containers are the main dock manager and all floating widgets
* Dock containers are the main dock manager and all floating widgets.
*/
const QList<DockContainerWidget *> dockContainers() const;
/**
* Returns the list of all floating widgets
* Returns the list of all floating widgets.
*/
const QList<FloatingDockContainer *> floatingWidgets() const;
/**
* This function always return 0 because the main window is always behind
* any floating widget
* any floating widget.
*/
virtual unsigned int zOrderIndex() const override;
@@ -378,33 +378,33 @@ public:
signals:
/**
* This signal is emitted if the list of perspectives changed
* This signal is emitted if the list of workspaces changed.
*/
void workspaceListChanged();
/**
* This signal is emitted if perspectives have been removed
* This signal is emitted if workspaces have been removed.
*/
void workspacesRemoved();
/**
* This signal is emitted, if the restore function is called, just before
* the dock manager starts restoring the state.
* If this function is called, nothing has changed yet
* If this function is called, nothing has changed yet.
*/
void restoringState();
/**
* This signal is emitted if the state changed in restoreState.
* The signal is emitted if the restoreState() function is called or
* if the openWorkspace() function is called
* if the openWorkspace() function is called.
*/
void stateRestored();
/**
* This signal is emitted, if the dock manager starts opening a
* perspective.
* Opening a perspective may take more than a second if there are
* workspace.
* Opening a workspace may take more than a second if there are
* many complex widgets. The application may use this signal
* to show some progress indicator or to change the mouse cursor
* into a busy cursor.
@@ -413,7 +413,7 @@ signals:
/**
* This signal is emitted if the dock manager finished opening a
* perspective
* workspace.
*/
void workspaceOpened(const QString &workspaceName);
@@ -432,8 +432,7 @@ signals:
void dockAreaCreated(DockAreaWidget *dockArea);
/**
* This signal is emitted just before the given dock widget is removed
* from the
* This signal is emitted just before removal of the given DockWidget.
*/
void dockWidgetAboutToBeRemoved(DockWidget *dockWidget);
@@ -452,10 +451,13 @@ public:
QString activeWorkspace() const;
QString lastWorkspace() const;
bool autoRestorLastWorkspace() const;
QString workspaceFileExtension() const;
QStringList workspaces();
QSet<QString> workspacePresets() const;
QDateTime workspaceDateTime(const QString &workspace) const;
Utils::FilePath workspaceNameToFileName(const QString &workspaceName) const;
Utils::FilePath workspaceNameToFilePath(const QString &workspaceName) const;
QString fileNameToWorkspaceName(const QString &fileName) const;
QString workspaceNameToFileName(const QString &workspaceName) const;
bool createWorkspace(const QString &workspace);
@@ -478,6 +480,9 @@ public:
void setModeChangeState(bool value);
bool isModeChangeState() const;
void importWorkspace(const QString &workspace);
void exportWorkspace(const QString &target, const QString &workspace);
signals:
void aboutToUnloadWorkspace(QString workspaceName);
void aboutToLoadWorkspace(QString workspaceName);

View File

@@ -170,6 +170,14 @@ WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent)
&WorkspaceView::selected,
this,
&WorkspaceDialog::updateActions);
connect(m_ui.btImport,
&QAbstractButton::clicked,
m_ui.workspaceView,
&WorkspaceView::importWorkspace);
connect(m_ui.btExport,
&QAbstractButton::clicked,
m_ui.workspaceView,
&WorkspaceView::exportCurrentWorkspace);
m_ui.whatsAWorkspaceLabel->setOpenExternalLinks(true);
@@ -199,6 +207,7 @@ void WorkspaceDialog::updateActions(const QStringList &workspaces)
m_ui.btClone->setEnabled(false);
m_ui.btReset->setEnabled(false);
m_ui.btSwitch->setEnabled(false);
m_ui.btExport->setEnabled(false);
return;
}
const bool presetIsSelected = Utils::anyOf(workspaces, [this](const QString &workspace) {
@@ -212,6 +221,7 @@ void WorkspaceDialog::updateActions(const QStringList &workspaces)
m_ui.btClone->setEnabled(workspaces.size() == 1);
m_ui.btReset->setEnabled(presetIsSelected);
m_ui.btSwitch->setEnabled(workspaces.size() == 1);
m_ui.btExport->setEnabled(workspaces.size() == 1);
}
} // namespace ADS

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>373</width>
<height>282</height>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
@@ -90,6 +90,20 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btImport">
<property name="text">
<string>Import</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btExport">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">

View File

@@ -260,6 +260,18 @@ void WorkspaceModel::switchToWorkspace(const QString &workspace)
emit workspaceSwitched();
}
void WorkspaceModel::importWorkspace(const QString &workspace)
{
m_manager->importWorkspace(workspace);
m_sortedWorkspaces = m_manager->workspaces();
sort(m_currentSortColumn, m_currentSortOrder);
}
void WorkspaceModel::exportWorkspace(const QString &target, const QString &workspace)
{
m_manager->exportWorkspace(target, workspace);
}
void WorkspaceModel::runWorkspaceNameInputDialog(WorkspaceNameInputDialog *workspaceInputDialog,
std::function<void(const QString &)> createWorkspace)
{

View File

@@ -77,6 +77,9 @@ public:
void resetWorkspace(const QString &workspace);
void switchToWorkspace(const QString &workspace);
void importWorkspace(const QString &workspace);
void exportWorkspace(const QString &target, const QString &workspace);
private:
void runWorkspaceNameInputDialog(WorkspaceNameInputDialog *workspaceInputDialog,
std::function<void(const QString &)> createWorkspace);

View File

@@ -40,6 +40,7 @@
#include <utils/algorithm.h>
#include <QFileDialog>
#include <QHeaderView>
#include <QItemSelection>
#include <QStringList>
@@ -135,6 +136,38 @@ void WorkspaceView::deleteWorkspaces(const QStringList &workspaces)
m_workspaceModel.deleteWorkspaces(workspaces);
}
void WorkspaceView::importWorkspace()
{
static QString lastDir;
const QString currentDir = lastDir.isEmpty() ? "" : lastDir;
const auto fileName = QFileDialog::getOpenFileName(this,
tr("Import Workspace"),
currentDir,
"Workspaces (*" + m_manager->workspaceFileExtension() + ")");
if (!fileName.isEmpty())
lastDir = QFileInfo(fileName).absolutePath();
m_workspaceModel.importWorkspace(fileName);
}
void WorkspaceView::exportCurrentWorkspace()
{
static QString lastDir;
const QString currentDir = lastDir.isEmpty() ? "" : lastDir;
QFileInfo fileInfo(currentDir, m_manager->workspaceNameToFileName(currentWorkspace()));
const auto fileName = QFileDialog::getSaveFileName(this,
tr("Export Workspace"),
fileInfo.absoluteFilePath(),
"Workspaces (*" + m_manager->workspaceFileExtension() + ")");
if (!fileName.isEmpty())
lastDir = QFileInfo(fileName).absolutePath();
m_workspaceModel.exportWorkspace(fileName, currentWorkspace());
}
void WorkspaceView::cloneCurrentWorkspace()
{
m_workspaceModel.cloneWorkspace(this, currentWorkspace());

View File

@@ -60,6 +60,9 @@ public:
void resetCurrentWorkspace();
void switchToCurrentWorkspace();
void importWorkspace();
void exportCurrentWorkspace();
QString currentWorkspace();
WorkspaceModel *workspaceModel();
void selectActiveWorkspace();