From 7f5e393cfb8d0b5ae361e9ed57836e8ef5b671ed Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Fri, 3 Feb 2017 22:56:08 +0100 Subject: [PATCH] Startet refactoring common container stuff into CContainerWidget --- .../AdvancedDockingSystem.pri | 10 +- .../include/ads/ContainerWidget.h | 187 +-- .../include/ads/MainContainerWidget.h | 191 +++ AdvancedDockingSystem/src/API.cpp | 2 +- AdvancedDockingSystem/src/ContainerWidget.cpp | 1276 +--------------- AdvancedDockingSystem/src/FloatingWidget.cpp | 2 +- .../src/MainContainerWidget.cpp | 1278 +++++++++++++++++ AdvancedDockingSystem/src/SectionContent.cpp | 2 +- .../src/SectionTitleWidget.cpp | 2 +- AdvancedDockingSystem/src/SectionWidget.cpp | 2 +- .../src/dialogs/SectionContentListModel.h | 2 +- .../src/dialogs/SectionContentListWidget.h | 2 +- AdvancedDockingSystemDemo/src/mainwindow.h | 2 +- 13 files changed, 1529 insertions(+), 1429 deletions(-) create mode 100644 AdvancedDockingSystem/include/ads/MainContainerWidget.h create mode 100644 AdvancedDockingSystem/src/MainContainerWidget.cpp diff --git a/AdvancedDockingSystem/AdvancedDockingSystem.pri b/AdvancedDockingSystem/AdvancedDockingSystem.pri index 76e9016..b6a92ad 100644 --- a/AdvancedDockingSystem/AdvancedDockingSystem.pri +++ b/AdvancedDockingSystem/AdvancedDockingSystem.pri @@ -1,7 +1,7 @@ SOURCES += \ $$PWD/src/API.cpp \ - $$PWD/src/ContainerWidget.cpp \ + $$PWD/src/MainContainerWidget.cpp \ $$PWD/src/SectionWidget.cpp \ $$PWD/src/SectionContent.cpp \ $$PWD/src/SectionTitleWidget.cpp \ @@ -9,11 +9,12 @@ SOURCES += \ $$PWD/src/DropOverlay.cpp \ $$PWD/src/FloatingWidget.cpp \ $$PWD/src/Internal.cpp \ - $$PWD/src/Serialization.cpp + $$PWD/src/Serialization.cpp \ + $$PWD/src/ContainerWidget.cpp HEADERS += \ $$PWD/include/ads/API.h \ - $$PWD/include/ads/ContainerWidget.h \ + $$PWD/include/ads/MainContainerWidget.h \ $$PWD/include/ads/SectionWidget.h \ $$PWD/include/ads/SectionContent.h \ $$PWD/include/ads/SectionTitleWidget.h \ @@ -21,4 +22,5 @@ HEADERS += \ $$PWD/include/ads/DropOverlay.h \ $$PWD/include/ads/FloatingWidget.h \ $$PWD/include/ads/Internal.h \ - $$PWD/include/ads/Serialization.h + $$PWD/include/ads/Serialization.h \ + $$PWD/include/ads/ContainerWidget.h diff --git a/AdvancedDockingSystem/include/ads/ContainerWidget.h b/AdvancedDockingSystem/include/ads/ContainerWidget.h index 3f12d62..fa1d994 100644 --- a/AdvancedDockingSystem/include/ads/ContainerWidget.h +++ b/AdvancedDockingSystem/include/ads/ContainerWidget.h @@ -1,15 +1,16 @@ -#ifndef ADS_CONTAINERWIDGET_H -#define ADS_CONTAINERWIDGET_H +#ifndef ContainerWidgetH +#define ContainerWidgetH +//============================================================================ +/// \file ContainerWidget.h +/// \author Uwe Kindler +/// \date 03.02.2017 +/// \brief Declaration of CContainerWidget +//============================================================================ -#include -#include -#include +//============================================================================ +// INCLUDES +//============================================================================ #include -#include -#include - -class QPoint; -class QMenu; #include "ads/API.h" #include "ads/Internal.h" @@ -18,18 +19,16 @@ class QMenu; #include "ads/Serialization.h" #include "ads/DropOverlay.h" -ADS_NAMESPACE_BEGIN +namespace ads +{ class SectionWidget; class DropOverlay; class InternalContentData; - - -/*! - * ContainerWidget is the main container to provide the docking - * functionality. It manages multiple sections with all possible areas. +/** + * @brief */ -class ADS_EXPORT_API MainContainerWidget : public QFrame +class CContainerWidget : public QFrame { Q_OBJECT @@ -40,152 +39,10 @@ class ADS_EXPORT_API MainContainerWidget : public QFrame friend class ContainerWidgetPrivate; public: - explicit MainContainerWidget(QWidget *parent = NULL); - virtual ~MainContainerWidget(); - - // - // Public API - // - - /*! - * Adds the section-content sc to this container-widget into the section-widget sw. - * If sw is not NULL, the area is used to indicate how the content should be arranged. - * Returns a pointer to the SectionWidget of the added SectionContent. Do not use it for anything else than adding more - * SectionContent elements with this method. - */ - SectionWidget* addSectionContent(const SectionContent::RefPtr& sc, SectionWidget* sw = NULL, DropArea area = CenterDropArea); - - /*! - * Completely removes the sc from this ContainerWidget. - * This container will no longer hold a reference to the content. - * The content can be safely deleted. - */ - bool removeSectionContent(const SectionContent::RefPtr& sc); - - /*! - * Shows the specific SectionContent in UI. - * Independed of the current state, whether it is used inside a section or is floating. - */ - bool showSectionContent(const SectionContent::RefPtr& sc); - - /*! - * Closes the specified SectionContent from UI. - * Independed of the current state, whether it is used inside a section or is floating. - */ - bool hideSectionContent(const SectionContent::RefPtr& sc); - - /*! - * Selects the specific SectionContent as current, if it is part of a SectionWidget. - * If SC is floating, it does nothing (or should we show it?) - */ - bool raiseSectionContent(const SectionContent::RefPtr& sc); - - /*! - * Indicates whether the SectionContent sc is visible. - */ - bool isSectionContentVisible(const SectionContent::RefPtr& sc); - - /*! - * Creates a QMenu based on available SectionContents. - * The caller is responsible to delete the menu. - */ - QMenu* createContextMenu() const; - - /*! - * Serializes the current state of contents and returns it as a plain byte array. - * \see restoreState(const QByteArray&) - */ - QByteArray saveState() const; - - /*! - * Deserilizes the state of contents from data, which was written with saveState(). - * \see saveState() - */ - bool restoreState(const QByteArray& data); - - // - // Advanced Public API - // You usually should not need access to this methods - // - /*! - * \brief contents - * \return List of known SectionContent for this ContainerWidget. - */ - QList contents() const; - - /** - * Access function for the section drop overlay - */ - QPointer sectionDropOverlay() const; - - /** - * Filters the events of the floating widgets - */ - virtual bool event(QEvent *e) override; - -private: - // - // Internal Stuff Begins Here - // - - SectionWidget* newSectionWidget(); - SectionWidget* dropContent(const InternalContentData& data, SectionWidget* targetSection, DropArea area, bool autoActive = true); - void addSectionWidget(SectionWidget* section); - SectionWidget* sectionWidgetAt(const QPoint& pos) const; - SectionWidget* dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append); - - // Serialization - QByteArray saveHierarchy() const; - void saveFloatingWidgets(QDataStream& out) const; - void saveSectionWidgets(QDataStream& out, QWidget* widget) const; - - bool saveSectionIndex(ADS_NS_SER::SectionIndexData &sid) const; - - bool restoreHierarchy(const QByteArray& data); - bool restoreFloatingWidgets(QDataStream& in, int version, QList& floatings); - bool restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList& sections, QList& contentsToHide); - - bool takeContent(const SectionContent::RefPtr& sc, InternalContentData& data); - FloatingWidget* startFloating(SectionWidget* sectionwidget, int ContentUid, const QPoint& TargetPos); - void hideContainerOverlay(); - SectionWidget* insertNewSectionWidget(const InternalContentData& data, - SectionWidget* targetSection, SectionWidget* ret, Qt::Orientation Orientation, int InsertIndexOffset); - void moveFloatingWidget(const QPoint& TargetPos); - void dropFloatingWidget(FloatingWidget* FloatingWidget, const QPoint& TargetPos); - -private slots: - void onActiveTabChanged(); - void onActionToggleSectionContentVisibility(bool visible); - -signals: - void orientationChanged(); - - /*! - * Emits whenever the "isActiveTab" state of a SectionContent changes. - * Whenever the users sets another tab as active, this signal gets invoked - * for the old tab and the new active tab (the order is unspecified). - */ - void activeTabChanged(const SectionContent::RefPtr& sc, bool active); - - /*! - * Emits whenever the visibility of a SectionContent changes. - * \see showSectionContent(), hideSectionContent() - * \since 0.2 - */ - void sectionContentVisibilityChanged(const SectionContent::RefPtr& sc, bool visible); - -private: - QList m_Sections; - QList m_Floatings; - QHash m_HiddenSectionContents; - - - // Helper lookup maps, restricted to this container. - QHash m_SectionContentIdMap; - QHash m_SectionContentNameMap; - QHash m_SectionWidgetIdMap; - + explicit CContainerWidget(QWidget *parent = nullptr); + virtual ~CContainerWidget(); +protected: // Layout stuff QGridLayout* m_MainLayout = nullptr; Qt::Orientation m_Orientation = Qt::Horizontal; @@ -197,5 +54,7 @@ private: QPointer m_ContainerDropOverlay; }; -ADS_NAMESPACE_END -#endif +} // namespace ads + +//--------------------------------------------------------------------------- +#endif // ContainerWidgetH diff --git a/AdvancedDockingSystem/include/ads/MainContainerWidget.h b/AdvancedDockingSystem/include/ads/MainContainerWidget.h new file mode 100644 index 0000000..a7d572d --- /dev/null +++ b/AdvancedDockingSystem/include/ads/MainContainerWidget.h @@ -0,0 +1,191 @@ +#ifndef ADS_CONTAINERWIDGET_H +#define ADS_CONTAINERWIDGET_H + +#include +#include +#include +#include +#include +#include + +class QPoint; +class QMenu; + +#include "ads/API.h" +#include "ads/Internal.h" +#include "ads/SectionContent.h" +#include "ads/FloatingWidget.h" +#include "ads/Serialization.h" +#include "ads/DropOverlay.h" +#include "ads/ContainerWidget.h" + +ADS_NAMESPACE_BEGIN +class SectionWidget; +class DropOverlay; +class InternalContentData; + + + +/*! + * ContainerWidget is the main container to provide the docking + * functionality. It manages multiple sections with all possible areas. + */ +class ADS_EXPORT_API MainContainerWidget : public CContainerWidget +{ + Q_OBJECT + + friend class SectionContent; + friend class SectionWidget; + friend class FloatingWidget; + friend class SectionTitleWidget; + friend class ContainerWidgetPrivate; + +public: + explicit MainContainerWidget(QWidget *parent = nullptr); + virtual ~MainContainerWidget(); + + // + // Public API + // + + /*! + * Adds the section-content sc to this container-widget into the section-widget sw. + * If sw is not NULL, the area is used to indicate how the content should be arranged. + * Returns a pointer to the SectionWidget of the added SectionContent. Do not use it for anything else than adding more + * SectionContent elements with this method. + */ + SectionWidget* addSectionContent(const SectionContent::RefPtr& sc, SectionWidget* sw = NULL, DropArea area = CenterDropArea); + + /*! + * Completely removes the sc from this ContainerWidget. + * This container will no longer hold a reference to the content. + * The content can be safely deleted. + */ + bool removeSectionContent(const SectionContent::RefPtr& sc); + + /*! + * Shows the specific SectionContent in UI. + * Independed of the current state, whether it is used inside a section or is floating. + */ + bool showSectionContent(const SectionContent::RefPtr& sc); + + /*! + * Closes the specified SectionContent from UI. + * Independed of the current state, whether it is used inside a section or is floating. + */ + bool hideSectionContent(const SectionContent::RefPtr& sc); + + /*! + * Selects the specific SectionContent as current, if it is part of a SectionWidget. + * If SC is floating, it does nothing (or should we show it?) + */ + bool raiseSectionContent(const SectionContent::RefPtr& sc); + + /*! + * Indicates whether the SectionContent sc is visible. + */ + bool isSectionContentVisible(const SectionContent::RefPtr& sc); + + /*! + * Creates a QMenu based on available SectionContents. + * The caller is responsible to delete the menu. + */ + QMenu* createContextMenu() const; + + /*! + * Serializes the current state of contents and returns it as a plain byte array. + * \see restoreState(const QByteArray&) + */ + QByteArray saveState() const; + + /*! + * Deserilizes the state of contents from data, which was written with saveState(). + * \see saveState() + */ + bool restoreState(const QByteArray& data); + + // + // Advanced Public API + // You usually should not need access to this methods + // + /*! + * \brief contents + * \return List of known SectionContent for this ContainerWidget. + */ + QList contents() const; + + /** + * Access function for the section drop overlay + */ + QPointer sectionDropOverlay() const; + + /** + * Filters the events of the floating widgets + */ + virtual bool event(QEvent *e) override; + +private: + // + // Internal Stuff Begins Here + // + + SectionWidget* newSectionWidget(); + SectionWidget* dropContent(const InternalContentData& data, SectionWidget* targetSection, DropArea area, bool autoActive = true); + void addSectionWidget(SectionWidget* section); + SectionWidget* sectionWidgetAt(const QPoint& pos) const; + SectionWidget* dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append); + + // Serialization + QByteArray saveHierarchy() const; + void saveFloatingWidgets(QDataStream& out) const; + void saveSectionWidgets(QDataStream& out, QWidget* widget) const; + + bool saveSectionIndex(ADS_NS_SER::SectionIndexData &sid) const; + + bool restoreHierarchy(const QByteArray& data); + bool restoreFloatingWidgets(QDataStream& in, int version, QList& floatings); + bool restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList& sections, QList& contentsToHide); + + bool takeContent(const SectionContent::RefPtr& sc, InternalContentData& data); + FloatingWidget* startFloating(SectionWidget* sectionwidget, int ContentUid, const QPoint& TargetPos); + void hideContainerOverlay(); + SectionWidget* insertNewSectionWidget(const InternalContentData& data, + SectionWidget* targetSection, SectionWidget* ret, Qt::Orientation Orientation, int InsertIndexOffset); + void moveFloatingWidget(const QPoint& TargetPos); + void dropFloatingWidget(FloatingWidget* FloatingWidget, const QPoint& TargetPos); + +private slots: + void onActiveTabChanged(); + void onActionToggleSectionContentVisibility(bool visible); + +signals: + void orientationChanged(); + + /*! + * Emits whenever the "isActiveTab" state of a SectionContent changes. + * Whenever the users sets another tab as active, this signal gets invoked + * for the old tab and the new active tab (the order is unspecified). + */ + void activeTabChanged(const SectionContent::RefPtr& sc, bool active); + + /*! + * Emits whenever the visibility of a SectionContent changes. + * \see showSectionContent(), hideSectionContent() + * \since 0.2 + */ + void sectionContentVisibilityChanged(const SectionContent::RefPtr& sc, bool visible); + +private: + QList m_Sections; + QList m_Floatings; + QHash m_HiddenSectionContents; + + + // Helper lookup maps, restricted to this container. + QHash m_SectionContentIdMap; + QHash m_SectionContentNameMap; + QHash m_SectionWidgetIdMap; +}; + +ADS_NAMESPACE_END +#endif diff --git a/AdvancedDockingSystem/src/API.cpp b/AdvancedDockingSystem/src/API.cpp index 18de06b..86786b8 100644 --- a/AdvancedDockingSystem/src/API.cpp +++ b/AdvancedDockingSystem/src/API.cpp @@ -1,4 +1,4 @@ -#include +#include #include "ads/API.h" #include diff --git a/AdvancedDockingSystem/src/ContainerWidget.cpp b/AdvancedDockingSystem/src/ContainerWidget.cpp index 6226252..549ec99 100644 --- a/AdvancedDockingSystem/src/ContainerWidget.cpp +++ b/AdvancedDockingSystem/src/ContainerWidget.cpp @@ -1,3 +1,15 @@ +//============================================================================ +/// \file ContainerWidget.cpp +/// \author Uwe Kindler +/// \date 03.02.2017 +/// \brief Implementation of CContainerWidget +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "ads/ContainerWidget.h" + #include #include #include @@ -17,25 +29,11 @@ #include "ads/DropOverlay.h" #include "ads/Serialization.h" -#include -#include "../include/ads/ContainerWidget.h" - -ADS_NAMESPACE_BEGIN - - -static QSplitter* newSplitter(Qt::Orientation orientation = Qt::Horizontal, QWidget* parent = 0) +namespace ads { - QSplitter* s = new QSplitter(orientation, parent); - s->setProperty("ads-splitter", QVariant(true)); - s->setChildrenCollapsible(false); - s->setOpaqueResize(false); - return s; -} - - - -MainContainerWidget::MainContainerWidget(QWidget *parent) : - QFrame(parent) +//============================================================================ +CContainerWidget::CContainerWidget(QWidget *parent) + : QFrame(parent) { m_SectionDropOverlay = new DropOverlay(this, DropOverlay::ModeSectionOverlay); m_ContainerDropOverlay = new DropOverlay(this, DropOverlay::ModeContainerOverlay); @@ -46,1243 +44,15 @@ MainContainerWidget::MainContainerWidget(QWidget *parent) : m_MainLayout->setContentsMargins(0, 1, 0, 0); m_MainLayout->setSpacing(0); setLayout(m_MainLayout); - setAcceptDrops(true); } -MainContainerWidget::~MainContainerWidget() + +//============================================================================ +CContainerWidget::~CContainerWidget() { - // Note: It's required to delete in 2 steps - // Remove from list, and then delete. - // Because the destrcutor of objects wants to modify the current - // iterating list as well... :-/ - while (!m_Sections.isEmpty()) - { - SectionWidget* sw = m_Sections.takeLast(); - delete sw; - } - while (!m_Floatings.isEmpty()) - { - FloatingWidget* fw = m_Floatings.takeLast(); - delete fw; - } - m_SectionContentIdMap.clear(); - m_SectionContentNameMap.clear(); - m_SectionWidgetIdMap.clear(); + } +} // namespace ads - -SectionWidget* MainContainerWidget::addSectionContent(const SectionContent::RefPtr& sc, SectionWidget* sw, DropArea area) -{ - ADS_Expects(!sc.isNull()); - - // Drop it based on "area" - InternalContentData data; - data.content = sc; - data.titleWidget = new SectionTitleWidget(sc, NULL); - data.contentWidget = new SectionContentWidget(sc, NULL); - - connect(data.titleWidget, SIGNAL(activeTabChanged()), this, SLOT(onActiveTabChanged())); - return dropContent(data, sw, area, false); -} - -bool MainContainerWidget::removeSectionContent(const SectionContent::RefPtr& sc) -{ - ADS_Expects(!sc.isNull()); - - // Hide the content. - // The hideSectionContent() automatically deletes no longer required SectionWidget objects. - if (!hideSectionContent(sc)) - return false; - - // Begin of ugly work arround. - // TODO The hideSectionContent() method should take care of deleting FloatingWidgets and SectionWidgets, - // but only cares about SectionWidgets right now. So we need to check whether it was a FloatingWidget - // and delete it. - bool found = false; - for (int i = 0; i < m_Floatings.count(); ++i) - { - FloatingWidget* fw = m_Floatings.at(i); - InternalContentData data; - if (!(found = fw->takeContent(data))) - continue; - m_Floatings.removeAll(fw); - delete fw; - delete data.titleWidget; - delete data.contentWidget; - break; - } // End of ugly work arround. - - // Get from hidden contents and delete associated internal stuff. - if (!m_HiddenSectionContents.contains(sc->uid())) - { - qFatal("Something went wrong... The content should have been there :-/"); - return false; - } - - // Delete internal objects. - HiddenSectionItem hsi = m_HiddenSectionContents.take(sc->uid()); - delete hsi.data.titleWidget; - delete hsi.data.contentWidget; - - // Hide the custom widgets of SectionContent. - // ... should we? ... - - return true; -} - -bool MainContainerWidget::showSectionContent(const SectionContent::RefPtr& sc) -{ - ADS_Expects(!sc.isNull()); - - // Search SC in floatings - for (int i = 0; i < m_Floatings.count(); ++i) - { - FloatingWidget* fw = m_Floatings.at(i); - const bool found = fw->content()->uid() == sc->uid(); - if (!found) - continue; - fw->setVisible(true); - fw->_titleWidget->setVisible(true); - fw->_contentWidget->setVisible(true); - emit sectionContentVisibilityChanged(sc, true); - return true; - } - - // Search SC in hidden sections - // Try to show them in the last position, otherwise simply append - // it to the first section (or create a new section?) - if (m_HiddenSectionContents.contains(sc->uid())) - { - const HiddenSectionItem hsi = m_HiddenSectionContents.take(sc->uid()); - hsi.data.titleWidget->setVisible(true); - hsi.data.contentWidget->setVisible(true); - SectionWidget* sw = nullptr; - if (hsi.preferredSectionId > 0 && (sw = m_SectionWidgetIdMap.value(hsi.preferredSectionId)) != nullptr) - { - sw->addContent(hsi.data, true); - emit sectionContentVisibilityChanged(sc, true); - return true; - } - else if (m_Sections.size() > 0 && (sw = m_Sections.first()) != NULL) - { - sw->addContent(hsi.data, true); - emit sectionContentVisibilityChanged(sc, true); - return true; - } - else - { - sw = newSectionWidget(); - addSectionWidget(sw); - sw->addContent(hsi.data, true); - emit sectionContentVisibilityChanged(sc, true); - return true; - } - } - - // Already visible? - // TODO - qWarning("Unable to show SectionContent, don't know where 8-/ (already visible?)"); - return false; -} - -bool MainContainerWidget::hideSectionContent(const SectionContent::RefPtr& sc) -{ - ADS_Expects(!sc.isNull()); - - // Search SC in floatings - // We can simply hide floatings, nothing else required. - for (int i = 0; i < m_Floatings.count(); ++i) - { - const bool found = m_Floatings.at(i)->content()->uid() == sc->uid(); - if (!found) - continue; - m_Floatings.at(i)->setVisible(false); - emit sectionContentVisibilityChanged(sc, false); - return true; - } - - // Search SC in sections - // It's required to remove the SC from SW completely and hold it in a - // separate list as long as a "showSectionContent" gets called for the SC again. - // In case that the SW does not have any other SCs, we need to delete it. - for (int i = 0; i < m_Sections.count(); ++i) - { - SectionWidget* sw = m_Sections.at(i); - const bool found = sw->indexOfContent(sc) >= 0; - if (!found) - continue; - - HiddenSectionItem hsi; - hsi.preferredSectionId = sw->uid(); - hsi.preferredSectionIndex = sw->indexOfContent(sc); - if (!sw->takeContent(sc->uid(), hsi.data)) - return false; - - hsi.data.titleWidget->setVisible(false); - hsi.data.contentWidget->setVisible(false); - m_HiddenSectionContents.insert(sc->uid(), hsi); - - if (sw->contents().isEmpty()) - { - delete sw; - sw = NULL; - deleteEmptySplitter(this); - } - emit sectionContentVisibilityChanged(sc, false); - return true; - } - - // Search SC in hidden elements - // The content may already be hidden - if (m_HiddenSectionContents.contains(sc->uid())) - return true; - - qFatal("Unable to hide SectionContent, don't know this one 8-/"); - return false; -} - -bool MainContainerWidget::raiseSectionContent(const SectionContent::RefPtr& sc) -{ - ADS_Expects(!sc.isNull()); - - // Search SC in sections - for (int i = 0; i < m_Sections.count(); ++i) - { - SectionWidget* sw = m_Sections.at(i); - int index = sw->indexOfContent(sc); - if (index < 0) - continue; - sw->setCurrentIndex(index); - return true; - } - - // Search SC in floatings - for (int i = 0; i < m_Floatings.size(); ++i) - { - FloatingWidget* fw = m_Floatings.at(i); - if (fw->content()->uid() != sc->uid()) - continue; - fw->setVisible(true); - fw->raise(); - return true; - } - - // Search SC in hidden - if (m_HiddenSectionContents.contains(sc->uid())) - return showSectionContent(sc); - - qFatal("Unable to hide SectionContent, don't know this one 8-/"); - return false; -} - -bool MainContainerWidget::isSectionContentVisible(const SectionContent::RefPtr& sc) -{ - ADS_Expects(!sc.isNull()); - - // Search SC in floatings - for (int i = 0; i < m_Floatings.count(); ++i) - { - const bool found = m_Floatings.at(i)->content()->uid() == sc->uid(); - if (!found) - continue; - return m_Floatings.at(i)->isVisible(); - } - - // Search SC in sections - for (int i = 0; i < m_Sections.count(); ++i) - { - SectionWidget* sw = m_Sections.at(i); - const int index = sw->indexOfContent(sc); - if (index < 0) - continue; - return true; - } - - // Search SC in hidden - if (m_HiddenSectionContents.contains(sc->uid())) - return false; - - qWarning() << "SectionContent is not a part of this ContainerWidget:" << sc->uniqueName(); - return false; -} - -QMenu* MainContainerWidget::createContextMenu() const -{ - // Fill map with actions (sorted by key!) - QMap actions; - - // Visible contents of sections - for (int i = 0; i < m_Sections.size(); ++i) - { - const SectionWidget* sw = m_Sections.at(i); - const QList& contents = sw->contents(); - foreach (const SectionContent::RefPtr& sc, contents) - { - QAction* a = new QAction(QIcon(), sc->visibleTitle(), NULL); - a->setObjectName(QString("ads-action-sc-%1").arg(QString::number(sc->uid()))); - a->setProperty("uid", sc->uid()); - a->setProperty("type", "section"); - a->setCheckable(true); - a->setChecked(true); - a->setEnabled(sc->flags().testFlag(SectionContent::Closeable)); - connect(a, SIGNAL(toggled(bool)), this, SLOT(onActionToggleSectionContentVisibility(bool))); - actions.insert(a->text(), a); - } - } - - // Hidden contents of sections - QHashIterator hiddenIter(m_HiddenSectionContents); - while (hiddenIter.hasNext()) - { - hiddenIter.next(); - const SectionContent::RefPtr sc = hiddenIter.value().data.content; - - QAction* a = new QAction(QIcon(), sc->visibleTitle(), NULL); - a->setObjectName(QString("ads-action-sc-%1").arg(QString::number(sc->uid()))); - a->setProperty("uid", sc->uid()); - a->setProperty("type", "section"); - a->setCheckable(true); - a->setChecked(false); - connect(a, SIGNAL(toggled(bool)), this, SLOT(onActionToggleSectionContentVisibility(bool))); - actions.insert(a->text(), a); - } - - // Floating contents - for (int i = 0; i < m_Floatings.size(); ++i) - { - const FloatingWidget* fw = m_Floatings.at(i); - const SectionContent::RefPtr sc = fw->content(); - - QAction* a = new QAction(QIcon(), sc->visibleTitle(), NULL); - a->setObjectName(QString("ads-action-sc-%1").arg(QString::number(sc->uid()))); - a->setProperty("uid", sc->uid()); - a->setProperty("type", "floating"); - a->setCheckable(true); - a->setChecked(fw->isVisible()); - connect(a, SIGNAL(toggled(bool)), this, SLOT(onActionToggleSectionContentVisibility(bool))); - actions.insert(a->text(), a); - } - - // Create menu from "actions" - QMenu* m = new QMenu(NULL); - m->addActions(actions.values()); - return m; -} - -QByteArray MainContainerWidget::saveState() const -{ - ADS_NS_SER::InMemoryWriter writer; - - // Hierarchy data. - const QByteArray hierarchyData = saveHierarchy(); - if (!hierarchyData.isEmpty()) - { - writer.write(ADS_NS_SER::ET_Hierarchy, hierarchyData); - } - - // SectionIndex data. - ADS_NS_SER::SectionIndexData sid; - if (saveSectionIndex(sid)) - { - writer.write(sid); - } - - if (writer.offsetsCount() == 0) - return QByteArray(); - return writer.toByteArray(); -} - -bool MainContainerWidget::restoreState(const QByteArray& data) -{ - if (data.isEmpty()) - return false; - - ADS_NS_SER::InMemoryReader reader(data); - if (!reader.initReadHeader()) - return false; - - // Basic hierarchy data. - QByteArray hierarchyData; - if (reader.read(ADS_NS_SER::ET_Hierarchy, hierarchyData)) - { - restoreHierarchy(hierarchyData); - } - return true; -} - - -QList MainContainerWidget::contents() const -{ - QList wl = m_SectionContentIdMap.values(); - QList sl; - for (int i = 0; i < wl.count(); ++i) - { - const SectionContent::RefPtr sc = wl.at(i).toStrongRef(); - if (sc) - sl.append(sc); - } - return sl; -} - -QPointer MainContainerWidget::sectionDropOverlay() const -{ - return m_SectionDropOverlay; -} - -/////////////////////////////////////////////////////////////////////// -// PRIVATE API BEGINS HERE -/////////////////////////////////////////////////////////////////////// - -SectionWidget* MainContainerWidget::newSectionWidget() -{ - SectionWidget* sw = new SectionWidget(this); - m_Sections.append(sw); - return sw; -} - -SectionWidget* MainContainerWidget::insertNewSectionWidget( - const InternalContentData& data, SectionWidget* targetSection, SectionWidget* ret, - Qt::Orientation Orientation, int InsertIndexOffset) -{ - QSplitter* targetSectionSplitter = findParentSplitter(targetSection); - SectionWidget* sw = newSectionWidget(); - sw->addContent(data, true); - if (targetSectionSplitter->orientation() == Orientation) - { - const int index = targetSectionSplitter->indexOf(targetSection); - targetSectionSplitter->insertWidget(index + InsertIndexOffset, sw); - } - else - { - const int index = targetSectionSplitter->indexOf(targetSection); - QSplitter* s = newSplitter(Orientation); - s->addWidget(sw); - s->addWidget(targetSection); - targetSectionSplitter->insertWidget(index, s); - } - ret = sw; - return ret; -} - -SectionWidget* MainContainerWidget::dropContent(const InternalContentData& data, SectionWidget* targetSectionWidget, DropArea area, bool autoActive) -{ - ADS_Expects(targetSection != NULL); - - SectionWidget* section_widget = nullptr; - - // If no sections exists yet, create a default one and always drop into it. - if (m_Sections.isEmpty()) - { - targetSectionWidget = newSectionWidget(); - addSectionWidget(targetSectionWidget); - area = CenterDropArea; - } - - // Drop on outer area - if (!targetSectionWidget) - { - switch (area) - { - case TopDropArea:return dropContentOuterHelper(m_MainLayout, data, Qt::Vertical, false); - case RightDropArea: return dropContentOuterHelper(m_MainLayout, data, Qt::Horizontal, true); - case CenterDropArea: - case BottomDropArea:return dropContentOuterHelper(m_MainLayout, data, Qt::Vertical, true); - case LeftDropArea: return dropContentOuterHelper(m_MainLayout, data, Qt::Horizontal, false); - default: - return nullptr; - } - return section_widget; - } - - // Drop logic based on area. - switch (area) - { - case TopDropArea:return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Vertical, 0); - case RightDropArea: return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Horizontal, 1); - case BottomDropArea: return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Vertical, 1); - case LeftDropArea: return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Horizontal, 0); - case CenterDropArea: - targetSectionWidget->addContent(data, autoActive); - return targetSectionWidget; - - default: - break; - } - return section_widget; -} - -void MainContainerWidget::addSectionWidget(SectionWidget* section) -{ - ADS_Expects(section != NULL); - - // Create default splitter. - if (!m_Splitter) - { - m_Splitter = newSplitter(m_Orientation); - m_MainLayout->addWidget(m_Splitter, 0, 0); - } - if (m_Splitter->indexOf(section) != -1) - { - qWarning() << Q_FUNC_INFO << QString("Section has already been added"); - return; - } - m_Splitter->addWidget(section); -} - -SectionWidget* MainContainerWidget::sectionWidgetAt(const QPoint& pos) const -{ - const QPoint gpos = mapToGlobal(pos); - for (int i = 0; i < m_Sections.size(); ++i) - { - SectionWidget* sw = m_Sections[i]; - if (sw->rect().contains(sw->mapFromGlobal(gpos))) - { - return sw; - } - } - return 0; -} - -SectionWidget* MainContainerWidget::dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append) -{ - ADS_Expects(l != NULL); - - SectionWidget* sw = newSectionWidget(); - sw->addContent(data, true); - - QSplitter* oldsp = findImmediateSplitter(this); - if (!oldsp) - { - QSplitter* sp = newSplitter(orientation); - if (l->count() > 0) - { - qWarning() << "Still items in layout. This should never happen."; - QLayoutItem* li = l->takeAt(0); - delete li; - } - l->addWidget(sp); - sp->addWidget(sw); - } - else if (oldsp->orientation() == orientation - || oldsp->count() == 1) - { - oldsp->setOrientation(orientation); - if (append) - oldsp->addWidget(sw); - else - oldsp->insertWidget(0, sw); - } - else - { - QSplitter* sp = newSplitter(orientation); - if (append) - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - QLayoutItem* li = l->replaceWidget(oldsp, sp); - sp->addWidget(oldsp); - sp->addWidget(sw); - delete li; -#else - int index = l->indexOf(oldsp); - QLayoutItem* li = l->takeAt(index); - l->addWidget(sp); - sp->addWidget(oldsp); - sp->addWidget(sw); - delete li; -#endif - } - else - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - sp->addWidget(sw); - QLayoutItem* li = l->replaceWidget(oldsp, sp); - sp->addWidget(oldsp); - delete li; -#else - sp->addWidget(sw); - int index = l->indexOf(oldsp); - QLayoutItem* li = l->takeAt(index); - l->addWidget(sp); - sp->addWidget(oldsp); - delete li; -#endif - } - } - return sw; -} - -QByteArray MainContainerWidget::saveHierarchy() const -{ - /* - # Data Format - - quint32 Magic - quint32 Version - - int Number of floating widgets - LOOP Floating widgets - QString Unique name of content - QByteArray Geometry of floating widget - bool Visibility - - int Number of layout items (Valid values: 0, 1) - IF 0 - int Number of hidden contents - LOOP Contents - QString Unique name of content - ELSEIF 1 - ... todo ... - ENDIF - */ - QByteArray ba; - QDataStream out(&ba, QIODevice::WriteOnly); - out.setVersion(QDataStream::Qt_4_5); - out << (quint32) 0x00001337; // Magic - out << (quint32) 1; // Version - - // Save state of floating contents - saveFloatingWidgets(out); - - // Save state of sections and contents - if (m_MainLayout->count() <= 0 || m_Sections.isEmpty()) - { - // Looks like the user has hidden all contents and no more sections - // are available. We can simply write a list of all hidden contents. - out << 0; // Mode - - out << m_HiddenSectionContents.count(); - QHashIterator iter(m_HiddenSectionContents); - while (iter.hasNext()) - { - iter.next(); - out << iter.value().data.content->uniqueName(); - } - } - else if (m_MainLayout->count() == 1) - { - out << 1; // Mode - - // There should only be one! - QLayoutItem* li = m_MainLayout->itemAt(0); - if (!li->widget()) - qFatal("Not a widget in mainLayout, this shouldn't happen."); - - // Save sections beginning with the first QSplitter (li->widget()). - saveSectionWidgets(out, li->widget()); - - // Safe state of hidden contents, which doesn't have an section association - // or the section association points to a no longer existing section. - QHashIterator iter(m_HiddenSectionContents); - int cnt = 0; - while (iter.hasNext()) - { - iter.next(); - if (iter.value().preferredSectionId <= 0 || !m_SectionWidgetIdMap.contains(iter.value().preferredSectionId)) - cnt++; - } - out << cnt; - iter.toFront(); - while (iter.hasNext()) - { - iter.next(); - if (iter.value().preferredSectionId <= 0 || !m_SectionWidgetIdMap.contains(iter.value().preferredSectionId)) - out << iter.value().data.content->uniqueName(); - } - } - else - { - // More? Oh oh.. something is wrong :-/ - out << -1; - qWarning() << "Oh noooz.. Something went wrong. There are too many items in mainLayout."; - } - return ba; -} - -void MainContainerWidget::saveFloatingWidgets(QDataStream& out) const -{ - out << m_Floatings.count(); - for (int i = 0; i < m_Floatings.count(); ++i) - { - FloatingWidget* fw = m_Floatings.at(i); - out << fw->content()->uniqueName(); - out << fw->saveGeometry(); - out << fw->isVisible(); - } -} - -void MainContainerWidget::saveSectionWidgets(QDataStream& out, QWidget* widget) const -{ - QSplitter* sp = NULL; - SectionWidget* sw = NULL; - - if (!widget) - { - out << 0; - } - else if ((sp = dynamic_cast(widget)) != NULL) - { - out << 1; // Type = QSplitter - out << ((sp->orientation() == Qt::Horizontal) ? (int) 1 : (int) 2); - out << sp->count(); - out << sp->sizes(); - for (int i = 0; i < sp->count(); ++i) - { - saveSectionWidgets(out, sp->widget(i)); - } - } - else if ((sw = dynamic_cast(widget)) != NULL) - { - // Format (version 1) - // int Object type (SectionWidget=2) - // int Current active index - // int Number of contents (visible + hidden) - // LOOP Contents of section (last int) - // QString Unique name of SectionContent - // bool Visibility - // int Preferred index - - const QList& contents = sw->contents(); - QList hiddenContents; - - QHashIterator iter(m_HiddenSectionContents); - while (iter.hasNext()) - { - iter.next(); - const HiddenSectionItem& hsi = iter.value(); - if (hsi.preferredSectionId != sw->uid()) - continue; - hiddenContents.append(hsi); - } - - out << 2; // Type = SectionWidget - out << sw->currentIndex(); - out << contents.count() + hiddenContents.count(); - - for (int i = 0; i < contents.count(); ++i) - { - out << contents[i]->uniqueName(); // Unique name - out << (bool) true; // Visiblity - out << i; // Index - } - for (int i = 0; i < hiddenContents.count(); ++i) - { - out << hiddenContents.at(i).data.content->uniqueName(); - out << (bool) false; - out << hiddenContents.at(i).preferredSectionIndex; - } - } -} - -bool MainContainerWidget::saveSectionIndex(ADS_NS_SER::SectionIndexData& sid) const -{ - if (m_Sections.count() <= 0) - return false; - - sid.sectionsCount = m_Sections.count(); - for (int i = 0; i < sid.sectionsCount; ++i) - { - ADS_NS_SER::SectionEntity se; - se.x = mapFromGlobal(m_Sections[i]->parentWidget()->mapToGlobal(m_Sections[i]->pos())).x(); - se.y = mapFromGlobal(m_Sections[i]->parentWidget()->mapToGlobal(m_Sections[i]->pos())).y(); - se.width = m_Sections[i]->geometry().width(); - se.height = m_Sections[i]->geometry().height(); - se.currentIndex = m_Sections[i]->currentIndex(); - se.sectionContentsCount = m_Sections[i]->contents().count(); - foreach (const SectionContent::RefPtr& sc, m_Sections[i]->contents()) - { - ADS_NS_SER::SectionContentEntity sce; - sce.uniqueName = sc->uniqueName(); - sce.visible = true; - sce.preferredIndex = m_Sections[i]->indexOfContent(sc); - se.sectionContents.append(sce); // std::move()? - } - sid.sections.append(se); // std::move()? - } - return true; -} - -bool MainContainerWidget::restoreHierarchy(const QByteArray& data) -{ - QDataStream in(data); - in.setVersion(QDataStream::Qt_4_5); - - quint32 magic = 0; - in >> magic; - if (magic != 0x00001337) - return false; - - quint32 version = 0; - in >> version; - if (version != 1) - return false; - - QList oldFloatings = m_Floatings; - QList oldSections = m_Sections; - - // Restore floating widgets - QList floatings; - bool success = restoreFloatingWidgets(in, version, floatings); - if (!success) - { - qWarning() << "Could not restore floatings completely"; - } - - // Restore splitters, sections and contents - QList sections; - QList contentsToHide; - - int mode = 0; - in >> mode; - if (mode == 0) - { - // List of hidden contents. There are no sections at all. - int cnt = 0; - in >> cnt; - - if(cnt > 0) - { - // Create dummy section, required to call hideSectionContent() later. - SectionWidget* sw = new SectionWidget(this); - sections.append(sw); - - for (int i = 0; i < cnt; ++i) - { - QString uname; - in >> uname; - - const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname); - if (!sc) - continue; - - InternalContentData data; - if (!takeContent(sc, data)) - qFatal("This should never happen!!!"); - - sw->addContent(data, false); - contentsToHide.append(sc); - } - } - } - else if (mode == 1) - { - success = restoreSectionWidgets(in, version, NULL, sections, contentsToHide); - if (!success) - qWarning() << "Could not restore sections completely"; - - // Restore lonely hidden contents - int cnt = 0; - in >> cnt; - for (int i = 0; i < cnt; ++i) - { - QString uname; - in >> uname; - const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname); - if (!sc) - continue; - - InternalContentData data; - if (!takeContent(sc, data)) - { - qWarning("This should never happen! Looks like a bug during serialization, since the content is already being used in SWs."); - continue; - } - - SectionWidget* sw = NULL; - if (sections.size() <= 0) - qFatal("This should never happen, because above a section should have been created."); - else - sw = sections.first(); - - sw->addContent(data, false); - contentsToHide.append(sc); - } - } - - // Handle SectionContent which is not mentioned by deserialized data. - // What shall we do with it? For now: Simply drop them into the first SectionWidget. - if (true) - { - QList leftContents; - - // Collect all contents which has been restored - QList contents; - for (int i = 0; i < floatings.count(); ++i) - contents.append(floatings.at(i)->content()); - for (int i = 0; i < sections.count(); ++i) - for (int j = 0; j < sections.at(i)->contents().count(); ++j) - contents.append(sections.at(i)->contents().at(j)); - for (int i = 0; i < contentsToHide.count(); ++i) - contents.append(contentsToHide.at(i)); - - // Compare restored contents with available contents - const QList allContents = m_SectionContentIdMap.values(); - for (int i = 0; i < allContents.count(); ++i) - { - const SectionContent::RefPtr sc = allContents.at(i).toStrongRef(); - if (sc.isNull() || sc->containerWidget() != this) - continue; - if (contents.contains(sc)) - continue; - leftContents.append(sc); - } - - // What should we do with a drunken sailor.. what should.. erm.. - // What should we do with the left-contents? - // Lets add them to the first found SW or create one, if no SW is available. - for (int i = 0; i < leftContents.count(); ++i) - { - const SectionContent::RefPtr sc = leftContents.at(i); - SectionWidget* sw = NULL; - - if (sections.isEmpty()) - { - sw = new SectionWidget(this); - sections.append(sw); - addSectionWidget(sw); - } - else - sw = sections.first(); - - InternalContentData data; - if (!takeContent(sc, data)) - sw->addContent(sc); - else - sw->addContent(data, false); - } - } - - m_Floatings = floatings; - m_Sections = sections; - - // Delete old objects - QLayoutItem* old = m_MainLayout->takeAt(0); - m_MainLayout->addWidget(m_Splitter); - delete old; - qDeleteAll(oldFloatings); - qDeleteAll(oldSections); - - // Hide all as "hidden" marked contents - for (int i = 0; i < contentsToHide.count(); ++i) - hideSectionContent(contentsToHide.at(i)); - - deleteEmptySplitter(this); - return success; -} - -bool MainContainerWidget::restoreFloatingWidgets(QDataStream& in, int version, QList& floatings) -{ - Q_UNUSED(version) - - int fwCount = 0; - in >> fwCount; - if (fwCount <= 0) - return true; - - for (int i = 0; i < fwCount; ++i) - { - QString uname; - in >> uname; - QByteArray geom; - in >> geom; - bool visible = false; - in >> visible; - - const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname).toStrongRef(); - if (!sc) - { - qWarning() << "Can not find SectionContent:" << uname; - continue; - } - - InternalContentData data; - if (!this->takeContent(sc, data)) - continue; - - FloatingWidget* fw = new FloatingWidget(this, sc, data.titleWidget, data.contentWidget, this); - fw->restoreGeometry(geom); - fw->setVisible(visible); - if (visible) - { - fw->_titleWidget->setVisible(visible); - fw->_contentWidget->setVisible(visible); - } - floatings.append(fw); - data.titleWidget->m_FloatingWidget = fw; // $mfreiholz: Don't look at it :-< It's more than ugly... - } - return true; -} - -bool MainContainerWidget::restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList& sections, QList& contentsToHide) -{ - if (in.atEnd()) - return true; - - int type; - in >> type; - - // Splitter - if (type == 1) - { - int orientation, count; - QList sizes; - in >> orientation >> count >> sizes; - - QSplitter* sp = newSplitter((Qt::Orientation) orientation); - for (int i = 0; i < count; ++i) - { - if (!restoreSectionWidgets(in, version, sp, sections, contentsToHide)) - return false; - } - if (sp->count() <= 0) - { - delete sp; - sp = NULL; - } - else if (sp) - { - sp->setSizes(sizes); - - if (!currentSplitter) - m_Splitter = sp; - else - currentSplitter->addWidget(sp); - } - } - // Section - else if (type == 2) - { - if (!currentSplitter) - { - qWarning() << "Missing splitter object for section"; - return false; - } - - int currentIndex, count; - in >> currentIndex >> count; - - SectionWidget* sw = new SectionWidget(this); - for (int i = 0; i < count; ++i) - { - QString uname; - in >> uname; - bool visible = false; - in >> visible; - int preferredIndex = -1; - in >> preferredIndex; - - const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname).toStrongRef(); - if (!sc) - { - qWarning() << "Can not find SectionContent:" << uname; - continue; - } - - InternalContentData data; - if (!takeContent(sc, data)) - { - qCritical() << "Can not find InternalContentData of SC, this should never happen!" << sc->uid() << sc->uniqueName(); - sw->addContent(sc); - } - else - sw->addContent(data, false); - - if (!visible) - contentsToHide.append(sc); - } - if (sw->contents().isEmpty()) - { - delete sw; - sw = NULL; - } - else if (sw) - { - sw->setCurrentIndex(currentIndex); - currentSplitter->addWidget(sw); - sections.append(sw); - } - } - // Unknown - else - { - qWarning() << "Unknown object type during restore"; - } - - return true; -} - -bool MainContainerWidget::takeContent(const SectionContent::RefPtr& sc, InternalContentData& data) -{ - ADS_Expects(!sc.isNull()); - - // Search in sections - bool found = false; - for (int i = 0; i < m_Sections.count() && !found; ++i) - { - found = m_Sections.at(i)->takeContent(sc->uid(), data); - } - - // Search in floating widgets - for (int i = 0; i < m_Floatings.count() && !found; ++i) - { - found = m_Floatings.at(i)->content()->uid() == sc->uid(); - if (found) - m_Floatings.at(i)->takeContent(data); - } - - // Search in hidden items - if (!found && m_HiddenSectionContents.contains(sc->uid())) - { - const HiddenSectionItem hsi = m_HiddenSectionContents.take(sc->uid()); - data = hsi.data; - found = true; - } - - return found; -} - -void MainContainerWidget::onActiveTabChanged() -{ - SectionTitleWidget* stw = qobject_cast(sender()); - if (stw) - { - emit activeTabChanged(stw->m_Content, stw->isActiveTab()); - } -} - -void MainContainerWidget::onActionToggleSectionContentVisibility(bool visible) -{ - QAction* a = qobject_cast(sender()); - if (!a) - return; - const int uid = a->property("uid").toInt(); - const SectionContent::RefPtr sc = m_SectionContentIdMap.value(uid).toStrongRef(); - if (sc.isNull()) - { - qCritical() << "Can not find content by ID" << uid; - return; - } - if (visible) - showSectionContent(sc); - else - hideSectionContent(sc); -} - - -void MainContainerWidget::hideContainerOverlay() -{ - m_ContainerDropOverlay->hideDropOverlay(); -} - - -FloatingWidget* MainContainerWidget::startFloating(SectionWidget* sectionwidget, int ContentUid, const QPoint& TargetPos) -{ - // Create floating widget. - InternalContentData data; - if (!sectionwidget->takeContent(ContentUid, data)) - { - qWarning() << "THIS SHOULD NOT HAPPEN!!" << ContentUid; - return 0; - } - - FloatingWidget* fw = new FloatingWidget(this, data.content, data.titleWidget, data.contentWidget, this); - fw->resize(sectionwidget->size()); - m_Floatings.append(fw); - fw->move(TargetPos); - fw->show(); - fw->setObjectName("FloatingWidget"); - //fw->installEventFilter(this); - - // Delete old section, if it is empty now. - if (sectionwidget->contents().isEmpty()) - { - delete sectionwidget; - sectionwidget = NULL; - } - deleteEmptySplitter(this); - - m_ContainerDropOverlay->setAllowedAreas(OuterAreas); - m_ContainerDropOverlay->showDropOverlay(this); - m_ContainerDropOverlay->raise(); - return fw; -} - - -bool MainContainerWidget::event(QEvent *e) -{ - //std::cout << "ContainerWidget::event " << e->type() << std::endl; - return QFrame::event(e); -} - -void MainContainerWidget::moveFloatingWidget(const QPoint& TargetPos) -{ - QPoint MousePos = mapFromGlobal(QCursor::pos()); - - // Mouse is over the container widget - if (rect().contains(MousePos)) - { - //std::cout << "over Container" << std::endl; - m_ContainerDropOverlay->showDropOverlay(this); - m_ContainerDropOverlay->raise(); - } - else - { - std::cout << "-----------------" << std::endl; - m_ContainerDropOverlay->hideDropOverlay(); - } - - // Mouse is over a SectionWidget - SectionWidget* sectionwidget = sectionWidgetAt(MousePos); - if (sectionwidget) - { - //qInfo() << "over sectionWidget"; - m_SectionDropOverlay->setAllowedAreas(ADS_NS::AllAreas); - m_SectionDropOverlay->showDropOverlay(sectionwidget); - } - else - { - m_SectionDropOverlay->hideDropOverlay(); - } -} - - -void MainContainerWidget::dropFloatingWidget(FloatingWidget* FloatingWidget, - const QPoint& TargetPos) -{ - if (!FloatingWidget->isDraggingActive()) - { - return; - } - - QPoint MousePos = mapFromGlobal(TargetPos); - SectionWidget* sectionWidget = sectionWidgetAt(MousePos); - DropArea dropArea = InvalidDropArea; - if (sectionWidget) - { - m_SectionDropOverlay->setAllowedAreas(ADS_NS::AllAreas); - dropArea = m_SectionDropOverlay->showDropOverlay(sectionWidget); - if (dropArea != InvalidDropArea) - { - InternalContentData data; - FloatingWidget->takeContent(data); - FloatingWidget->deleteLater(); - dropContent(data, sectionWidget, dropArea, true); - } - } - - // mouse is over container - if (InvalidDropArea == dropArea) - { - dropArea = m_ContainerDropOverlay->dropAreaUnderCursor(); - std::cout << "Cursor location: " << dropArea << std::endl; - if (dropArea != InvalidDropArea) - { - InternalContentData data; - FloatingWidget->takeContent(data); - FloatingWidget->deleteLater(); - dropContent(data, nullptr, dropArea, true); - } - } -} - - -ADS_NAMESPACE_END +//--------------------------------------------------------------------------- +// EOF ContainerWidget.cpp diff --git a/AdvancedDockingSystem/src/FloatingWidget.cpp b/AdvancedDockingSystem/src/FloatingWidget.cpp index e6c13b6..ccfe52a 100644 --- a/AdvancedDockingSystem/src/FloatingWidget.cpp +++ b/AdvancedDockingSystem/src/FloatingWidget.cpp @@ -1,4 +1,4 @@ -#include +#include #include "ads/FloatingWidget.h" #include diff --git a/AdvancedDockingSystem/src/MainContainerWidget.cpp b/AdvancedDockingSystem/src/MainContainerWidget.cpp new file mode 100644 index 0000000..a3675ec --- /dev/null +++ b/AdvancedDockingSystem/src/MainContainerWidget.cpp @@ -0,0 +1,1278 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ads/Internal.h" +#include "ads/SectionWidget.h" +#include "ads/SectionTitleWidget.h" +#include "ads/SectionContentWidget.h" +#include "ads/DropOverlay.h" +#include "ads/Serialization.h" + +#include + +ADS_NAMESPACE_BEGIN + + +static QSplitter* newSplitter(Qt::Orientation orientation = Qt::Horizontal, QWidget* parent = 0) +{ + QSplitter* s = new QSplitter(orientation, parent); + s->setProperty("ads-splitter", QVariant(true)); + s->setChildrenCollapsible(false); + s->setOpaqueResize(false); + return s; +} + + + +MainContainerWidget::MainContainerWidget(QWidget *parent) : + CContainerWidget(parent) +{ + +} + +MainContainerWidget::~MainContainerWidget() +{ + // Note: It's required to delete in 2 steps + // Remove from list, and then delete. + // Because the destrcutor of objects wants to modify the current + // iterating list as well... :-/ + while (!m_Sections.isEmpty()) + { + SectionWidget* sw = m_Sections.takeLast(); + delete sw; + } + while (!m_Floatings.isEmpty()) + { + FloatingWidget* fw = m_Floatings.takeLast(); + delete fw; + } + m_SectionContentIdMap.clear(); + m_SectionContentNameMap.clear(); + m_SectionWidgetIdMap.clear(); +} + + +SectionWidget* MainContainerWidget::addSectionContent(const SectionContent::RefPtr& sc, SectionWidget* sw, DropArea area) +{ + ADS_Expects(!sc.isNull()); + + // Drop it based on "area" + InternalContentData data; + data.content = sc; + data.titleWidget = new SectionTitleWidget(sc, NULL); + data.contentWidget = new SectionContentWidget(sc, NULL); + + connect(data.titleWidget, SIGNAL(activeTabChanged()), this, SLOT(onActiveTabChanged())); + return dropContent(data, sw, area, false); +} + +bool MainContainerWidget::removeSectionContent(const SectionContent::RefPtr& sc) +{ + ADS_Expects(!sc.isNull()); + + // Hide the content. + // The hideSectionContent() automatically deletes no longer required SectionWidget objects. + if (!hideSectionContent(sc)) + return false; + + // Begin of ugly work arround. + // TODO The hideSectionContent() method should take care of deleting FloatingWidgets and SectionWidgets, + // but only cares about SectionWidgets right now. So we need to check whether it was a FloatingWidget + // and delete it. + bool found = false; + for (int i = 0; i < m_Floatings.count(); ++i) + { + FloatingWidget* fw = m_Floatings.at(i); + InternalContentData data; + if (!(found = fw->takeContent(data))) + continue; + m_Floatings.removeAll(fw); + delete fw; + delete data.titleWidget; + delete data.contentWidget; + break; + } // End of ugly work arround. + + // Get from hidden contents and delete associated internal stuff. + if (!m_HiddenSectionContents.contains(sc->uid())) + { + qFatal("Something went wrong... The content should have been there :-/"); + return false; + } + + // Delete internal objects. + HiddenSectionItem hsi = m_HiddenSectionContents.take(sc->uid()); + delete hsi.data.titleWidget; + delete hsi.data.contentWidget; + + // Hide the custom widgets of SectionContent. + // ... should we? ... + + return true; +} + +bool MainContainerWidget::showSectionContent(const SectionContent::RefPtr& sc) +{ + ADS_Expects(!sc.isNull()); + + // Search SC in floatings + for (int i = 0; i < m_Floatings.count(); ++i) + { + FloatingWidget* fw = m_Floatings.at(i); + const bool found = fw->content()->uid() == sc->uid(); + if (!found) + continue; + fw->setVisible(true); + fw->_titleWidget->setVisible(true); + fw->_contentWidget->setVisible(true); + emit sectionContentVisibilityChanged(sc, true); + return true; + } + + // Search SC in hidden sections + // Try to show them in the last position, otherwise simply append + // it to the first section (or create a new section?) + if (m_HiddenSectionContents.contains(sc->uid())) + { + const HiddenSectionItem hsi = m_HiddenSectionContents.take(sc->uid()); + hsi.data.titleWidget->setVisible(true); + hsi.data.contentWidget->setVisible(true); + SectionWidget* sw = nullptr; + if (hsi.preferredSectionId > 0 && (sw = m_SectionWidgetIdMap.value(hsi.preferredSectionId)) != nullptr) + { + sw->addContent(hsi.data, true); + emit sectionContentVisibilityChanged(sc, true); + return true; + } + else if (m_Sections.size() > 0 && (sw = m_Sections.first()) != NULL) + { + sw->addContent(hsi.data, true); + emit sectionContentVisibilityChanged(sc, true); + return true; + } + else + { + sw = newSectionWidget(); + addSectionWidget(sw); + sw->addContent(hsi.data, true); + emit sectionContentVisibilityChanged(sc, true); + return true; + } + } + + // Already visible? + // TODO + qWarning("Unable to show SectionContent, don't know where 8-/ (already visible?)"); + return false; +} + +bool MainContainerWidget::hideSectionContent(const SectionContent::RefPtr& sc) +{ + ADS_Expects(!sc.isNull()); + + // Search SC in floatings + // We can simply hide floatings, nothing else required. + for (int i = 0; i < m_Floatings.count(); ++i) + { + const bool found = m_Floatings.at(i)->content()->uid() == sc->uid(); + if (!found) + continue; + m_Floatings.at(i)->setVisible(false); + emit sectionContentVisibilityChanged(sc, false); + return true; + } + + // Search SC in sections + // It's required to remove the SC from SW completely and hold it in a + // separate list as long as a "showSectionContent" gets called for the SC again. + // In case that the SW does not have any other SCs, we need to delete it. + for (int i = 0; i < m_Sections.count(); ++i) + { + SectionWidget* sw = m_Sections.at(i); + const bool found = sw->indexOfContent(sc) >= 0; + if (!found) + continue; + + HiddenSectionItem hsi; + hsi.preferredSectionId = sw->uid(); + hsi.preferredSectionIndex = sw->indexOfContent(sc); + if (!sw->takeContent(sc->uid(), hsi.data)) + return false; + + hsi.data.titleWidget->setVisible(false); + hsi.data.contentWidget->setVisible(false); + m_HiddenSectionContents.insert(sc->uid(), hsi); + + if (sw->contents().isEmpty()) + { + delete sw; + sw = NULL; + deleteEmptySplitter(this); + } + emit sectionContentVisibilityChanged(sc, false); + return true; + } + + // Search SC in hidden elements + // The content may already be hidden + if (m_HiddenSectionContents.contains(sc->uid())) + return true; + + qFatal("Unable to hide SectionContent, don't know this one 8-/"); + return false; +} + +bool MainContainerWidget::raiseSectionContent(const SectionContent::RefPtr& sc) +{ + ADS_Expects(!sc.isNull()); + + // Search SC in sections + for (int i = 0; i < m_Sections.count(); ++i) + { + SectionWidget* sw = m_Sections.at(i); + int index = sw->indexOfContent(sc); + if (index < 0) + continue; + sw->setCurrentIndex(index); + return true; + } + + // Search SC in floatings + for (int i = 0; i < m_Floatings.size(); ++i) + { + FloatingWidget* fw = m_Floatings.at(i); + if (fw->content()->uid() != sc->uid()) + continue; + fw->setVisible(true); + fw->raise(); + return true; + } + + // Search SC in hidden + if (m_HiddenSectionContents.contains(sc->uid())) + return showSectionContent(sc); + + qFatal("Unable to hide SectionContent, don't know this one 8-/"); + return false; +} + +bool MainContainerWidget::isSectionContentVisible(const SectionContent::RefPtr& sc) +{ + ADS_Expects(!sc.isNull()); + + // Search SC in floatings + for (int i = 0; i < m_Floatings.count(); ++i) + { + const bool found = m_Floatings.at(i)->content()->uid() == sc->uid(); + if (!found) + continue; + return m_Floatings.at(i)->isVisible(); + } + + // Search SC in sections + for (int i = 0; i < m_Sections.count(); ++i) + { + SectionWidget* sw = m_Sections.at(i); + const int index = sw->indexOfContent(sc); + if (index < 0) + continue; + return true; + } + + // Search SC in hidden + if (m_HiddenSectionContents.contains(sc->uid())) + return false; + + qWarning() << "SectionContent is not a part of this ContainerWidget:" << sc->uniqueName(); + return false; +} + +QMenu* MainContainerWidget::createContextMenu() const +{ + // Fill map with actions (sorted by key!) + QMap actions; + + // Visible contents of sections + for (int i = 0; i < m_Sections.size(); ++i) + { + const SectionWidget* sw = m_Sections.at(i); + const QList& contents = sw->contents(); + foreach (const SectionContent::RefPtr& sc, contents) + { + QAction* a = new QAction(QIcon(), sc->visibleTitle(), NULL); + a->setObjectName(QString("ads-action-sc-%1").arg(QString::number(sc->uid()))); + a->setProperty("uid", sc->uid()); + a->setProperty("type", "section"); + a->setCheckable(true); + a->setChecked(true); + a->setEnabled(sc->flags().testFlag(SectionContent::Closeable)); + connect(a, SIGNAL(toggled(bool)), this, SLOT(onActionToggleSectionContentVisibility(bool))); + actions.insert(a->text(), a); + } + } + + // Hidden contents of sections + QHashIterator hiddenIter(m_HiddenSectionContents); + while (hiddenIter.hasNext()) + { + hiddenIter.next(); + const SectionContent::RefPtr sc = hiddenIter.value().data.content; + + QAction* a = new QAction(QIcon(), sc->visibleTitle(), NULL); + a->setObjectName(QString("ads-action-sc-%1").arg(QString::number(sc->uid()))); + a->setProperty("uid", sc->uid()); + a->setProperty("type", "section"); + a->setCheckable(true); + a->setChecked(false); + connect(a, SIGNAL(toggled(bool)), this, SLOT(onActionToggleSectionContentVisibility(bool))); + actions.insert(a->text(), a); + } + + // Floating contents + for (int i = 0; i < m_Floatings.size(); ++i) + { + const FloatingWidget* fw = m_Floatings.at(i); + const SectionContent::RefPtr sc = fw->content(); + + QAction* a = new QAction(QIcon(), sc->visibleTitle(), NULL); + a->setObjectName(QString("ads-action-sc-%1").arg(QString::number(sc->uid()))); + a->setProperty("uid", sc->uid()); + a->setProperty("type", "floating"); + a->setCheckable(true); + a->setChecked(fw->isVisible()); + connect(a, SIGNAL(toggled(bool)), this, SLOT(onActionToggleSectionContentVisibility(bool))); + actions.insert(a->text(), a); + } + + // Create menu from "actions" + QMenu* m = new QMenu(NULL); + m->addActions(actions.values()); + return m; +} + +QByteArray MainContainerWidget::saveState() const +{ + ADS_NS_SER::InMemoryWriter writer; + + // Hierarchy data. + const QByteArray hierarchyData = saveHierarchy(); + if (!hierarchyData.isEmpty()) + { + writer.write(ADS_NS_SER::ET_Hierarchy, hierarchyData); + } + + // SectionIndex data. + ADS_NS_SER::SectionIndexData sid; + if (saveSectionIndex(sid)) + { + writer.write(sid); + } + + if (writer.offsetsCount() == 0) + return QByteArray(); + return writer.toByteArray(); +} + +bool MainContainerWidget::restoreState(const QByteArray& data) +{ + if (data.isEmpty()) + return false; + + ADS_NS_SER::InMemoryReader reader(data); + if (!reader.initReadHeader()) + return false; + + // Basic hierarchy data. + QByteArray hierarchyData; + if (reader.read(ADS_NS_SER::ET_Hierarchy, hierarchyData)) + { + restoreHierarchy(hierarchyData); + } + return true; +} + + +QList MainContainerWidget::contents() const +{ + QList wl = m_SectionContentIdMap.values(); + QList sl; + for (int i = 0; i < wl.count(); ++i) + { + const SectionContent::RefPtr sc = wl.at(i).toStrongRef(); + if (sc) + sl.append(sc); + } + return sl; +} + +QPointer MainContainerWidget::sectionDropOverlay() const +{ + return m_SectionDropOverlay; +} + +/////////////////////////////////////////////////////////////////////// +// PRIVATE API BEGINS HERE +/////////////////////////////////////////////////////////////////////// + +SectionWidget* MainContainerWidget::newSectionWidget() +{ + SectionWidget* sw = new SectionWidget(this); + m_Sections.append(sw); + return sw; +} + +SectionWidget* MainContainerWidget::insertNewSectionWidget( + const InternalContentData& data, SectionWidget* targetSection, SectionWidget* ret, + Qt::Orientation Orientation, int InsertIndexOffset) +{ + QSplitter* targetSectionSplitter = findParentSplitter(targetSection); + SectionWidget* sw = newSectionWidget(); + sw->addContent(data, true); + if (targetSectionSplitter->orientation() == Orientation) + { + const int index = targetSectionSplitter->indexOf(targetSection); + targetSectionSplitter->insertWidget(index + InsertIndexOffset, sw); + } + else + { + const int index = targetSectionSplitter->indexOf(targetSection); + QSplitter* s = newSplitter(Orientation); + s->addWidget(sw); + s->addWidget(targetSection); + targetSectionSplitter->insertWidget(index, s); + } + ret = sw; + return ret; +} + +SectionWidget* MainContainerWidget::dropContent(const InternalContentData& data, SectionWidget* targetSectionWidget, DropArea area, bool autoActive) +{ + ADS_Expects(targetSection != NULL); + + SectionWidget* section_widget = nullptr; + + // If no sections exists yet, create a default one and always drop into it. + if (m_Sections.isEmpty()) + { + targetSectionWidget = newSectionWidget(); + addSectionWidget(targetSectionWidget); + area = CenterDropArea; + } + + // Drop on outer area + if (!targetSectionWidget) + { + switch (area) + { + case TopDropArea:return dropContentOuterHelper(m_MainLayout, data, Qt::Vertical, false); + case RightDropArea: return dropContentOuterHelper(m_MainLayout, data, Qt::Horizontal, true); + case CenterDropArea: + case BottomDropArea:return dropContentOuterHelper(m_MainLayout, data, Qt::Vertical, true); + case LeftDropArea: return dropContentOuterHelper(m_MainLayout, data, Qt::Horizontal, false); + default: + return nullptr; + } + return section_widget; + } + + // Drop logic based on area. + switch (area) + { + case TopDropArea:return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Vertical, 0); + case RightDropArea: return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Horizontal, 1); + case BottomDropArea: return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Vertical, 1); + case LeftDropArea: return insertNewSectionWidget(data, targetSectionWidget, section_widget, Qt::Horizontal, 0); + case CenterDropArea: + targetSectionWidget->addContent(data, autoActive); + return targetSectionWidget; + + default: + break; + } + return section_widget; +} + +void MainContainerWidget::addSectionWidget(SectionWidget* section) +{ + ADS_Expects(section != NULL); + + // Create default splitter. + if (!m_Splitter) + { + m_Splitter = newSplitter(m_Orientation); + m_MainLayout->addWidget(m_Splitter, 0, 0); + } + if (m_Splitter->indexOf(section) != -1) + { + qWarning() << Q_FUNC_INFO << QString("Section has already been added"); + return; + } + m_Splitter->addWidget(section); +} + +SectionWidget* MainContainerWidget::sectionWidgetAt(const QPoint& pos) const +{ + const QPoint gpos = mapToGlobal(pos); + for (int i = 0; i < m_Sections.size(); ++i) + { + SectionWidget* sw = m_Sections[i]; + if (sw->rect().contains(sw->mapFromGlobal(gpos))) + { + return sw; + } + } + return 0; +} + +SectionWidget* MainContainerWidget::dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append) +{ + ADS_Expects(l != NULL); + + SectionWidget* sw = newSectionWidget(); + sw->addContent(data, true); + + QSplitter* oldsp = findImmediateSplitter(this); + if (!oldsp) + { + QSplitter* sp = newSplitter(orientation); + if (l->count() > 0) + { + qWarning() << "Still items in layout. This should never happen."; + QLayoutItem* li = l->takeAt(0); + delete li; + } + l->addWidget(sp); + sp->addWidget(sw); + } + else if (oldsp->orientation() == orientation + || oldsp->count() == 1) + { + oldsp->setOrientation(orientation); + if (append) + oldsp->addWidget(sw); + else + oldsp->insertWidget(0, sw); + } + else + { + QSplitter* sp = newSplitter(orientation); + if (append) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + QLayoutItem* li = l->replaceWidget(oldsp, sp); + sp->addWidget(oldsp); + sp->addWidget(sw); + delete li; +#else + int index = l->indexOf(oldsp); + QLayoutItem* li = l->takeAt(index); + l->addWidget(sp); + sp->addWidget(oldsp); + sp->addWidget(sw); + delete li; +#endif + } + else + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + sp->addWidget(sw); + QLayoutItem* li = l->replaceWidget(oldsp, sp); + sp->addWidget(oldsp); + delete li; +#else + sp->addWidget(sw); + int index = l->indexOf(oldsp); + QLayoutItem* li = l->takeAt(index); + l->addWidget(sp); + sp->addWidget(oldsp); + delete li; +#endif + } + } + return sw; +} + +QByteArray MainContainerWidget::saveHierarchy() const +{ + /* + # Data Format + + quint32 Magic + quint32 Version + + int Number of floating widgets + LOOP Floating widgets + QString Unique name of content + QByteArray Geometry of floating widget + bool Visibility + + int Number of layout items (Valid values: 0, 1) + IF 0 + int Number of hidden contents + LOOP Contents + QString Unique name of content + ELSEIF 1 + ... todo ... + ENDIF + */ + QByteArray ba; + QDataStream out(&ba, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_5); + out << (quint32) 0x00001337; // Magic + out << (quint32) 1; // Version + + // Save state of floating contents + saveFloatingWidgets(out); + + // Save state of sections and contents + if (m_MainLayout->count() <= 0 || m_Sections.isEmpty()) + { + // Looks like the user has hidden all contents and no more sections + // are available. We can simply write a list of all hidden contents. + out << 0; // Mode + + out << m_HiddenSectionContents.count(); + QHashIterator iter(m_HiddenSectionContents); + while (iter.hasNext()) + { + iter.next(); + out << iter.value().data.content->uniqueName(); + } + } + else if (m_MainLayout->count() == 1) + { + out << 1; // Mode + + // There should only be one! + QLayoutItem* li = m_MainLayout->itemAt(0); + if (!li->widget()) + qFatal("Not a widget in mainLayout, this shouldn't happen."); + + // Save sections beginning with the first QSplitter (li->widget()). + saveSectionWidgets(out, li->widget()); + + // Safe state of hidden contents, which doesn't have an section association + // or the section association points to a no longer existing section. + QHashIterator iter(m_HiddenSectionContents); + int cnt = 0; + while (iter.hasNext()) + { + iter.next(); + if (iter.value().preferredSectionId <= 0 || !m_SectionWidgetIdMap.contains(iter.value().preferredSectionId)) + cnt++; + } + out << cnt; + iter.toFront(); + while (iter.hasNext()) + { + iter.next(); + if (iter.value().preferredSectionId <= 0 || !m_SectionWidgetIdMap.contains(iter.value().preferredSectionId)) + out << iter.value().data.content->uniqueName(); + } + } + else + { + // More? Oh oh.. something is wrong :-/ + out << -1; + qWarning() << "Oh noooz.. Something went wrong. There are too many items in mainLayout."; + } + return ba; +} + +void MainContainerWidget::saveFloatingWidgets(QDataStream& out) const +{ + out << m_Floatings.count(); + for (int i = 0; i < m_Floatings.count(); ++i) + { + FloatingWidget* fw = m_Floatings.at(i); + out << fw->content()->uniqueName(); + out << fw->saveGeometry(); + out << fw->isVisible(); + } +} + +void MainContainerWidget::saveSectionWidgets(QDataStream& out, QWidget* widget) const +{ + QSplitter* sp = NULL; + SectionWidget* sw = NULL; + + if (!widget) + { + out << 0; + } + else if ((sp = dynamic_cast(widget)) != NULL) + { + out << 1; // Type = QSplitter + out << ((sp->orientation() == Qt::Horizontal) ? (int) 1 : (int) 2); + out << sp->count(); + out << sp->sizes(); + for (int i = 0; i < sp->count(); ++i) + { + saveSectionWidgets(out, sp->widget(i)); + } + } + else if ((sw = dynamic_cast(widget)) != NULL) + { + // Format (version 1) + // int Object type (SectionWidget=2) + // int Current active index + // int Number of contents (visible + hidden) + // LOOP Contents of section (last int) + // QString Unique name of SectionContent + // bool Visibility + // int Preferred index + + const QList& contents = sw->contents(); + QList hiddenContents; + + QHashIterator iter(m_HiddenSectionContents); + while (iter.hasNext()) + { + iter.next(); + const HiddenSectionItem& hsi = iter.value(); + if (hsi.preferredSectionId != sw->uid()) + continue; + hiddenContents.append(hsi); + } + + out << 2; // Type = SectionWidget + out << sw->currentIndex(); + out << contents.count() + hiddenContents.count(); + + for (int i = 0; i < contents.count(); ++i) + { + out << contents[i]->uniqueName(); // Unique name + out << (bool) true; // Visiblity + out << i; // Index + } + for (int i = 0; i < hiddenContents.count(); ++i) + { + out << hiddenContents.at(i).data.content->uniqueName(); + out << (bool) false; + out << hiddenContents.at(i).preferredSectionIndex; + } + } +} + +bool MainContainerWidget::saveSectionIndex(ADS_NS_SER::SectionIndexData& sid) const +{ + if (m_Sections.count() <= 0) + return false; + + sid.sectionsCount = m_Sections.count(); + for (int i = 0; i < sid.sectionsCount; ++i) + { + ADS_NS_SER::SectionEntity se; + se.x = mapFromGlobal(m_Sections[i]->parentWidget()->mapToGlobal(m_Sections[i]->pos())).x(); + se.y = mapFromGlobal(m_Sections[i]->parentWidget()->mapToGlobal(m_Sections[i]->pos())).y(); + se.width = m_Sections[i]->geometry().width(); + se.height = m_Sections[i]->geometry().height(); + se.currentIndex = m_Sections[i]->currentIndex(); + se.sectionContentsCount = m_Sections[i]->contents().count(); + foreach (const SectionContent::RefPtr& sc, m_Sections[i]->contents()) + { + ADS_NS_SER::SectionContentEntity sce; + sce.uniqueName = sc->uniqueName(); + sce.visible = true; + sce.preferredIndex = m_Sections[i]->indexOfContent(sc); + se.sectionContents.append(sce); // std::move()? + } + sid.sections.append(se); // std::move()? + } + return true; +} + +bool MainContainerWidget::restoreHierarchy(const QByteArray& data) +{ + QDataStream in(data); + in.setVersion(QDataStream::Qt_4_5); + + quint32 magic = 0; + in >> magic; + if (magic != 0x00001337) + return false; + + quint32 version = 0; + in >> version; + if (version != 1) + return false; + + QList oldFloatings = m_Floatings; + QList oldSections = m_Sections; + + // Restore floating widgets + QList floatings; + bool success = restoreFloatingWidgets(in, version, floatings); + if (!success) + { + qWarning() << "Could not restore floatings completely"; + } + + // Restore splitters, sections and contents + QList sections; + QList contentsToHide; + + int mode = 0; + in >> mode; + if (mode == 0) + { + // List of hidden contents. There are no sections at all. + int cnt = 0; + in >> cnt; + + if(cnt > 0) + { + // Create dummy section, required to call hideSectionContent() later. + SectionWidget* sw = new SectionWidget(this); + sections.append(sw); + + for (int i = 0; i < cnt; ++i) + { + QString uname; + in >> uname; + + const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname); + if (!sc) + continue; + + InternalContentData data; + if (!takeContent(sc, data)) + qFatal("This should never happen!!!"); + + sw->addContent(data, false); + contentsToHide.append(sc); + } + } + } + else if (mode == 1) + { + success = restoreSectionWidgets(in, version, NULL, sections, contentsToHide); + if (!success) + qWarning() << "Could not restore sections completely"; + + // Restore lonely hidden contents + int cnt = 0; + in >> cnt; + for (int i = 0; i < cnt; ++i) + { + QString uname; + in >> uname; + const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname); + if (!sc) + continue; + + InternalContentData data; + if (!takeContent(sc, data)) + { + qWarning("This should never happen! Looks like a bug during serialization, since the content is already being used in SWs."); + continue; + } + + SectionWidget* sw = NULL; + if (sections.size() <= 0) + qFatal("This should never happen, because above a section should have been created."); + else + sw = sections.first(); + + sw->addContent(data, false); + contentsToHide.append(sc); + } + } + + // Handle SectionContent which is not mentioned by deserialized data. + // What shall we do with it? For now: Simply drop them into the first SectionWidget. + if (true) + { + QList leftContents; + + // Collect all contents which has been restored + QList contents; + for (int i = 0; i < floatings.count(); ++i) + contents.append(floatings.at(i)->content()); + for (int i = 0; i < sections.count(); ++i) + for (int j = 0; j < sections.at(i)->contents().count(); ++j) + contents.append(sections.at(i)->contents().at(j)); + for (int i = 0; i < contentsToHide.count(); ++i) + contents.append(contentsToHide.at(i)); + + // Compare restored contents with available contents + const QList allContents = m_SectionContentIdMap.values(); + for (int i = 0; i < allContents.count(); ++i) + { + const SectionContent::RefPtr sc = allContents.at(i).toStrongRef(); + if (sc.isNull() || sc->containerWidget() != this) + continue; + if (contents.contains(sc)) + continue; + leftContents.append(sc); + } + + // What should we do with a drunken sailor.. what should.. erm.. + // What should we do with the left-contents? + // Lets add them to the first found SW or create one, if no SW is available. + for (int i = 0; i < leftContents.count(); ++i) + { + const SectionContent::RefPtr sc = leftContents.at(i); + SectionWidget* sw = NULL; + + if (sections.isEmpty()) + { + sw = new SectionWidget(this); + sections.append(sw); + addSectionWidget(sw); + } + else + sw = sections.first(); + + InternalContentData data; + if (!takeContent(sc, data)) + sw->addContent(sc); + else + sw->addContent(data, false); + } + } + + m_Floatings = floatings; + m_Sections = sections; + + // Delete old objects + QLayoutItem* old = m_MainLayout->takeAt(0); + m_MainLayout->addWidget(m_Splitter); + delete old; + qDeleteAll(oldFloatings); + qDeleteAll(oldSections); + + // Hide all as "hidden" marked contents + for (int i = 0; i < contentsToHide.count(); ++i) + hideSectionContent(contentsToHide.at(i)); + + deleteEmptySplitter(this); + return success; +} + +bool MainContainerWidget::restoreFloatingWidgets(QDataStream& in, int version, QList& floatings) +{ + Q_UNUSED(version) + + int fwCount = 0; + in >> fwCount; + if (fwCount <= 0) + return true; + + for (int i = 0; i < fwCount; ++i) + { + QString uname; + in >> uname; + QByteArray geom; + in >> geom; + bool visible = false; + in >> visible; + + const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname).toStrongRef(); + if (!sc) + { + qWarning() << "Can not find SectionContent:" << uname; + continue; + } + + InternalContentData data; + if (!this->takeContent(sc, data)) + continue; + + FloatingWidget* fw = new FloatingWidget(this, sc, data.titleWidget, data.contentWidget, this); + fw->restoreGeometry(geom); + fw->setVisible(visible); + if (visible) + { + fw->_titleWidget->setVisible(visible); + fw->_contentWidget->setVisible(visible); + } + floatings.append(fw); + data.titleWidget->m_FloatingWidget = fw; // $mfreiholz: Don't look at it :-< It's more than ugly... + } + return true; +} + +bool MainContainerWidget::restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList& sections, QList& contentsToHide) +{ + if (in.atEnd()) + return true; + + int type; + in >> type; + + // Splitter + if (type == 1) + { + int orientation, count; + QList sizes; + in >> orientation >> count >> sizes; + + QSplitter* sp = newSplitter((Qt::Orientation) orientation); + for (int i = 0; i < count; ++i) + { + if (!restoreSectionWidgets(in, version, sp, sections, contentsToHide)) + return false; + } + if (sp->count() <= 0) + { + delete sp; + sp = NULL; + } + else if (sp) + { + sp->setSizes(sizes); + + if (!currentSplitter) + m_Splitter = sp; + else + currentSplitter->addWidget(sp); + } + } + // Section + else if (type == 2) + { + if (!currentSplitter) + { + qWarning() << "Missing splitter object for section"; + return false; + } + + int currentIndex, count; + in >> currentIndex >> count; + + SectionWidget* sw = new SectionWidget(this); + for (int i = 0; i < count; ++i) + { + QString uname; + in >> uname; + bool visible = false; + in >> visible; + int preferredIndex = -1; + in >> preferredIndex; + + const SectionContent::RefPtr sc = m_SectionContentNameMap.value(uname).toStrongRef(); + if (!sc) + { + qWarning() << "Can not find SectionContent:" << uname; + continue; + } + + InternalContentData data; + if (!takeContent(sc, data)) + { + qCritical() << "Can not find InternalContentData of SC, this should never happen!" << sc->uid() << sc->uniqueName(); + sw->addContent(sc); + } + else + sw->addContent(data, false); + + if (!visible) + contentsToHide.append(sc); + } + if (sw->contents().isEmpty()) + { + delete sw; + sw = NULL; + } + else if (sw) + { + sw->setCurrentIndex(currentIndex); + currentSplitter->addWidget(sw); + sections.append(sw); + } + } + // Unknown + else + { + qWarning() << "Unknown object type during restore"; + } + + return true; +} + +bool MainContainerWidget::takeContent(const SectionContent::RefPtr& sc, InternalContentData& data) +{ + ADS_Expects(!sc.isNull()); + + // Search in sections + bool found = false; + for (int i = 0; i < m_Sections.count() && !found; ++i) + { + found = m_Sections.at(i)->takeContent(sc->uid(), data); + } + + // Search in floating widgets + for (int i = 0; i < m_Floatings.count() && !found; ++i) + { + found = m_Floatings.at(i)->content()->uid() == sc->uid(); + if (found) + m_Floatings.at(i)->takeContent(data); + } + + // Search in hidden items + if (!found && m_HiddenSectionContents.contains(sc->uid())) + { + const HiddenSectionItem hsi = m_HiddenSectionContents.take(sc->uid()); + data = hsi.data; + found = true; + } + + return found; +} + +void MainContainerWidget::onActiveTabChanged() +{ + SectionTitleWidget* stw = qobject_cast(sender()); + if (stw) + { + emit activeTabChanged(stw->m_Content, stw->isActiveTab()); + } +} + +void MainContainerWidget::onActionToggleSectionContentVisibility(bool visible) +{ + QAction* a = qobject_cast(sender()); + if (!a) + return; + const int uid = a->property("uid").toInt(); + const SectionContent::RefPtr sc = m_SectionContentIdMap.value(uid).toStrongRef(); + if (sc.isNull()) + { + qCritical() << "Can not find content by ID" << uid; + return; + } + if (visible) + showSectionContent(sc); + else + hideSectionContent(sc); +} + + +void MainContainerWidget::hideContainerOverlay() +{ + m_ContainerDropOverlay->hideDropOverlay(); +} + + +FloatingWidget* MainContainerWidget::startFloating(SectionWidget* sectionwidget, int ContentUid, const QPoint& TargetPos) +{ + // Create floating widget. + InternalContentData data; + if (!sectionwidget->takeContent(ContentUid, data)) + { + qWarning() << "THIS SHOULD NOT HAPPEN!!" << ContentUid; + return 0; + } + + FloatingWidget* fw = new FloatingWidget(this, data.content, data.titleWidget, data.contentWidget, this); + fw->resize(sectionwidget->size()); + m_Floatings.append(fw); + fw->move(TargetPos); + fw->show(); + fw->setObjectName("FloatingWidget"); + + // Delete old section, if it is empty now. + if (sectionwidget->contents().isEmpty()) + { + delete sectionwidget; + sectionwidget = NULL; + } + deleteEmptySplitter(this); + + m_ContainerDropOverlay->setAllowedAreas(OuterAreas); + m_ContainerDropOverlay->showDropOverlay(this); + m_ContainerDropOverlay->raise(); + return fw; +} + + +bool MainContainerWidget::event(QEvent *e) +{ + //std::cout << "ContainerWidget::event " << e->type() << std::endl; + return QFrame::event(e); +} + +void MainContainerWidget::moveFloatingWidget(const QPoint& TargetPos) +{ + QPoint MousePos = mapFromGlobal(QCursor::pos()); + + // Mouse is over the container widget + if (rect().contains(MousePos)) + { + //std::cout << "over Container" << std::endl; + m_ContainerDropOverlay->showDropOverlay(this); + m_ContainerDropOverlay->raise(); + } + else + { + std::cout << "-----------------" << std::endl; + m_ContainerDropOverlay->hideDropOverlay(); + } + + // Mouse is over a SectionWidget + SectionWidget* sectionwidget = sectionWidgetAt(MousePos); + if (sectionwidget) + { + //qInfo() << "over sectionWidget"; + m_SectionDropOverlay->setAllowedAreas(ADS_NS::AllAreas); + m_SectionDropOverlay->showDropOverlay(sectionwidget); + } + else + { + m_SectionDropOverlay->hideDropOverlay(); + } +} + + +void MainContainerWidget::dropFloatingWidget(FloatingWidget* FloatingWidget, + const QPoint& TargetPos) +{ + if (!FloatingWidget->isDraggingActive()) + { + return; + } + + QPoint MousePos = mapFromGlobal(TargetPos); + SectionWidget* sectionWidget = sectionWidgetAt(MousePos); + DropArea dropArea = InvalidDropArea; + if (sectionWidget) + { + m_SectionDropOverlay->setAllowedAreas(ADS_NS::AllAreas); + dropArea = m_SectionDropOverlay->showDropOverlay(sectionWidget); + if (dropArea != InvalidDropArea) + { + InternalContentData data; + FloatingWidget->takeContent(data); + FloatingWidget->deleteLater(); + dropContent(data, sectionWidget, dropArea, true); + } + } + + // mouse is over container + if (InvalidDropArea == dropArea) + { + dropArea = m_ContainerDropOverlay->dropAreaUnderCursor(); + std::cout << "Cursor location: " << dropArea << std::endl; + if (dropArea != InvalidDropArea) + { + InternalContentData data; + FloatingWidget->takeContent(data); + FloatingWidget->deleteLater(); + dropContent(data, nullptr, dropArea, true); + } + } +} + + +ADS_NAMESPACE_END diff --git a/AdvancedDockingSystem/src/SectionContent.cpp b/AdvancedDockingSystem/src/SectionContent.cpp index 316822c..7923c7b 100644 --- a/AdvancedDockingSystem/src/SectionContent.cpp +++ b/AdvancedDockingSystem/src/SectionContent.cpp @@ -1,4 +1,4 @@ -#include +#include #include "ads/SectionContent.h" #include diff --git a/AdvancedDockingSystem/src/SectionTitleWidget.cpp b/AdvancedDockingSystem/src/SectionTitleWidget.cpp index 6a029b3..01f1a6f 100644 --- a/AdvancedDockingSystem/src/SectionTitleWidget.cpp +++ b/AdvancedDockingSystem/src/SectionTitleWidget.cpp @@ -20,7 +20,7 @@ #include "ads/SectionContent.h" #include "ads/SectionWidget.h" #include "ads/FloatingWidget.h" -#include +#include #include diff --git a/AdvancedDockingSystem/src/SectionWidget.cpp b/AdvancedDockingSystem/src/SectionWidget.cpp index f45558f..1f6c289 100644 --- a/AdvancedDockingSystem/src/SectionWidget.cpp +++ b/AdvancedDockingSystem/src/SectionWidget.cpp @@ -25,7 +25,7 @@ #include "ads/SectionTitleWidget.h" #include "ads/SectionContentWidget.h" #include "ads/FloatingWidget.h" -#include +#include ADS_NAMESPACE_BEGIN diff --git a/AdvancedDockingSystemDemo/src/dialogs/SectionContentListModel.h b/AdvancedDockingSystemDemo/src/dialogs/SectionContentListModel.h index 6b59e01..92034ed 100644 --- a/AdvancedDockingSystemDemo/src/dialogs/SectionContentListModel.h +++ b/AdvancedDockingSystemDemo/src/dialogs/SectionContentListModel.h @@ -5,7 +5,7 @@ #include #include #include -#include "../../../AdvancedDockingSystem/include/ads/ContainerWidget.h" +#include "../../../AdvancedDockingSystem/include/ads/MainContainerWidget.h" #include "ads/API.h" #include "ads/SectionContent.h" ADS_NAMESPACE_BEGIN diff --git a/AdvancedDockingSystemDemo/src/dialogs/SectionContentListWidget.h b/AdvancedDockingSystemDemo/src/dialogs/SectionContentListWidget.h index cf563b5..7899a08 100644 --- a/AdvancedDockingSystemDemo/src/dialogs/SectionContentListWidget.h +++ b/AdvancedDockingSystemDemo/src/dialogs/SectionContentListWidget.h @@ -2,7 +2,7 @@ #define SECTIONCONTENTLISTWIDGET #include -#include "../../../AdvancedDockingSystem/include/ads/ContainerWidget.h" +#include "../../../AdvancedDockingSystem/include/ads/MainContainerWidget.h" #include "ui_SectionContentListWidget.h" #include "ads/API.h" diff --git a/AdvancedDockingSystemDemo/src/mainwindow.h b/AdvancedDockingSystemDemo/src/mainwindow.h index 8d45a60..fee15d8 100644 --- a/AdvancedDockingSystemDemo/src/mainwindow.h +++ b/AdvancedDockingSystemDemo/src/mainwindow.h @@ -2,7 +2,7 @@ #define MAINWINDOW_H #include -#include "../../AdvancedDockingSystem/include/ads/ContainerWidget.h" +#include "../../AdvancedDockingSystem/include/ads/MainContainerWidget.h" #include "ads/API.h" #include "ads/SectionContent.h"