From dae852d9f92ff6513e59fcd9b88b8a5e91bbf318 Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Fri, 29 Dec 2017 18:18:16 +0100 Subject: [PATCH] Implemented XML serialization and loading of docking state --- .settings/language.settings.xml | 2 +- demo/mainwindow.cpp | 4 +- src/DockAreaWidget.cpp | 10 +- src/DockAreaWidget.h | 4 +- src/DockContainerWidget.cpp | 216 ++++++++++++++++++++++---------- src/DockContainerWidget.h | 7 +- src/DockManager.cpp | 141 ++++++++++----------- src/DockWidget.cpp | 10 +- src/DockWidget.h | 4 +- src/FloatingDockContainer.cpp | 2 +- src/FloatingDockContainer.h | 4 +- 11 files changed, 245 insertions(+), 159 deletions(-) diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml index c76d45c..ef6f6a9 100644 --- a/.settings/language.settings.xml +++ b/.settings/language.settings.xml @@ -2,7 +2,7 @@ - + diff --git a/demo/mainwindow.cpp b/demo/mainwindow.cpp index 1dc8cac..7c36326 100644 --- a/demo/mainwindow.cpp +++ b/demo/mainwindow.cpp @@ -105,7 +105,7 @@ void MainWindow::createContent() DockWidget->setFeatures(DockWidget->features().setFlag(ads::CDockWidget::DockWidgetClosable, false)); m_DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget); m_DockManager->addDockWidget(ads::LeftDockWidgetArea, createLongTextLabelDockWidget(ViewMenu)); - m_DockManager->addDockWidget(ads::BottomDockWidgetArea, createFileSystemTreeDockWidget(ViewMenu)); + /*m_DockManager->addDockWidget(ads::BottomDockWidgetArea, createFileSystemTreeDockWidget(ViewMenu)); auto TopDockArea = m_DockManager->addDockWidget(ads::TopDockWidgetArea, createFileSystemTreeDockWidget(ViewMenu)); DockWidget = createCalendarDockWidget(ViewMenu); DockWidget->setFeatures(DockWidget->features().setFlag(ads::CDockWidget::DockWidgetClosable, false)); @@ -116,7 +116,7 @@ void MainWindow::createContent() m_DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea); auto BottomDockArea = m_DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea); m_DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea); - m_DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), BottomDockArea); + m_DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), BottomDockArea);*/ } diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index c732d99..868934e 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "DockContainerWidget.h" @@ -706,15 +707,18 @@ void CDockAreaWidget::updateDockArea() //============================================================================ -void CDockAreaWidget::saveState(QDataStream& stream) const +void CDockAreaWidget::saveState(QXmlStreamWriter& s) const { - stream << d->ContentsLayout->count() << d->ContentsLayout->currentIndex(); + s.writeStartElement("DockAreaWidget"); + s.writeAttribute("Tabs", QString::number(d->ContentsLayout->count())); + s.writeAttribute("CurrentIndex", QString::number(d->ContentsLayout->currentIndex())); qDebug() << "CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count() << " CurrentIndex: " << d->ContentsLayout->currentIndex(); for (int i = 0; i < d->ContentsLayout->count(); ++i) { - dockWidget(i)->saveState(stream); + dockWidget(i)->saveState(s); } + s.writeEndElement(); } } // namespace ads diff --git a/src/DockAreaWidget.h b/src/DockAreaWidget.h index 7a7072c..35edec5 100644 --- a/src/DockAreaWidget.h +++ b/src/DockAreaWidget.h @@ -32,6 +32,8 @@ //============================================================================ #include +class QXmlStreamWriter; + namespace ads { struct DockAreaWidgetPrivate; @@ -164,7 +166,7 @@ public: /** * Saves the state into the given stream */ - void saveState(QDataStream& Stream) const; + void saveState(QXmlStreamWriter& Stream) const; public slots: /** diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index b1a893d..0ec1163 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -30,12 +30,15 @@ //============================================================================ #include "DockContainerWidget.h" +#include + #include #include #include #include #include #include +#include #include "DockManager.h" #include "DockAreaWidget.h" @@ -120,7 +123,7 @@ struct DockContainerWidgetPrivate /** * Save state of child nodes */ - void saveChildNodesState(QDataStream& Stream, QWidget* Widget); + void saveChildNodesState(QXmlStreamWriter& Stream, QWidget* Widget); /** * Restore state of child nodes. @@ -130,21 +133,21 @@ struct DockContainerWidgetPrivate * \param[in] Testing If Testing is true, only the stream data is * parsed without modifiying anything. */ - bool restoreChildNodes(QDataStream& Stream, QWidget*& CreatedWidget, + bool restoreChildNodes(QXmlStreamReader& Stream, QWidget*& CreatedWidget, bool Testing); /** * Restores a splitter. * \see restoreChildNodes() for details */ - bool restoreSplitter(QDataStream& Stream, QWidget*& CreatedWidget, + bool restoreSplitter(QXmlStreamReader& Stream, QWidget*& CreatedWidget, bool Testing); /** * Restores a dock area. * \see restoreChildNodes() for details */ - bool restoreDockArea(QDataStream& Stream, QWidget*& CreatedWidget, + bool restoreDockArea(QXmlStreamReader& Stream, QWidget*& CreatedWidget, bool Testing); /** @@ -314,39 +317,56 @@ void DockContainerWidgetPrivate::addDockAreasToList(const QList(Widget); if (Splitter) { - stream << internal::SplitterMarker << Splitter->orientation() << Splitter->count(); + s.writeStartElement("Splitter"); + s.writeAttribute("Orientation", QString::number(Splitter->orientation())); + s.writeAttribute("Count", QString::number(Splitter->count())); qDebug() << "NodeSplitter orient: " << Splitter->orientation() << " WidgetCont: " << Splitter->count(); - for (int i = 0; i < Splitter->count(); ++i) - { - saveChildNodesState(stream, Splitter->widget(i)); - } - stream << Splitter->sizes(); + for (int i = 0; i < Splitter->count(); ++i) + { + saveChildNodesState(s, Splitter->widget(i)); + } + + s.writeStartElement("Sizes"); + for (auto Size : Splitter->sizes()) + { + s.writeCharacters(QString::number(Size) + " "); + } + s.writeEndElement(); + s.writeEndElement(); } else { - stream << internal::DockAreaMarker; CDockAreaWidget* DockArea = dynamic_cast(Widget); if (DockArea) { - DockArea->saveState(stream); + DockArea->saveState(s); } } } //============================================================================ -bool DockContainerWidgetPrivate::restoreSplitter(QDataStream& stream, +bool DockContainerWidgetPrivate::restoreSplitter(QXmlStreamReader& s, QWidget*& CreatedWidget, bool Testing) { - int Orientation; - int WidgetCount; - stream >> Orientation >> WidgetCount; + bool Ok; + int Orientation = s.attributes().value("Orientation").toInt(&Ok); + if (!Ok) + { + return false; + } + + int WidgetCount = s.attributes().value("Count").toInt(&Ok); + if (!Ok) + { + return false; + } qDebug() << "Restore NodeSplitter Orientation: " << Orientation << " WidgetCount: " << WidgetCount; QSplitter* Splitter = nullptr; @@ -355,15 +375,42 @@ bool DockContainerWidgetPrivate::restoreSplitter(QDataStream& stream, Splitter = internal::newSplitter((Qt::Orientation)Orientation); } bool Visible = false; - for (int i = 0; i < WidgetCount; ++i) + QList Sizes; + while (s.readNextStartElement()) { - QWidget* ChildNode; - if (!restoreChildNodes(stream, ChildNode, Testing)) + QWidget* ChildNode = nullptr; + bool Result = true; + if (s.name() == "Splitter") + { + Result = restoreSplitter(s, ChildNode, Testing); + } + else if (s.name() == "DockAreaWidget") + { + Result = restoreDockArea(s, ChildNode, Testing); + } + else if (s.name() == "Sizes") + { + QString sSizes = s.readElementText().trimmed(); + qDebug() << "Sizes: " << sSizes; + QTextStream TextStream(&sSizes); + while (!TextStream.atEnd()) + { + int value; + TextStream >> value; + Sizes.append(value); + } + } + else + { + s.skipCurrentElement(); + } + + if (!Result) { return false; } - if (Testing) + if (Testing || !ChildNode) { continue; } @@ -374,8 +421,11 @@ bool DockContainerWidgetPrivate::restoreSplitter(QDataStream& stream, Visible |= ChildNode->isVisibleTo(Splitter); } - QList Sizes; - stream >> Sizes; + if (Sizes.count() != WidgetCount) + { + return false; + } + if (!Testing) { if (!Splitter->count()) @@ -394,17 +444,27 @@ bool DockContainerWidgetPrivate::restoreSplitter(QDataStream& stream, { CreatedWidget = nullptr; } + return true; } //============================================================================ -bool DockContainerWidgetPrivate::restoreDockArea(QDataStream& stream, +bool DockContainerWidgetPrivate::restoreDockArea(QXmlStreamReader& s, QWidget*& CreatedWidget, bool Testing) { - int Tabs; - int CurrentIndex; - stream >> Tabs >> CurrentIndex; + bool Ok; + int Tabs = s.attributes().value("Tabs").toInt(&Ok); + if (!Ok) + { + return false; + } + + int CurrentIndex = s.attributes().value("CurrentIndex").toInt(&Ok); + if (!Ok) + { + return false; + } qDebug() << "Restore NodeDockArea Tabs: " << Tabs << " CurrentIndex: " << CurrentIndex; @@ -414,21 +474,27 @@ bool DockContainerWidgetPrivate::restoreDockArea(QDataStream& stream, DockArea = new CDockAreaWidget(DockManager, _this); } - for (int i = 0; i < Tabs; ++i) + while (s.readNextStartElement()) { - int Marker; - stream >> Marker; - if (Marker != internal::DockWidgetMarker) + if (s.name() != "DockWidget") + { + continue; + } + + auto ObjectName = s.attributes().value("ObjectName"); + if (ObjectName.isEmpty()) { return false; } - QString ObjectName; - bool Closed; - stream >> ObjectName >> Closed; - qDebug() << "Restore DockWidget " << ObjectName << " Closed: " << Closed; + bool Closed = s.attributes().value("Closed").toInt(&Ok); + if (!Ok) + { + return false; + } - CDockWidget* DockWidget = DockManager->findDockWidget(ObjectName); + s.skipCurrentElement(); + CDockWidget* DockWidget = DockManager->findDockWidget(ObjectName.toString()); if (!DockWidget || Testing) { continue; @@ -464,23 +530,30 @@ bool DockContainerWidgetPrivate::restoreDockArea(QDataStream& stream, //============================================================================ -bool DockContainerWidgetPrivate::restoreChildNodes(QDataStream& stream, +bool DockContainerWidgetPrivate::restoreChildNodes(QXmlStreamReader& s, QWidget*& CreatedWidget, bool Testing) { - int Marker; - stream >> Marker; - if (internal::SplitterMarker == Marker) + bool Result = true; + while (s.readNextStartElement()) { - return restoreSplitter(stream, CreatedWidget, Testing); - } - else if (internal::DockAreaMarker == Marker) - { - return restoreDockArea(stream, CreatedWidget, Testing); - } - else - { - return false; + if (s.name() == "Splitter") + { + Result = restoreSplitter(s, CreatedWidget, Testing); + qDebug() << "Splitter"; + } + else if (s.name() == "DockAreaWidget") + { + Result = restoreDockArea(s, CreatedWidget, Testing); + qDebug() << "DockAreaWidget"; + } + else + { + s.skipCurrentElement(); + qDebug() << "Unknown element"; + } } + + return Result; } @@ -879,37 +952,31 @@ QList CDockContainerWidget::openedDockAreas() const //============================================================================ -void CDockContainerWidget::saveState(QDataStream& stream) const +void CDockContainerWidget::saveState(QXmlStreamWriter& s) const { qDebug() << "CDockContainerWidget::saveState isFloating " << isFloating(); - stream << internal::ContainerMarker; - stream << isFloating(); + + s.writeStartElement("DockContainerWidget"); + s.writeAttribute("Floating", QString::number(isFloating() ? 1 : 0)); if (isFloating()) { CFloatingDockContainer* FloatingWidget = internal::findParent(this); - stream << FloatingWidget->saveGeometry(); + QByteArray Geometry = FloatingWidget->saveGeometry(); + s.writeTextElement("Geometry", Geometry.toHex(' ')); } - - d->saveChildNodesState(stream, d->RootSplitter); + d->saveChildNodesState(s, d->RootSplitter); + s.writeEndElement(); } //============================================================================ -bool CDockContainerWidget::restoreState(QDataStream& stream, bool Testing) +bool CDockContainerWidget::restoreState(QXmlStreamReader& s, bool Testing) { - bool IsFloating; - int Marker; - stream >> Marker; - if (Marker != internal::ContainerMarker) - { - return false; - } - - stream >> IsFloating; + bool IsFloating = s.attributes().value("Floating").toInt(); qDebug() << "Restore CDockContainerWidget Floating" << IsFloating; - QWidget* NewRootSplitter; + QWidget*NewRootSplitter {}; if (!Testing) { d->DockAreas.clear(); @@ -918,8 +985,19 @@ bool CDockContainerWidget::restoreState(QDataStream& stream, bool Testing) if (IsFloating) { qDebug() << "Restore floating widget"; - QByteArray Geometry; - stream >> Geometry; + if (!s.readNextStartElement() || s.name() != "Geometry") + { + return false; + } + + QByteArray GeometryString = s.readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).toLocal8Bit(); + QByteArray Geometry = QByteArray::fromHex(GeometryString); + std::cout << "Geometry: " << Geometry.toHex(' ').toStdString() << std::endl; + if (Geometry.isEmpty()) + { + return false; + } + if (!Testing) { CFloatingDockContainer* FloatingWidget = internal::findParent(this); @@ -928,7 +1006,7 @@ bool CDockContainerWidget::restoreState(QDataStream& stream, bool Testing) } } - if (!d->restoreChildNodes(stream, NewRootSplitter, Testing)) + if (!d->restoreChildNodes(s, NewRootSplitter, Testing)) { return false; } diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index a6565fd..0e385d2 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -34,6 +34,9 @@ #include "ads_globals.h" +class QXmlStreamWriter; +class QXmlStreamReader; + namespace ads { struct DockContainerWidgetPrivate; @@ -147,7 +150,7 @@ public: /** * Saves the state into the given stream */ - void saveState(QDataStream& Stream) const; + void saveState(QXmlStreamWriter& Stream) const; /** * Restores the state from given stream. @@ -155,7 +158,7 @@ public: * stream but does not restore anything. You can use this check for * faulty files before you start restoring the state */ - bool restoreState(QDataStream& Stream, bool Testing); + bool restoreState(QXmlStreamReader& Stream, bool Testing); /** * Dumps the layout for debugging purposes diff --git a/src/DockManager.cpp b/src/DockManager.cpp index fa926c0..dd27845 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -30,6 +30,8 @@ //============================================================================ #include "DockManager.h" +#include + #include #include #include @@ -38,6 +40,8 @@ #include #include #include +#include +#include #include "FloatingDockContainer.h" #include "DockOverlay.h" @@ -76,12 +80,12 @@ struct DockManagerPrivate /** * Restores the state */ - bool restoreState(const QByteArray &state, int version); + bool restoreState(const QByteArray &state, int version, bool Testing = internal::Restore); /** * Restores the container with the given index */ - bool restoreContainer(int Index, QDataStream& stream, bool Testing); + bool restoreContainer(int Index, QXmlStreamReader& stream, bool Testing); /** * Loads the stylesheet @@ -112,45 +116,13 @@ void DockManagerPrivate::loadStylesheet() //============================================================================ -bool DockManagerPrivate::checkFormat(const QByteArray &state, int version) +bool DockManagerPrivate::restoreContainer(int Index, QXmlStreamReader& stream, bool Testing) { - if (state.isEmpty()) - { - return false; - } - QByteArray sd = state; - QDataStream stream(&sd, QIODevice::ReadOnly); + if (Testing) + { + Index = 0; + } - int marker; - int v; - stream >> marker; - stream >> v; - - if (stream.status() != QDataStream::Ok || marker != internal::VersionMarker || v != version) - { - return false; - } - - int Result = true; - int ContainerCount; - stream >> ContainerCount; - int i; - for (i = 0; i < ContainerCount; ++i) - { - if (!Containers[0]->restoreState(stream, internal::RestoreTesting)) - { - Result = false; - break; - } - } - - return Result; -} - - -//============================================================================ -bool DockManagerPrivate::restoreContainer(int Index, QDataStream& stream, bool Testing) -{ if (Index >= Containers.count()) { CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this); @@ -165,45 +137,60 @@ bool DockManagerPrivate::restoreContainer(int Index, QDataStream& stream, bool T //============================================================================ -bool DockManagerPrivate::restoreState(const QByteArray &state, int version) +bool DockManagerPrivate::checkFormat(const QByteArray &state, int version) +{ + return restoreState(state, version, internal::RestoreTesting); +} + + +//============================================================================ +bool DockManagerPrivate::restoreState(const QByteArray &state, int version, + bool Testing) { if (state.isEmpty()) { return false; } - QByteArray sd = state; - QDataStream stream(&sd, QIODevice::ReadOnly); - - int marker; - int v; - stream >> marker; - stream >> v; - - if (stream.status() != QDataStream::Ok || marker != internal::VersionMarker || v != version) + QXmlStreamReader s(state); + s.readNextStartElement(); + if (s.name() != "QtAdvancedDockingSystem") { - return false; + return false; + } + qDebug() << s.attributes().value("Version"); + bool ok; + int v = s.attributes().value("Version").toInt(&ok); + if (!ok || v != version) + { + return false; } - int Result = true; - int ContainerCount; - stream >> ContainerCount; - qDebug() << "ContainerCount " << ContainerCount; - int i; - for (i = 0; i < ContainerCount; ++i) + bool Result = true; + int DockContainers = s.attributes().value("DockContainers").toInt(); + qDebug() << DockContainers; + int DockContainerCount = 0; + while (s.readNextStartElement()) { - Result = restoreContainer(i, stream, internal::Restore); - if (!Result) + if (s.name() == "DockContainerWidget") { - break; + Result = restoreContainer(DockContainerCount, s, Testing); + if (!Result) + { + break; + } + DockContainerCount++; } } - // Delete remaining empty floating widgets - int FloatingWidgetIndex = i - 1; - int DeleteCount = FloatingWidgets.count() - FloatingWidgetIndex; - for (int i = 0; i < DeleteCount; ++i) + if (!Testing) { - FloatingWidgets[FloatingWidgetIndex + i]->deleteLater(); + // Delete remaining empty floating widgets + int FloatingWidgetIndex = DockContainerCount - 1; + int DeleteCount = FloatingWidgets.count() - FloatingWidgetIndex; + for (int i = 0; i < DeleteCount; ++i) + { + FloatingWidgets[FloatingWidgetIndex + i]->deleteLater(); + } } return Result; @@ -309,17 +296,23 @@ unsigned int CDockManager::zOrderIndex() const //============================================================================ QByteArray CDockManager::saveState(int version) const { - QByteArray data; - QDataStream stream(&data, QIODevice::WriteOnly); - stream << internal::VersionMarker; - stream << version; + QByteArray xmldata; + QXmlStreamWriter s(&xmldata); + s.setAutoFormatting(true); + s.writeStartDocument(); + s.writeStartElement("QtAdvancedDockingSystem"); + s.writeAttribute("Version", QString::number(version)); + s.writeAttribute("DockContainers", QString::number(d->Containers.count())); + for (auto Container : d->Containers) + { + Container->saveState(s); + } - stream << d->Containers.count(); - for (auto Container : d->Containers) - { - Container->saveState(stream); - } - return data; + s.writeEndElement(); + s.writeEndDocument(); + + std::cout << xmldata.toStdString() << std::endl; + return xmldata; } diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index 8085e00..494ebed 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "DockWidgetTitleBar.h" #include "DockContainerWidget.h" @@ -361,11 +362,12 @@ void CDockWidget::setDockArea(CDockAreaWidget* DockArea) //============================================================================ -void CDockWidget::saveState(QDataStream& stream) const +void CDockWidget::saveState(QXmlStreamWriter& s) const { - stream << internal::DockWidgetMarker; - qDebug() << "CDockWidget::saveState " << objectName() << " closed " << d->Closed; - stream << objectName() << d->Closed; + s.writeStartElement("DockWidget"); + s.writeAttribute("ObjectName", objectName()); + s.writeAttribute("Closed", QString::number(d->Closed ? 1 : 0)); + s.writeEndElement(); } diff --git a/src/DockWidget.h b/src/DockWidget.h index edb19e1..dd86551 100644 --- a/src/DockWidget.h +++ b/src/DockWidget.h @@ -32,6 +32,8 @@ //============================================================================ #include +class QXmlStreamWriter; + namespace ads { struct DockWidgetPrivate; @@ -81,7 +83,7 @@ protected: /** * Saves the state into the given stream */ - void saveState(QDataStream& Stream) const; + void saveState(QXmlStreamWriter& Stream) const; /** * This is a helper function for the dock manager to flag this widget diff --git a/src/FloatingDockContainer.cpp b/src/FloatingDockContainer.cpp index 85fb12a..d29be25 100644 --- a/src/FloatingDockContainer.cpp +++ b/src/FloatingDockContainer.cpp @@ -439,7 +439,7 @@ void CFloatingDockContainer::onDockAreaCurrentChanged(int Index) //============================================================================ -bool CFloatingDockContainer::restoreState(QDataStream& Stream, bool Testing) +bool CFloatingDockContainer::restoreState(QXmlStreamReader& Stream, bool Testing) { if (!d->DockContainer->restoreState(Stream, Testing)) { diff --git a/src/FloatingDockContainer.h b/src/FloatingDockContainer.h index 36c7417..072271b 100644 --- a/src/FloatingDockContainer.h +++ b/src/FloatingDockContainer.h @@ -31,6 +31,8 @@ //============================================================================ #include +class QXmlStreamReader; + namespace ads { struct FloatingDockContainerPrivate; @@ -110,7 +112,7 @@ public: * stream but does not restore anything. You can use this check for * faulty files before you start restoring the state */ - bool restoreState(QDataStream& Stream, bool Testing); + bool restoreState(QXmlStreamReader& Stream, bool Testing); }; // class FloatingDockContainer } // namespace ads