diff --git a/libs/ui/KisWindowLayoutResource.cpp b/libs/ui/KisWindowLayoutResource.cpp index 68cb27a3d5..11f0bd2890 100644 --- a/libs/ui/KisWindowLayoutResource.cpp +++ b/libs/ui/KisWindowLayoutResource.cpp @@ -1,303 +1,308 @@ /* * Copyright (c) 2018 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisWindowLayoutResource.h" #include #include #include +#include +#include #include #include #include static const int WINDOW_LAYOUT_VERSION = 1; struct KisWindowLayoutResource::Private { struct Window { QUuid windowId; QByteArray geometry; QByteArray windowState; }; QVector windows; bool showImageInAllWindows; Private() = default; explicit Private(QVector windows) : windows(std::move(windows)) {} QPointer findWindow(QUuid id) { const QList> ¤tWindows = KisPart::instance()->mainWindows(); Q_FOREACH(QPointer mainWindow, currentWindows) { if (mainWindow->id() == id) { return mainWindow; } } return QPointer(); } void openNecessaryWindows(QList> ¤tWindows) { auto *kisPart = KisPart::instance(); Q_FOREACH(const Window &window, windows) { QPointer mainWindow = findWindow(window.windowId); if (mainWindow.isNull()) { mainWindow = kisPart->createMainWindow(window.windowId); currentWindows.append(mainWindow); mainWindow->show(); } } } void closeUnneededWindows(QList> ¤tWindows) { QVector> windowsToClose; Q_FOREACH(KisMainWindow *mainWindow, currentWindows) { bool keep = false; Q_FOREACH(const Window &window, windows) { if (window.windowId == mainWindow->id()) { keep = true; break; } } if (!keep) { windowsToClose.append(mainWindow); } } migrateViewsFromClosingWindows(windowsToClose); Q_FOREACH(QPointer mainWindow, windowsToClose) { mainWindow->close(); } } void migrateViewsFromClosingWindows(QVector> &closingWindows) const { auto *kisPart = KisPart::instance(); KisMainWindow *migrationTarget = nullptr; Q_FOREACH(KisMainWindow *mainWindow, kisPart->mainWindows()) { if (!closingWindows.contains(mainWindow)) { migrationTarget = mainWindow; break; } } if (!migrationTarget) { qWarning() << "Problem: window layout with no windows would leave user with zero main windows."; migrationTarget = closingWindows.takeLast(); migrationTarget->show(); } QVector visibleDocuments; Q_FOREACH(KisView *view, kisPart->views()) { KisMainWindow *window = view->mainWindow(); if (!closingWindows.contains(window)) { visibleDocuments.append(view->document()); } } Q_FOREACH(KisDocument *document, kisPart->documents()) { if (!visibleDocuments.contains(document)) { visibleDocuments.append(document); migrationTarget->newView(document); } } } }; KisWindowLayoutResource::KisWindowLayoutResource(const QString &filename) : KoResource(filename) , d(new Private) {} KisWindowLayoutResource::~KisWindowLayoutResource() {} KisWindowLayoutResource * KisWindowLayoutResource::fromCurrentWindows( const QString &filename, const QList> &mainWindows, bool showImageInAllWindows ) { auto resource = new KisWindowLayoutResource(filename); resource->setWindows(mainWindows); resource->setShowImageInAllWindows(showImageInAllWindows); return resource; } void KisWindowLayoutResource::applyLayout() { KisPart *kisPart = KisPart::instance(); QList> currentWindows = kisPart->mainWindows(); d->openNecessaryWindows(currentWindows); + d->closeUnneededWindows(currentWindows); + + // Wait for the windows to finish opening / closing before applying saved geometry. + // If we don't, the geometry may get reset after we apply it. + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); Q_FOREACH(const auto &window, d->windows) { QPointer mainWindow = d->findWindow(window.windowId); KIS_SAFE_ASSERT_RECOVER_BREAK(mainWindow); mainWindow->restoreGeometry(window.geometry); mainWindow->restoreWorkspace(window.windowState); } - d->closeUnneededWindows(currentWindows); - kisPart->setShowImageInAllWindowsEnabled(d->showImageInAllWindows); } bool KisWindowLayoutResource::save() { if (filename().isEmpty()) return false; QFile file(filename()); file.open(QIODevice::WriteOnly); bool res = saveToDevice(&file); file.close(); return res; } bool KisWindowLayoutResource::load() { if (filename().isEmpty()) return false; QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool KisWindowLayoutResource::saveToDevice(QIODevice *dev) const { QDomDocument doc; QDomElement root = doc.createElement("WindowLayout"); root.setAttribute("name", name()); root.setAttribute("version", WINDOW_LAYOUT_VERSION); saveXml(doc, root); doc.appendChild(root); QTextStream textStream(dev); textStream.setCodec("UTF-8"); doc.save(textStream, 4); KoResource::saveToDevice(dev); return true; } bool KisWindowLayoutResource::loadFromDevice(QIODevice *dev) { QDomDocument doc; if (!doc.setContent(dev)) { return false; } QDomElement element = doc.documentElement(); setName(element.attribute("name")); d->windows.clear(); loadXml(element); setValid(true); return true; } void KisWindowLayoutResource::saveXml(QDomDocument &doc, QDomElement &root) const { root.setAttribute("showImageInAllWindows", (int)d->showImageInAllWindows); Q_FOREACH(const auto &window, d->windows) { QDomElement elem = doc.createElement("window"); elem.setAttribute("id", window.windowId.toString()); QDomElement geometry = doc.createElement("geometry"); geometry.appendChild(doc.createCDATASection(window.geometry.toBase64())); elem.appendChild(geometry); QDomElement state = doc.createElement("windowState"); state.appendChild(doc.createCDATASection(window.windowState.toBase64())); elem.appendChild(state); root.appendChild(elem); } } void KisWindowLayoutResource::loadXml(const QDomElement &element) const { d->showImageInAllWindows = KisDomUtils::toInt(element.attribute("showImageInAllWindows", "0")); for (auto windowElement = element.firstChildElement("window"); !windowElement.isNull(); windowElement = windowElement.nextSiblingElement("window")) { Private::Window window; window.windowId = QUuid(windowElement.attribute("id", QUuid().toString())); if (window.windowId.isNull()) { window.windowId = QUuid::createUuid(); } QDomElement geometry = windowElement.firstChildElement("geometry"); QDomElement state = windowElement.firstChildElement("windowState"); window.geometry = QByteArray::fromBase64(geometry.text().toLatin1()); window.windowState = QByteArray::fromBase64(state.text().toLatin1()); d->windows.append(window); } } QString KisWindowLayoutResource::defaultFileExtension() const { return QString(".kwl"); } void KisWindowLayoutResource::setWindows(const QList> &mainWindows) { d->windows.clear(); Q_FOREACH(auto window, mainWindows) { Private::Window state; state.windowId = window->id(); state.geometry = window->saveGeometry(); state.windowState = window->saveState(); d->windows.append(state); } } void KisWindowLayoutResource::setShowImageInAllWindows(bool showInAll) { d->showImageInAllWindows = showInAll; }