File indexing completed on 2024-06-02 03:47:29

0001 /*******************************************************************************
0002 ** Qt Advanced Docking System
0003 ** Copyright (C) 2017 Uwe Kindler
0004 ** 
0005 ** This library is free software; you can redistribute it and/or
0006 ** modify it under the terms of the GNU Lesser General Public
0007 ** License as published by the Free Software Foundation; either
0008 ** version 2.1 of the License, or (at your option) any later version.
0009 ** 
0010 ** This library is distributed in the hope that it will be useful,
0011 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013 ** Lesser General Public License for more details.
0014 ** 
0015 ** You should have received a copy of the GNU Lesser General Public
0016 ** License along with this library; If not, see <http://www.gnu.org/licenses/>.
0017 ******************************************************************************/
0018 
0019 
0020 //============================================================================
0021 /// \file   DockContainerWidget.cpp
0022 /// \author Uwe Kindler
0023 /// \date   24.02.2017
0024 /// \brief  Implementation of CDockContainerWidget class
0025 //============================================================================
0026 
0027 
0028 //============================================================================
0029 //                                   INCLUDES
0030 //============================================================================
0031 #include "DockContainerWidget.h"
0032 
0033 #include <QEvent>
0034 #include <QList>
0035 #include <QGridLayout>
0036 #include <QPointer>
0037 #include <QVariant>
0038 #include <QDebug>
0039 #include <QXmlStreamWriter>
0040 #include <QAbstractButton>
0041 #include <QLabel>
0042 #include <QTimer>
0043 #include <QMetaObject>
0044 #include <QMetaType>
0045 #include <QApplication>
0046 
0047 #include "DockManager.h"
0048 #include "DockAreaWidget.h"
0049 #include "DockWidget.h"
0050 #include "DockingStateReader.h"
0051 #include "FloatingDockContainer.h"
0052 #include "DockOverlay.h"
0053 #include "ads_globals.h"
0054 #include "DockSplitter.h"
0055 #include "DockWidgetTab.h"
0056 #include "DockAreaTitleBar.h"
0057 #include "DockFocusController.h"
0058 #include "AutoHideDockContainer.h"
0059 #include "AutoHideSideBar.h"
0060 #include "AutoHideTab.h"
0061 
0062 #include <functional>
0063 #include <iostream>
0064 
0065 #if QT_VERSION < 0x050900
0066 
0067 inline char toHexLower(uint value)
0068 {
0069     return "0123456789abcdef"[value & 0xF];
0070 }
0071 
0072 QByteArray qByteArrayToHex(const QByteArray& src, char separator)
0073 {
0074     if(src.size() == 0)
0075         return QByteArray();
0076 
0077     const int length = separator ? (src.size() * 3 - 1) : (src.size() * 2);
0078     QByteArray hex(length, Qt::Uninitialized);
0079     char *hexData = hex.data();
0080     const uchar *data = reinterpret_cast<const uchar *>(src.data());
0081     for (int i = 0, o = 0; i < src.size(); ++i) {
0082         hexData[o++] = toHexLower(data[i] >> 4);
0083         hexData[o++] = toHexLower(data[i] & 0xf);
0084 
0085         if ((separator) && (o < length))
0086             hexData[o++] = separator;
0087     }
0088     return hex;
0089 }
0090 #endif
0091 
0092 namespace ads
0093 {
0094 static unsigned int zOrderCounter = 0;
0095 
0096 enum eDropMode
0097 {
0098     DropModeIntoArea,///< drop widget into a dock area
0099     DropModeIntoContainer,///< drop into container
0100     DropModeInvalid///< invalid mode - do not drop
0101 };
0102 
0103 /**
0104  * Converts dock area ID to an index for array access
0105  */
0106 static int areaIdToIndex(DockWidgetArea area)
0107 {
0108     switch (area)
0109     {
0110     case LeftDockWidgetArea: return 0;
0111     case RightDockWidgetArea: return 1;
0112     case TopDockWidgetArea: return 2;
0113     case BottomDockWidgetArea: return 3;
0114     case CenterDockWidgetArea: return 4;
0115     default:
0116         return 4;
0117     }
0118 }
0119 
0120 /**
0121  * Helper function to ease insertion of dock area into splitter
0122  */
0123 static void insertWidgetIntoSplitter(QSplitter* Splitter, QWidget* widget, bool Append)
0124 {
0125     if (Append)
0126     {
0127         Splitter->addWidget(widget);
0128     }
0129     else
0130     {
0131         Splitter->insertWidget(0, widget);
0132     }
0133 }
0134 
0135 /**
0136  * Private data class of CDockContainerWidget class (pimpl)
0137  */
0138 class DockContainerWidgetPrivate
0139 {
0140 public:
0141     CDockContainerWidget* _this;
0142     QPointer<CDockManager> DockManager;
0143     unsigned int zOrderIndex = 0;
0144     QList<QPointer<CDockAreaWidget>> DockAreas;
0145     QList<CAutoHideDockContainer*> AutoHideWidgets;
0146     QMap<SideBarLocation, CAutoHideSideBar*> SideTabBarWidgets;
0147     QGridLayout* Layout = nullptr;
0148     CDockSplitter* RootSplitter = nullptr;
0149     bool isFloating = false;
0150     CDockAreaWidget* LastAddedAreaCache[5];
0151     int VisibleDockAreaCount = -1;
0152     CDockAreaWidget* TopLevelDockArea = nullptr;
0153     QTimer DelayedAutoHideTimer;
0154     CAutoHideTab* DelayedAutoHideTab;
0155     bool DelayedAutoHideShow = false;
0156 
0157     /**
0158      * Private data constructor
0159      */
0160     DockContainerWidgetPrivate(CDockContainerWidget* _public);
0161 
0162     /**
0163      * Adds dock widget to container and returns the dock area that contains
0164      * the inserted dock widget
0165      */
0166     CDockAreaWidget* addDockWidgetToContainer(DockWidgetArea area, CDockWidget* Dockwidget);
0167 
0168     /**
0169      * Adds dock widget to a existing DockWidgetArea
0170      */
0171     CDockAreaWidget* addDockWidgetToDockArea(DockWidgetArea area, CDockWidget* Dockwidget,
0172         CDockAreaWidget* TargetDockArea, int Index = -1);
0173 
0174     /**
0175      * Add dock area to this container
0176      */
0177     void addDockArea(CDockAreaWidget* NewDockWidget, DockWidgetArea area = CenterDockWidgetArea);
0178 
0179     /**
0180      * Drop floating widget into container
0181      */
0182     void dropIntoContainer(CFloatingDockContainer* FloatingWidget, DockWidgetArea area);
0183 
0184     /**
0185      * Drop floating widget into auto hide side bar
0186      */
0187     void dropIntoAutoHideSideBar(CFloatingDockContainer* FloatingWidget, DockWidgetArea area);
0188 
0189     /**
0190      * Creates a new tab for a widget dropped into the center of a section
0191      */
0192     void dropIntoCenterOfSection(CFloatingDockContainer* FloatingWidget,
0193         CDockAreaWidget* TargetArea, int TabIndex = 0);
0194 
0195     /**
0196      * Drop floating widget into dock area
0197      */
0198     void dropIntoSection(CFloatingDockContainer* FloatingWidget,
0199         CDockAreaWidget* TargetArea, DockWidgetArea area, int TabIndex = 0);
0200 
0201     /**
0202      * Moves the dock widget or dock area given in Widget parameter to a
0203      * new dock widget area
0204      */
0205     void moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area,
0206         int TabIndex = 0);
0207 
0208     /**
0209      * Moves the dock widget or dock area given in Widget parameter to a
0210      * a dock area in container
0211      */
0212     void moveToContainer(QWidget* Widgett, DockWidgetArea area);
0213 
0214     /**
0215      * Creates a new tab for a widget dropped into the center of a section
0216      */
0217     void moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea, int TabIndex = 0);
0218 
0219     /**
0220      * Moves the dock widget or dock area given in Widget parameter to
0221      * a auto hide sidebar area
0222      */
0223     void moveToAutoHideSideBar(QWidget* Widget, DockWidgetArea area, int TabIndex = TabDefaultInsertIndex);
0224 
0225 
0226     /**
0227      * Adds new dock areas to the internal dock area list
0228      */
0229     void addDockAreasToList(const QList<CDockAreaWidget*> NewDockAreas);
0230 
0231     /**
0232      * Wrapper function for DockAreas append, that ensures that dock area signals
0233      * are properly connected to dock container slots
0234      */
0235     void appendDockAreas(const QList<CDockAreaWidget*> NewDockAreas);
0236 
0237     /**
0238      * Save state of child nodes
0239      */
0240     void saveChildNodesState(QXmlStreamWriter& Stream, QWidget* Widget);
0241 
0242     /**
0243      * Save state of auto hide widgets
0244      */
0245     void saveAutoHideWidgetsState(QXmlStreamWriter& Stream);
0246 
0247     /**
0248      * Restore state of child nodes.
0249      * \param[in] Stream The data stream that contains the serialized state
0250      * \param[out] CreatedWidget The widget created from parsed data or 0 if
0251      * the parsed widget was an empty splitter
0252      * \param[in] Testing If Testing is true, only the stream data is
0253      * parsed without modifying anything.
0254      */
0255     bool restoreChildNodes(CDockingStateReader& Stream, QWidget*& CreatedWidget,
0256         bool Testing);
0257 
0258     /**
0259      * Restores a splitter.
0260      * \see restoreChildNodes() for details
0261      */
0262     bool restoreSplitter(CDockingStateReader& Stream, QWidget*& CreatedWidget,
0263         bool Testing);
0264 
0265     /**
0266      * Restores a dock area.
0267      * \see restoreChildNodes() for details
0268      */
0269     bool restoreDockArea(CDockingStateReader& Stream, QWidget*& CreatedWidget,
0270         bool Testing);
0271 
0272     /**
0273      * Restores a auto hide side bar
0274      */
0275     bool restoreSideBar(CDockingStateReader& Stream, QWidget*& CreatedWidget,
0276         bool Testing);
0277 
0278     /**
0279      * Helper function for recursive dumping of layout
0280      */
0281     void dumpRecursive(int level, QWidget* widget);
0282 
0283     /**
0284      * Calculate the drop mode from the given target position
0285      */
0286     eDropMode getDropMode(const QPoint& TargetPos);
0287 
0288     /**
0289      * Initializes the visible dock area count variable if it is not initialized
0290      * yet
0291      */
0292     void initVisibleDockAreaCount()
0293     {
0294         if (VisibleDockAreaCount > -1)
0295         {
0296             return;
0297         }
0298 
0299         VisibleDockAreaCount = 0;
0300         for (auto DockArea : DockAreas)
0301         {
0302             if (!DockArea)
0303             {
0304                 continue;
0305             }
0306             VisibleDockAreaCount += (DockArea->isHidden() ? 0 : 1);
0307         }
0308     }
0309 
0310     /**
0311      * Access function for the visible dock area counter
0312      */
0313     int& visibleDockAreaCount()
0314     {
0315         // Lazy initialisation - we initialize the VisibleDockAreaCount variable
0316         // on first use
0317         initVisibleDockAreaCount();
0318         return VisibleDockAreaCount;
0319     }
0320 
0321     /**
0322      * The visible dock area count changes, if dock areas are remove, added or
0323      * when its view is toggled
0324      */
0325     void onVisibleDockAreaCountChanged();
0326 
0327     void emitDockAreasRemoved()
0328     {
0329         onVisibleDockAreaCountChanged();
0330         Q_EMIT _this->dockAreasRemoved();
0331     }
0332 
0333     void emitDockAreasAdded()
0334     {
0335         onVisibleDockAreaCountChanged();
0336         Q_EMIT _this->dockAreasAdded();
0337     }
0338 
0339     /**
0340      * Helper function for creation of new splitter
0341      */
0342     CDockSplitter* newSplitter(Qt::Orientation orientation, QWidget* parent = nullptr)
0343     {
0344         CDockSplitter* s = new CDockSplitter(orientation, parent);
0345         s->setOpaqueResize(CDockManager::testConfigFlag(CDockManager::OpaqueSplitterResize));
0346         s->setChildrenCollapsible(false);
0347         return s;
0348     }
0349 
0350     /**
0351      * Ensures equal distribution of the sizes of a splitter if an dock widget
0352      * is inserted from code
0353      */
0354     void adjustSplitterSizesOnInsertion(QSplitter* Splitter, qreal LastRatio = 1.0)
0355     {
0356         int AreaSize = (Splitter->orientation() == Qt::Horizontal) ? Splitter->width() : Splitter->height();
0357         auto SplitterSizes = Splitter->sizes();
0358 
0359         qreal TotRatio = SplitterSizes.size() - 1.0 + LastRatio;
0360         for(int i = 0; i < SplitterSizes.size() -1; i++)
0361         {
0362             SplitterSizes[i] = AreaSize / TotRatio;
0363         }
0364         SplitterSizes.back() = AreaSize * LastRatio / TotRatio;
0365         Splitter->setSizes(SplitterSizes);
0366     }
0367 
0368     /**
0369      * This function forces the dock container widget to update handles of splitters
0370      * based if a central widget exists.
0371      */
0372     void updateSplitterHandles(QSplitter* splitter);
0373 
0374     /**
0375      * If no central widget exists, the widgets resize with the container.
0376      * If a central widget exists, the widgets surrounding the central widget
0377      * do not resize its height or width.
0378      */
0379     bool widgetResizesWithContainer(QWidget* widget);
0380 
0381 // private slots: ------------------------------------------------------------
0382     void onDockAreaViewToggled(bool Visible)
0383     {
0384         CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(_this->sender());
0385         VisibleDockAreaCount += Visible ? 1 : -1;
0386         onVisibleDockAreaCountChanged();
0387         Q_EMIT _this->dockAreaViewToggled(DockArea, Visible);
0388     }
0389 }; // struct DockContainerWidgetPrivate
0390 
0391 
0392 //============================================================================
0393 DockContainerWidgetPrivate::DockContainerWidgetPrivate(CDockContainerWidget* _public) :
0394     _this(_public)
0395 {
0396     std::fill(std::begin(LastAddedAreaCache),std::end(LastAddedAreaCache), nullptr);
0397     DelayedAutoHideTimer.setSingleShot(true);
0398     DelayedAutoHideTimer.setInterval(500);
0399     QObject::connect(&DelayedAutoHideTimer, &QTimer::timeout, [this](){
0400         auto GlobalPos = DelayedAutoHideTab->mapToGlobal(QPoint(0, 0));
0401         qApp->sendEvent(DelayedAutoHideTab, new QMouseEvent(QEvent::MouseButtonPress,
0402                 QPoint(0, 0), GlobalPos, Qt::LeftButton, {Qt::LeftButton}, Qt::NoModifier));
0403     });
0404 }
0405 
0406 
0407 //============================================================================
0408 eDropMode DockContainerWidgetPrivate::getDropMode(const QPoint& TargetPos)
0409 {
0410     CDockAreaWidget* DockArea = _this->dockAreaAt(TargetPos);
0411     auto dropArea = InvalidDockWidgetArea;
0412     auto ContainerDropArea = DockManager->containerOverlay()->dropAreaUnderCursor();
0413 
0414     if (DockArea)
0415     {
0416         auto dropOverlay = DockManager->dockAreaOverlay();
0417         dropOverlay->setAllowedAreas(DockArea->allowedAreas());
0418         dropArea = dropOverlay->showOverlay(DockArea);
0419         if (ContainerDropArea != InvalidDockWidgetArea &&
0420             ContainerDropArea != dropArea)
0421         {
0422             dropArea = InvalidDockWidgetArea;
0423         }
0424 
0425         if (dropArea != InvalidDockWidgetArea)
0426         {
0427             ADS_PRINT("Dock Area Drop Content: " << dropArea);
0428             return DropModeIntoArea;
0429         }
0430     }
0431 
0432     // mouse is over container
0433     if (InvalidDockWidgetArea == dropArea)
0434     {
0435         dropArea = ContainerDropArea;
0436         ADS_PRINT("Container Drop Content: " << dropArea);
0437         if (dropArea != InvalidDockWidgetArea)
0438         {
0439             return DropModeIntoContainer;
0440         }
0441     }
0442 
0443     return DropModeInvalid;
0444 }
0445 
0446 
0447 //============================================================================
0448 void DockContainerWidgetPrivate::onVisibleDockAreaCountChanged()
0449 {
0450     auto TopLevelDockArea = _this->topLevelDockArea();
0451 
0452     if (TopLevelDockArea)
0453     {
0454         this->TopLevelDockArea = TopLevelDockArea;
0455         TopLevelDockArea->updateTitleBarButtonVisibility(true);
0456     }
0457     else if (this->TopLevelDockArea)
0458     {
0459         this->TopLevelDockArea->updateTitleBarButtonVisibility(false);
0460         this->TopLevelDockArea = nullptr;
0461     }
0462 }
0463 
0464 
0465 //============================================================================
0466 void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* FloatingWidget,
0467     DockWidgetArea area)
0468 {
0469     auto InsertParam = internal::dockAreaInsertParameters(area);
0470     CDockContainerWidget* FloatingDockContainer = FloatingWidget->dockContainer();
0471     auto NewDockAreas = FloatingDockContainer->findChildren<CDockAreaWidget*>(
0472         QString(), Qt::FindChildrenRecursively);
0473     auto Splitter = RootSplitter;
0474 
0475     if (DockAreas.count() <= 1)
0476     {
0477         Splitter->setOrientation(InsertParam.orientation());
0478     }
0479     else if (Splitter->orientation() != InsertParam.orientation())
0480     {
0481         auto NewSplitter = newSplitter(InsertParam.orientation());
0482         QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
0483         NewSplitter->addWidget(Splitter);
0484         updateSplitterHandles(NewSplitter);
0485         Splitter = NewSplitter;
0486         delete li;
0487     }
0488 
0489     // Now we can insert the floating widget content into this container
0490     auto FloatingSplitter = FloatingDockContainer->rootSplitter();
0491     if (FloatingSplitter->count() == 1)
0492     {
0493         insertWidgetIntoSplitter(Splitter, FloatingSplitter->widget(0), InsertParam.append());
0494         updateSplitterHandles(Splitter);
0495     }
0496     else if (FloatingSplitter->orientation() == InsertParam.orientation())
0497     {
0498         int InsertIndex = InsertParam.append() ? Splitter->count() : 0;
0499         while (FloatingSplitter->count())
0500         {
0501             Splitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0));
0502             updateSplitterHandles(Splitter);
0503         }
0504     }
0505     else
0506     {
0507         insertWidgetIntoSplitter(Splitter, FloatingSplitter, InsertParam.append());
0508     }
0509 
0510     RootSplitter = Splitter;
0511     addDockAreasToList(NewDockAreas);
0512 
0513     // If we dropped the floating widget into the main dock container that does
0514     // not contain any dock widgets, then splitter is invisible and we need to
0515     // show it to display the docked widgets
0516     if (!Splitter->isVisible())
0517     {
0518         Splitter->show();
0519     }
0520     _this->dumpLayout();
0521 }
0522 
0523 
0524 //============================================================================
0525 void DockContainerWidgetPrivate::dropIntoAutoHideSideBar(CFloatingDockContainer* FloatingWidget, DockWidgetArea area)
0526 {
0527     auto SideBarLocation = internal::toSideBarLocation(area);
0528     auto NewDockAreas = FloatingWidget->findChildren<CDockAreaWidget*>(
0529         QString(), Qt::FindChildrenRecursively);
0530     int TabIndex = DockManager->containerOverlay()->tabIndexUnderCursor();
0531     for (auto DockArea : NewDockAreas)
0532     {
0533         auto DockWidgets = DockArea->dockWidgets();
0534         for (auto DockWidget : DockWidgets)
0535         {
0536             _this->createAndSetupAutoHideContainer(SideBarLocation, DockWidget, TabIndex++);
0537         }
0538     }
0539 }
0540 
0541 
0542 //============================================================================
0543 void DockContainerWidgetPrivate::dropIntoCenterOfSection(
0544     CFloatingDockContainer* FloatingWidget, CDockAreaWidget* TargetArea, int TabIndex)
0545 {
0546     CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer();
0547     auto NewDockWidgets = FloatingContainer->dockWidgets();
0548     auto TopLevelDockArea = FloatingContainer->topLevelDockArea();
0549     int NewCurrentIndex = -1;
0550     TabIndex = qMax(0, TabIndex);
0551 
0552     // If the floating widget contains only one single dock are, then the
0553     // current dock widget of the dock area will also be the future current
0554     // dock widget in the drop area.
0555     if (TopLevelDockArea)
0556     {
0557         NewCurrentIndex = TopLevelDockArea->currentIndex();
0558     }
0559 
0560     for (int i = 0; i < NewDockWidgets.count(); ++i)
0561     {
0562         CDockWidget* DockWidget = NewDockWidgets[i];
0563         TargetArea->insertDockWidget(TabIndex + i, DockWidget, false);
0564         // If the floating widget contains multiple visible dock areas, then we
0565         // simply pick the first visible open dock widget and make it
0566         // the current one.
0567         if (NewCurrentIndex < 0 && !DockWidget->isClosed())
0568         {
0569             NewCurrentIndex = i;
0570         }
0571     }
0572     TargetArea->setCurrentIndex(NewCurrentIndex + TabIndex);
0573     TargetArea->updateTitleBarVisibility();
0574     return;
0575 }
0576 
0577 
0578 //============================================================================
0579 void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* FloatingWidget,
0580         CDockAreaWidget* TargetArea, DockWidgetArea area, int TabIndex)
0581 {
0582     // Dropping into center means all dock widgets in the dropped floating
0583     // widget will become tabs of the drop area
0584     if (CenterDockWidgetArea == area)
0585     {
0586         dropIntoCenterOfSection(FloatingWidget, TargetArea, TabIndex);
0587         return;
0588     }
0589 
0590     CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer();
0591     auto InsertParam = internal::dockAreaInsertParameters(area);
0592     auto NewDockAreas = FloatingContainer->findChildren<CDockAreaWidget*>(
0593         QString(), Qt::FindChildrenRecursively);
0594     auto TargetAreaSplitter = TargetArea->parentSplitter();
0595     int AreaIndex = TargetAreaSplitter->indexOf(TargetArea);
0596     auto FloatingSplitter = FloatingContainer->rootSplitter();
0597     if (TargetAreaSplitter->orientation() == InsertParam.orientation())
0598     {
0599         auto Sizes = TargetAreaSplitter->sizes();
0600         int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height();
0601         bool AdjustSplitterSizes = true;
0602         if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1)
0603         {
0604             TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), FloatingSplitter);
0605             updateSplitterHandles(TargetAreaSplitter);
0606         }
0607         else
0608         {
0609             AdjustSplitterSizes = (FloatingSplitter->count() == 1);
0610             int InsertIndex = AreaIndex + InsertParam.insertOffset();
0611             while (FloatingSplitter->count())
0612             {
0613                 TargetAreaSplitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0));
0614                 updateSplitterHandles(TargetAreaSplitter);
0615             }
0616         }
0617 
0618         if (AdjustSplitterSizes)
0619         {
0620             int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2;
0621             Sizes[AreaIndex] = Size;
0622             Sizes.insert(AreaIndex, Size);
0623             TargetAreaSplitter->setSizes(Sizes);
0624         }
0625     }
0626     else
0627     {
0628         QSplitter* NewSplitter = newSplitter(InsertParam.orientation());
0629         int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height();
0630         bool AdjustSplitterSizes = true;
0631         if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1)
0632         {
0633             NewSplitter->addWidget(FloatingSplitter);
0634             updateSplitterHandles(NewSplitter);
0635         }
0636         else
0637         {
0638             AdjustSplitterSizes = (FloatingSplitter->count() == 1);
0639             while (FloatingSplitter->count())
0640             {
0641                 NewSplitter->addWidget(FloatingSplitter->widget(0));
0642                 updateSplitterHandles(NewSplitter);
0643             }
0644         }
0645 
0646         // Save the sizes before insertion and restore it later to prevent
0647         // shrinking of existing area
0648         auto Sizes = TargetAreaSplitter->sizes();
0649         insertWidgetIntoSplitter(NewSplitter, TargetArea, !InsertParam.append());
0650         updateSplitterHandles(NewSplitter);
0651         if (AdjustSplitterSizes)
0652         {
0653             int Size = TargetAreaSize / 2;
0654             NewSplitter->setSizes({Size, Size});
0655         }
0656         TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter);
0657         TargetAreaSplitter->setSizes(Sizes);
0658         updateSplitterHandles(TargetAreaSplitter);
0659     }
0660 
0661     addDockAreasToList(NewDockAreas);
0662     _this->dumpLayout();
0663 }
0664 
0665 
0666 //============================================================================
0667 void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea,
0668     int TabIndex)
0669 {
0670     auto DroppedDockWidget = qobject_cast<CDockWidget*>(Widget);
0671     auto DroppedArea = qobject_cast<CDockAreaWidget*>(Widget);
0672 
0673     TabIndex = qMax(0, TabIndex);
0674     if (DroppedDockWidget)
0675     {
0676         CDockAreaWidget* OldDockArea = DroppedDockWidget->dockAreaWidget();
0677         if (OldDockArea == TargetArea)
0678         {
0679             return;
0680         }
0681 
0682         if (OldDockArea)
0683         {
0684             OldDockArea->removeDockWidget(DroppedDockWidget);
0685         }
0686         TargetArea->insertDockWidget(TabIndex, DroppedDockWidget, true);
0687     }
0688     else
0689     {
0690         QList<CDockWidget*> NewDockWidgets = DroppedArea->dockWidgets();
0691         int NewCurrentIndex = DroppedArea->currentIndex();
0692         for (int i = 0; i < NewDockWidgets.count(); ++i)
0693         {
0694             CDockWidget* DockWidget = NewDockWidgets[i];
0695             TargetArea->insertDockWidget(TabIndex + i, DockWidget, false);
0696         }
0697         TargetArea->setCurrentIndex(TabIndex + NewCurrentIndex);
0698         DroppedArea->dockContainer()->removeDockArea(DroppedArea);
0699         DroppedArea->deleteLater();
0700     }
0701 
0702     TargetArea->updateTitleBarVisibility();
0703     return;
0704 }
0705 
0706 
0707 //============================================================================
0708 void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area,
0709     int TabIndex)
0710 {
0711     // Dropping into center means all dock widgets in the dropped floating
0712     // widget will become tabs of the drop area
0713     if (CenterDockWidgetArea == area)
0714     {
0715         moveIntoCenterOfSection(Widget, TargetArea, TabIndex);
0716         return;
0717     }
0718 
0719 
0720     CDockWidget* DroppedDockWidget = qobject_cast<CDockWidget*>(Widget);
0721     CDockAreaWidget* DroppedDockArea = qobject_cast<CDockAreaWidget*>(Widget);
0722     CDockAreaWidget* NewDockArea;
0723     if (DroppedDockWidget)
0724     {
0725         NewDockArea = new CDockAreaWidget(DockManager, _this);
0726         CDockAreaWidget* OldDockArea = DroppedDockWidget->dockAreaWidget();
0727         if (OldDockArea)
0728         {
0729             OldDockArea->removeDockWidget(DroppedDockWidget);
0730         }
0731         NewDockArea->addDockWidget(DroppedDockWidget);
0732     }
0733     else
0734     {
0735         DroppedDockArea->dockContainer()->removeDockArea(DroppedDockArea);
0736         NewDockArea = DroppedDockArea;
0737     }
0738 
0739     auto InsertParam = internal::dockAreaInsertParameters(area);
0740     auto TargetAreaSplitter = TargetArea->parentSplitter();
0741     int AreaIndex = TargetAreaSplitter->indexOf(TargetArea);
0742     auto Sizes = TargetAreaSplitter->sizes();
0743     if (TargetAreaSplitter->orientation() == InsertParam.orientation())
0744     {
0745         int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height();
0746         TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), NewDockArea);
0747         updateSplitterHandles(TargetAreaSplitter);
0748         int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2;
0749         Sizes[AreaIndex] = Size;
0750         Sizes.insert(AreaIndex, Size);
0751     }
0752     else
0753     {
0754         int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height();
0755         QSplitter* NewSplitter = newSplitter(InsertParam.orientation());
0756         NewSplitter->addWidget(TargetArea);
0757         insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append());
0758         updateSplitterHandles(NewSplitter);
0759         int Size = TargetAreaSize / 2;
0760         NewSplitter->setSizes({Size, Size});
0761         TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter);
0762         updateSplitterHandles(TargetAreaSplitter);
0763     }
0764     TargetAreaSplitter->setSizes(Sizes);
0765 
0766     addDockAreasToList({NewDockArea});
0767 }
0768 
0769 
0770 //============================================================================
0771 void DockContainerWidgetPrivate::moveToAutoHideSideBar(QWidget* Widget, DockWidgetArea area, int TabIndex)
0772 {
0773     CDockWidget* DroppedDockWidget = qobject_cast<CDockWidget*>(Widget);
0774     CDockAreaWidget* DroppedDockArea = qobject_cast<CDockAreaWidget*>(Widget);
0775     auto SideBarLocation = internal::toSideBarLocation(area);
0776 
0777     if (DroppedDockWidget)
0778     {
0779         if (_this == DroppedDockWidget->dockContainer())
0780         {
0781             DroppedDockWidget->setAutoHide(true, SideBarLocation, TabIndex);
0782         }
0783         else
0784         {
0785             _this->createAndSetupAutoHideContainer(SideBarLocation, DroppedDockWidget, TabIndex);
0786         }
0787     }
0788     else
0789     {
0790         if (_this == DroppedDockArea->dockContainer())
0791         {
0792             DroppedDockArea->setAutoHide(true, SideBarLocation, TabIndex);
0793         }
0794         else
0795         {
0796             for (const auto DockWidget : DroppedDockArea->openedDockWidgets())
0797             {
0798                 if (!DockWidget->features().testFlag(
0799                     CDockWidget::DockWidgetPinnable))
0800                 {
0801                     continue;
0802                 }
0803 
0804                 _this->createAndSetupAutoHideContainer(SideBarLocation,
0805                     DockWidget, TabIndex++);
0806             }
0807         }
0808     }
0809 }
0810 
0811 
0812 //============================================================================
0813 void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter )
0814 {
0815     if (!DockManager->centralWidget() || !splitter)
0816     {
0817         return;
0818     }
0819 
0820     for (int i = 0; i < splitter->count(); ++i)
0821     {
0822         splitter->setStretchFactor(i, widgetResizesWithContainer(splitter->widget(i)) ? 1 : 0);
0823     }
0824 }
0825 
0826 
0827 //============================================================================
0828 bool DockContainerWidgetPrivate::widgetResizesWithContainer(QWidget* widget)
0829 {
0830     if (!DockManager->centralWidget())
0831     {
0832         return true;
0833     }
0834 
0835     auto Area = qobject_cast<CDockAreaWidget*>(widget);
0836     if(Area)
0837     {
0838         return Area->isCentralWidgetArea();
0839     }
0840 
0841     auto innerSplitter = qobject_cast<CDockSplitter*>(widget);
0842     if (innerSplitter)
0843     {
0844         return innerSplitter->isResizingWithContainer();
0845     }
0846 
0847     return false;
0848 }
0849 
0850 
0851 
0852 //============================================================================
0853 void DockContainerWidgetPrivate::moveToContainer(QWidget* Widget, DockWidgetArea area)
0854 {
0855     CDockWidget* DroppedDockWidget = qobject_cast<CDockWidget*>(Widget);
0856     CDockAreaWidget* DroppedDockArea = qobject_cast<CDockAreaWidget*>(Widget);
0857     CDockAreaWidget* NewDockArea;
0858 
0859     if (DroppedDockWidget)
0860     {
0861         NewDockArea = new CDockAreaWidget(DockManager, _this);
0862         CDockAreaWidget* OldDockArea = DroppedDockWidget->dockAreaWidget();
0863         if (OldDockArea)
0864         {
0865             OldDockArea->removeDockWidget(DroppedDockWidget);
0866         }
0867         NewDockArea->addDockWidget(DroppedDockWidget);
0868     }
0869     else
0870     {
0871         // We check, if we insert the dropped widget into the same place that
0872         // it already has and do nothing, if it is the same place. It would
0873         // also work without this check, but it looks nicer with the check
0874         // because there will be no layout updates
0875         auto Splitter = DroppedDockArea->parentSplitter();
0876         auto InsertParam = internal::dockAreaInsertParameters(area);
0877         if (Splitter == RootSplitter && InsertParam.orientation() == Splitter->orientation())
0878         {
0879             if (InsertParam.append() && Splitter->lastWidget() == DroppedDockArea)
0880             {
0881                 return;
0882             }
0883             else if (!InsertParam.append() && Splitter->firstWidget() == DroppedDockArea)
0884             {
0885                 return;
0886             }
0887         }
0888         DroppedDockArea->dockContainer()->removeDockArea(DroppedDockArea);
0889         NewDockArea = DroppedDockArea;
0890     }
0891 
0892     addDockArea(NewDockArea, area);
0893     LastAddedAreaCache[areaIdToIndex(area)] = NewDockArea;
0894 }
0895 
0896 
0897 //============================================================================
0898 void DockContainerWidgetPrivate::addDockAreasToList(const QList<CDockAreaWidget*> NewDockAreas)
0899 {
0900     int CountBefore = DockAreas.count();
0901     int NewAreaCount = NewDockAreas.count();
0902     appendDockAreas(NewDockAreas);
0903     // If the user dropped a floating widget that contains only one single
0904     // visible dock area, then its title bar button TitleBarButtonUndock is
0905     // likely hidden. We need to ensure, that it is visible
0906     for (auto DockArea : NewDockAreas)
0907     {
0908         DockArea->titleBarButton(TitleBarButtonClose)->setVisible(true);
0909         DockArea->titleBarButton(TitleBarButtonAutoHide)->setVisible(true);
0910     }
0911 
0912     // We need to ensure, that the dock area title bar is visible. The title bar
0913     // is invisible, if the dock are is a single dock area in a floating widget.
0914     if (1 == CountBefore)
0915     {
0916         DockAreas.at(0)->updateTitleBarVisibility();
0917     }
0918 
0919     if (1 == NewAreaCount)
0920     {
0921         DockAreas.last()->updateTitleBarVisibility();
0922     }
0923 
0924     emitDockAreasAdded();
0925 }
0926 
0927 
0928 //============================================================================
0929 void DockContainerWidgetPrivate::appendDockAreas(const QList<CDockAreaWidget*> NewDockAreas)
0930 {
0931     for (auto *newDockArea : NewDockAreas)
0932     {
0933         DockAreas.append(newDockArea);
0934     }
0935     for (auto DockArea : NewDockAreas)
0936     {
0937         QObject::connect(DockArea,
0938             &CDockAreaWidget::viewToggled,
0939             _this,
0940             std::bind(&DockContainerWidgetPrivate::onDockAreaViewToggled, this, std::placeholders::_1));
0941     }
0942 }
0943 
0944 
0945 //============================================================================
0946 void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter& s, QWidget* Widget)
0947 {
0948     QSplitter* Splitter = qobject_cast<QSplitter*>(Widget);
0949     if (Splitter)
0950     {
0951         s.writeStartElement("Splitter");
0952         s.writeAttribute("Orientation", (Splitter->orientation() == Qt::Horizontal) ? "|" : "-");
0953         s.writeAttribute("Count", QString::number(Splitter->count()));
0954         ADS_PRINT("NodeSplitter orient: " << Splitter->orientation()
0955             << " WidgetCont: " << Splitter->count());
0956             for (int i = 0; i < Splitter->count(); ++i)
0957             {
0958                 saveChildNodesState(s, Splitter->widget(i));
0959             }
0960 
0961             s.writeStartElement("Sizes");
0962             for (auto Size : Splitter->sizes())
0963             {
0964                 s.writeCharacters(QString::number(Size) + QStringLiteral(" "));
0965             }
0966             s.writeEndElement();
0967         s.writeEndElement();
0968     }
0969     else
0970     {
0971         CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(Widget);
0972         if (DockArea)
0973         {
0974             DockArea->saveState(s);
0975         }
0976     }
0977 }
0978 
0979 
0980 //============================================================================
0981 void DockContainerWidgetPrivate::saveAutoHideWidgetsState(QXmlStreamWriter& s)
0982 {
0983     for (const auto sideTabBar : SideTabBarWidgets.values())
0984     {
0985         if (!sideTabBar->count())
0986         {
0987             continue;
0988         }
0989 
0990         sideTabBar->saveState(s);
0991     }
0992 }
0993 
0994 
0995 //============================================================================
0996 bool DockContainerWidgetPrivate::restoreSplitter(CDockingStateReader& s,
0997     QWidget*& CreatedWidget, bool Testing)
0998 {
0999     bool Ok;
1000     QString OrientationStr = s.attributes().value("Orientation").toString();
1001 
1002     // Check if the orientation string is right
1003     if (!OrientationStr.startsWith(QStringLiteral("|")) && !OrientationStr.startsWith(QStringLiteral("-")))
1004     {
1005         return false;
1006     }
1007 
1008     // The "|" shall indicate a vertical splitter handle which in turn means
1009     // a Horizontal orientation of the splitter layout.
1010     bool HorizontalSplitter = OrientationStr.startsWith(QStringLiteral("|"));
1011     // In version 0 we had a small bug. The "|" indicated a vertical orientation,
1012     // but this is wrong, because only the splitter handle is vertical, the
1013     // layout of the splitter is a horizontal layout. We fix this here
1014     if (s.fileVersion() == 0)
1015     {
1016         HorizontalSplitter = !HorizontalSplitter;
1017     }
1018 
1019     int Orientation = HorizontalSplitter ? Qt::Horizontal : Qt::Vertical;
1020     int WidgetCount = s.attributes().value("Count").toInt(&Ok);
1021     if (!Ok)
1022     {
1023         return false;
1024     }
1025     ADS_PRINT("Restore NodeSplitter Orientation: " <<  Orientation <<
1026             " WidgetCount: " << WidgetCount);
1027     QSplitter* Splitter = nullptr;
1028     if (!Testing)
1029     {
1030         Splitter = newSplitter(static_cast<Qt::Orientation>(Orientation));
1031     }
1032     bool Visible = false;
1033     QList<int> Sizes;
1034     while (s.readNextStartElement())
1035     {
1036         QWidget* ChildNode = nullptr;
1037         bool Result = true;
1038         if (s.name() == QLatin1String("Splitter"))
1039         {
1040             Result = restoreSplitter(s, ChildNode, Testing);
1041         }
1042         else if (s.name() == QLatin1String("Area"))
1043         {
1044             Result = restoreDockArea(s, ChildNode, Testing);
1045         }
1046         else if (s.name() == QLatin1String("Sizes"))
1047         {
1048             QString sSizes = s.readElementText().trimmed();
1049             ADS_PRINT("Sizes: " << sSizes);
1050             QTextStream TextStream(&sSizes);
1051             while (!TextStream.atEnd())
1052             {
1053                 int value;
1054                 TextStream >> value;
1055                 Sizes.append(value);
1056             }
1057         }
1058         else
1059         {
1060             s.skipCurrentElement();
1061         }
1062 
1063         if (!Result)
1064         {
1065             return false;
1066         }
1067 
1068         if (Testing || !ChildNode)
1069         {
1070             continue;
1071         }
1072 
1073         ADS_PRINT("ChildNode isVisible " << ChildNode->isVisible()
1074             << " isVisibleTo " << ChildNode->isVisibleTo(Splitter));
1075         Splitter->addWidget(ChildNode);
1076         Visible |= ChildNode->isVisibleTo(Splitter);
1077     }
1078     if(!Testing)
1079     {
1080        updateSplitterHandles(Splitter);
1081     }
1082 
1083     if (Sizes.count() != WidgetCount)
1084     {
1085         return false;
1086     }
1087 
1088     if (!Testing)
1089     {
1090         if (!Splitter->count())
1091         {
1092             delete Splitter;
1093             Splitter = nullptr;
1094         }
1095         else
1096         {
1097             Splitter->setSizes(Sizes);
1098             Splitter->setVisible(Visible);
1099         }
1100         CreatedWidget = Splitter;
1101     }
1102     else
1103     {
1104         CreatedWidget = nullptr;
1105     }
1106 
1107     return true;
1108 }
1109 
1110 
1111 //============================================================================
1112 bool DockContainerWidgetPrivate::restoreDockArea(CDockingStateReader& s,
1113     QWidget*& CreatedWidget, bool Testing)
1114 {
1115     CDockAreaWidget* DockArea = nullptr;
1116     auto Result = CDockAreaWidget::restoreState(s, DockArea, Testing, _this);
1117     if (Result && DockArea)
1118     {
1119         appendDockAreas({DockArea});
1120     }
1121     CreatedWidget = DockArea;
1122     return Result;
1123 }
1124 
1125 
1126 //============================================================================
1127 bool DockContainerWidgetPrivate::restoreSideBar(CDockingStateReader& s,
1128     QWidget*& CreatedWidget, bool Testing)
1129 {
1130     Q_UNUSED(CreatedWidget)
1131     // Simply ignore side bar auto hide widgets from saved state if
1132     // auto hide support is disabled
1133     if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled))
1134     {
1135         return true;
1136     }
1137 
1138     bool Ok;
1139     auto Area = (ads::SideBarLocation)s.attributes().value("Area").toInt(&Ok);
1140     if (!Ok)
1141     {
1142         return false;
1143     }
1144 
1145     while (s.readNextStartElement())
1146     {
1147         if (s.name() != QLatin1String("Widget"))
1148         {
1149             continue;
1150         }
1151 
1152         auto Name = s.attributes().value("Name");
1153         if (Name.isEmpty())
1154         {
1155             return false;
1156         }
1157 
1158         bool Ok;
1159         bool Closed = s.attributes().value("Closed").toInt(&Ok);
1160         if (!Ok)
1161         {
1162             return false;
1163         }
1164 
1165         int Size = s.attributes().value("Size").toInt(&Ok);
1166         if (!Ok)
1167         {
1168             return false;
1169         }
1170 
1171         s.skipCurrentElement();
1172         CDockWidget* DockWidget = DockManager->findDockWidget(Name.toString());
1173         if (!DockWidget || Testing)
1174         {
1175             continue;
1176         }
1177 
1178         auto SideBar = _this->autoHideSideBar(Area);
1179         CAutoHideDockContainer* AutoHideContainer;
1180         if (DockWidget->isAutoHide())
1181         {
1182             AutoHideContainer = DockWidget->autoHideDockContainer();
1183             if (AutoHideContainer->autoHideSideBar() != SideBar)
1184             {
1185                 SideBar->addAutoHideWidget(AutoHideContainer);
1186             }
1187         }
1188         else
1189         {
1190             AutoHideContainer = SideBar->insertDockWidget(-1, DockWidget);
1191         }
1192         AutoHideContainer->setSize(Size);
1193         DockWidget->setProperty(internal::ClosedProperty, Closed);
1194         DockWidget->setProperty(internal::DirtyProperty, false);
1195     }
1196 
1197     return true;
1198 }
1199 
1200 
1201 //============================================================================
1202 bool DockContainerWidgetPrivate::restoreChildNodes(CDockingStateReader& s,
1203     QWidget*& CreatedWidget, bool Testing)
1204 {
1205     bool Result = true;
1206     while (s.readNextStartElement())
1207     {
1208         if (s.name() == QLatin1String("Splitter"))
1209         {
1210             Result = restoreSplitter(s, CreatedWidget, Testing);
1211             ADS_PRINT("Splitter");
1212         }
1213         else if (s.name() == QLatin1String("Area"))
1214         {
1215             Result = restoreDockArea(s, CreatedWidget, Testing);
1216             ADS_PRINT("DockAreaWidget");
1217         }
1218         else if (s.name() == QLatin1String("SideBar"))
1219         {
1220             Result = restoreSideBar(s, CreatedWidget, Testing);
1221             ADS_PRINT("SideBar");
1222         }
1223         else
1224         {
1225             s.skipCurrentElement();
1226             ADS_PRINT("Unknown element");
1227         }
1228     }
1229 
1230     return Result;
1231 }
1232 
1233 
1234 //============================================================================
1235 CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToContainer(DockWidgetArea area,
1236     CDockWidget* Dockwidget)
1237 {
1238     CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this);
1239     NewDockArea->addDockWidget(Dockwidget);
1240     addDockArea(NewDockArea, area);
1241     NewDockArea->updateTitleBarVisibility();
1242     LastAddedAreaCache[areaIdToIndex(area)] = NewDockArea;
1243     return NewDockArea;
1244 }
1245 
1246 
1247 //============================================================================
1248 void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockWidgetArea area)
1249 {
1250     auto InsertParam = internal::dockAreaInsertParameters(area);
1251     // As long as we have only one dock area in the splitter we can adjust
1252     // its orientation
1253     if (DockAreas.count() <= 1)
1254     {
1255         RootSplitter->setOrientation(InsertParam.orientation());
1256     }
1257 
1258     QSplitter* Splitter = RootSplitter;
1259     if (Splitter->orientation() == InsertParam.orientation())
1260     {
1261         insertWidgetIntoSplitter(Splitter, NewDockArea, InsertParam.append());
1262         updateSplitterHandles(Splitter);
1263         if (Splitter->isHidden())
1264         {
1265             Splitter->show();
1266         }
1267     }
1268     else
1269     {
1270         auto NewSplitter = newSplitter(InsertParam.orientation());
1271         if (InsertParam.append())
1272         {
1273             QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
1274             NewSplitter->addWidget(Splitter);
1275             NewSplitter->addWidget(NewDockArea);
1276             updateSplitterHandles(NewSplitter);
1277             delete li;
1278         }
1279         else
1280         {
1281             NewSplitter->addWidget(NewDockArea);
1282             QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
1283             NewSplitter->addWidget(Splitter);
1284             updateSplitterHandles(NewSplitter);
1285             delete li;
1286         }
1287         RootSplitter = NewSplitter;
1288     }
1289 
1290     addDockAreasToList({NewDockArea});
1291 }
1292 
1293 
1294 //============================================================================
1295 void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
1296 {
1297 #if defined(QT_DEBUG)
1298     QSplitter* Splitter = qobject_cast<QSplitter*>(widget);
1299     QByteArray buf;
1300     buf.fill(' ', level * 4);
1301     if (Splitter)
1302     {
1303 #ifdef ADS_DEBUG_PRINT
1304         qDebug("%sSplitter %s v: %s c: %s",
1305             (const char*)buf,
1306             (Splitter->orientation() == Qt::Vertical) ? "--" : "|",
1307              Splitter->isHidden() ? " " : "v",
1308              QString::number(Splitter->count()).toStdString().c_str());
1309         std::cout << (const char*)buf << "Splitter "
1310             << ((Splitter->orientation() == Qt::Vertical) ? "--" : "|") << " "
1311             << (Splitter->isHidden() ? " " : "v") << " "
1312             << QString::number(Splitter->count()).toStdString() << std::endl;
1313 #endif
1314         for (int i = 0; i < Splitter->count(); ++i)
1315         {
1316             dumpRecursive(level + 1, Splitter->widget(i));
1317         }
1318     }
1319     else
1320     {
1321         CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(widget);
1322         if (!DockArea)
1323         {
1324             return;
1325         }
1326 #ifdef ADS_DEBUG_PRINT
1327         qDebug("%sDockArea", (const char*)buf);
1328         std::cout << (const char*)buf
1329             << (DockArea->isHidden() ? " " : "v")
1330             << (DockArea->openDockWidgetsCount() > 0 ? " " : "c")
1331             << " DockArea " << "[hs: " << DockArea->sizePolicy().horizontalStretch() << ", vs: " <<  DockArea->sizePolicy().verticalStretch() << "]" << std::endl;
1332         buf.fill(' ', (level + 1) * 4);
1333         for (int i = 0; i < DockArea->dockWidgetsCount(); ++i)
1334         {
1335             std::cout << (const char*)buf << (i == DockArea->currentIndex() ? "*" : " ");
1336             CDockWidget* DockWidget = DockArea->dockWidget(i);
1337             std::cout << (DockWidget->isHidden() ? " " : "v");
1338             std::cout << (DockWidget->isClosed() ? "c" : " ") << " ";
1339             std::cout << DockWidget->windowTitle().toStdString() << std::endl;
1340         }
1341 #endif
1342     }
1343 #else
1344     Q_UNUSED(level);
1345     Q_UNUSED(widget);
1346 #endif
1347 }
1348 
1349 
1350 //============================================================================
1351 CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetArea area,
1352     CDockWidget* Dockwidget, CDockAreaWidget* TargetDockArea, int Index)
1353 {
1354     if (CenterDockWidgetArea == area)
1355     {
1356         TargetDockArea->insertDockWidget(Index, Dockwidget);
1357         TargetDockArea->updateTitleBarVisibility();
1358         return TargetDockArea;
1359     }
1360 
1361     CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this);
1362     NewDockArea->addDockWidget(Dockwidget);
1363     auto InsertParam = internal::dockAreaInsertParameters(area);
1364 
1365     auto TargetAreaSplitter = TargetDockArea->parentSplitter();
1366     int index = TargetAreaSplitter ->indexOf(TargetDockArea);
1367     if (TargetAreaSplitter->orientation() == InsertParam.orientation())
1368     {
1369         ADS_PRINT("TargetAreaSplitter->orientation() == InsertParam.orientation()");
1370         TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea);
1371         updateSplitterHandles(TargetAreaSplitter);
1372         // do nothing, if flag is not enabled
1373         if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion))
1374         {
1375             adjustSplitterSizesOnInsertion(TargetAreaSplitter);
1376         }
1377     }
1378     else
1379     {
1380         ADS_PRINT("TargetAreaSplitter->orientation() != InsertParam.orientation()");
1381         auto TargetAreaSizes = TargetAreaSplitter->sizes();
1382         auto NewSplitter = newSplitter(InsertParam.orientation());
1383         NewSplitter->addWidget(TargetDockArea);
1384 
1385         insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append());
1386         updateSplitterHandles(NewSplitter);
1387         TargetAreaSplitter->insertWidget(index, NewSplitter);
1388         updateSplitterHandles(TargetAreaSplitter);
1389         if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion))
1390         {
1391             TargetAreaSplitter->setSizes(TargetAreaSizes);
1392             adjustSplitterSizesOnInsertion(NewSplitter);
1393         }
1394     }
1395 
1396     addDockAreasToList({NewDockArea});
1397     return NewDockArea;
1398 }
1399 
1400 
1401 //============================================================================
1402 CDockContainerWidget::CDockContainerWidget(CDockManager* DockManager, QWidget *parent) :
1403     QFrame(parent),
1404     d(new DockContainerWidgetPrivate(this))
1405 {
1406     d->DockManager = DockManager;
1407     d->isFloating = floatingWidget() != nullptr;
1408 
1409     d->Layout = new QGridLayout();
1410     d->Layout->setContentsMargins(0, 0, 0, 0);
1411     d->Layout->setSpacing(0);
1412     d->Layout->setColumnStretch(1, 1);
1413     d->Layout->setRowStretch(1, 1);
1414     setLayout(d->Layout);
1415 
1416     // The function d->newSplitter() accesses the config flags from dock
1417     // manager which in turn requires a properly constructed dock manager.
1418     // If this dock container is the dock manager, then it is not properly
1419     // constructed yet because this base class constructor is called before
1420     // the constructor of the DockManager private class
1421     if (DockManager != this)
1422     {
1423         d->DockManager->registerDockContainer(this);
1424         createRootSplitter();
1425         createSideTabBarWidgets();
1426     }
1427 }
1428 
1429 
1430 //============================================================================
1431 CDockContainerWidget::~CDockContainerWidget()
1432 {
1433     if (d->DockManager)
1434     {
1435         d->DockManager->removeDockContainer(this);
1436     }
1437 
1438     delete d;
1439 }
1440 
1441 
1442 //============================================================================
1443 CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget,
1444     CDockAreaWidget* DockAreaWidget, int Index)
1445 {
1446     auto TopLevelDockWidget = topLevelDockWidget();
1447     CDockAreaWidget* OldDockArea = Dockwidget->dockAreaWidget();
1448     if (OldDockArea)
1449     {
1450         OldDockArea->removeDockWidget(Dockwidget);
1451     }
1452 
1453     Dockwidget->setDockManager(d->DockManager);
1454     CDockAreaWidget* DockArea;
1455     if (DockAreaWidget)
1456     {
1457         DockArea = d->addDockWidgetToDockArea(area, Dockwidget, DockAreaWidget, Index);
1458     }
1459     else
1460     {
1461         DockArea = d->addDockWidgetToContainer(area, Dockwidget);
1462     }
1463 
1464     if (TopLevelDockWidget)
1465     {
1466         auto NewTopLevelDockWidget = topLevelDockWidget();
1467         // If the container contained only one visible dock widget, the we need
1468         // to emit a top level event for this widget because it is not the one and
1469         // only visible docked widget anymore
1470         if (!NewTopLevelDockWidget)
1471         {
1472             CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidget, false);
1473         }
1474     }
1475     return DockArea;
1476 }
1477 
1478 
1479 //============================================================================
1480 CAutoHideDockContainer* CDockContainerWidget::createAndSetupAutoHideContainer(
1481     SideBarLocation area, CDockWidget* DockWidget, int TabIndex)
1482 {
1483     if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled))
1484     {
1485         Q_ASSERT_X(false, "CDockContainerWidget::createAndInitializeDockWidgetOverlayContainer",
1486             "Requested area does not exist in config");
1487         return nullptr;
1488     }
1489     if (d->DockManager != DockWidget->dockManager())
1490     {
1491         DockWidget->setDockManager(d->DockManager); // Auto hide Dock Container needs a valid dock manager
1492     }
1493 
1494     return autoHideSideBar(area)->insertDockWidget(TabIndex, DockWidget);
1495 }
1496 
1497 
1498 //============================================================================
1499 void CDockContainerWidget::removeDockWidget(CDockWidget* Dockwidget)
1500 {
1501     CDockAreaWidget* Area = Dockwidget->dockAreaWidget();
1502     if (Area)
1503     {
1504         Area->removeDockWidget(Dockwidget);
1505     }
1506 }
1507 
1508 //============================================================================
1509 unsigned int CDockContainerWidget::zOrderIndex() const
1510 {
1511     return d->zOrderIndex;
1512 }
1513 
1514 
1515 //============================================================================
1516 bool CDockContainerWidget::isInFrontOf(CDockContainerWidget* Other) const
1517 {
1518     return this->zOrderIndex() > Other->zOrderIndex();
1519 }
1520 
1521 
1522 //============================================================================
1523 bool CDockContainerWidget::event(QEvent *e)
1524 {
1525     bool Result = QWidget::event(e);
1526     if (e->type() == QEvent::WindowActivate)
1527     {
1528         d->zOrderIndex = ++zOrderCounter;
1529     }
1530     else if (e->type() == QEvent::Show && !d->zOrderIndex)
1531     {
1532         d->zOrderIndex = ++zOrderCounter;
1533     }
1534 
1535     return Result;
1536 }
1537 
1538 
1539 //============================================================================
1540 QList<CAutoHideDockContainer*> CDockContainerWidget::autoHideWidgets() const
1541 {
1542     return d->AutoHideWidgets;
1543 }
1544 
1545 
1546 //============================================================================
1547 void CDockContainerWidget::addDockArea(CDockAreaWidget* DockAreaWidget,
1548     DockWidgetArea area)
1549 {
1550     CDockContainerWidget* Container = DockAreaWidget->dockContainer();
1551     if (Container && Container != this)
1552     {
1553         Container->removeDockArea(DockAreaWidget);
1554     }
1555 
1556     d->addDockArea(DockAreaWidget, area);
1557 }
1558 
1559 
1560 //============================================================================
1561 void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
1562 {
1563     ADS_PRINT("CDockContainerWidget::removeDockArea");
1564     // If it is an auto hide area, then there is nothing much to do
1565     if (area->isAutoHide())
1566     {
1567         area->setAutoHideDockContainer(nullptr);
1568         return;
1569     }
1570 
1571     area->disconnect(this);
1572     d->DockAreas.removeAll(area);
1573     auto Splitter = area->parentSplitter();
1574 
1575     // Remove are from parent splitter and recursively hide tree of parent
1576     // splitters if it has no visible content
1577     area->setParent(nullptr);
1578     internal::hideEmptyParentSplitters(Splitter);
1579 
1580     // Remove this area from cached areas
1581     auto p = std::find(std::begin(d->LastAddedAreaCache), std::end(d->LastAddedAreaCache), area);
1582     if (p != std::end(d->LastAddedAreaCache)) {
1583         *p = nullptr;
1584     }
1585 
1586     // If splitter has more than 1 widgets, we are finished and can leave
1587     if (Splitter->count() >  1)
1588     {
1589         goto emitAndExit;
1590     }
1591 
1592     // If this is the RootSplitter we need to remove empty splitters to
1593     // avoid too many empty splitters
1594     if (Splitter == d->RootSplitter)
1595     {
1596         ADS_PRINT("Removed from RootSplitter");
1597         // If splitter is empty, we are finished
1598         if (!Splitter->count())
1599         {
1600             Splitter->hide();
1601             goto emitAndExit;
1602         }
1603 
1604         QWidget* widget = Splitter->widget(0);
1605         auto ChildSplitter = qobject_cast<CDockSplitter*>(widget);
1606         // If the one and only content widget of the splitter is not a splitter
1607         // then we are finished
1608         if (!ChildSplitter)
1609         {
1610             goto emitAndExit;
1611         }
1612 
1613         // We replace the superfluous RootSplitter with the ChildSplitter
1614         ChildSplitter->setParent(nullptr);
1615         QLayoutItem* li = d->Layout->replaceWidget(Splitter, ChildSplitter);
1616         d->RootSplitter = ChildSplitter;
1617         delete li;
1618         ADS_PRINT("RootSplitter replaced by child splitter");
1619     }
1620     else if (Splitter->count() == 1)
1621     {
1622         ADS_PRINT("Replacing splitter with content");
1623         QSplitter* ParentSplitter = internal::findParent<QSplitter*>(Splitter);
1624         auto Sizes = ParentSplitter->sizes();
1625         QWidget* widget = Splitter->widget(0);
1626         widget->setParent(this);
1627         internal::replaceSplitterWidget(ParentSplitter, Splitter, widget);
1628         ParentSplitter->setSizes(Sizes);
1629     }
1630 
1631     delete Splitter;
1632     Splitter = nullptr;
1633 
1634 emitAndExit:
1635     updateSplitterHandles(Splitter);
1636     CDockWidget* TopLevelWidget = topLevelDockWidget();
1637 
1638     // Updated the title bar visibility of the dock widget if there is only
1639     // one single visible dock widget
1640     CDockWidget::emitTopLevelEventForWidget(TopLevelWidget, true);
1641     dumpLayout();
1642     d->emitDockAreasRemoved();
1643 }
1644 
1645 
1646 //============================================================================
1647 CDockAreaWidget* CDockContainerWidget::dockAreaAt(const QPoint& GlobalPos) const
1648 {
1649     for (const auto& DockArea : d->DockAreas)
1650     {
1651         if (DockArea && DockArea->isVisible() && DockArea->rect().contains(DockArea->mapFromGlobal(GlobalPos)))
1652         {
1653             return DockArea;
1654         }
1655     }
1656 
1657     return nullptr;
1658 }
1659 
1660 
1661 //============================================================================
1662 CDockAreaWidget* CDockContainerWidget::dockArea(int Index) const
1663 {
1664     return (Index < dockAreaCount()) ? d->DockAreas[Index] : nullptr;
1665 }
1666 
1667 
1668 //============================================================================
1669 bool CDockContainerWidget::isFloating() const
1670 {
1671     return d->isFloating;
1672 }
1673 
1674 
1675 //============================================================================
1676 int CDockContainerWidget::dockAreaCount() const
1677 {
1678     return d->DockAreas.count();
1679 }
1680 
1681 
1682 //============================================================================
1683 int CDockContainerWidget::visibleDockAreaCount() const
1684 {
1685     int Result = 0;
1686     for (auto DockArea : d->DockAreas)
1687     {
1688         Result += (!DockArea || DockArea->isHidden()) ? 0 : 1;
1689     }
1690 
1691     return Result;
1692 
1693     // TODO Cache or precalculate this to speed it up because it is used during
1694     // movement of floating widget
1695     //return d->visibleDockAreaCount();
1696 }
1697 
1698 
1699 //============================================================================
1700 void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWidget,
1701     const QPoint& TargetPos)
1702 {
1703     ADS_PRINT("CDockContainerWidget::dropFloatingWidget");
1704     CDockWidget* SingleDroppedDockWidget = FloatingWidget->topLevelDockWidget();
1705     CDockWidget* SingleDockWidget = topLevelDockWidget();
1706     auto dropArea = InvalidDockWidgetArea;
1707     auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor();
1708     bool Dropped = false;
1709 
1710     CDockAreaWidget* DockArea = dockAreaAt(TargetPos);
1711     // mouse is over dock area
1712     if (DockArea)
1713     {
1714         auto dropOverlay = d->DockManager->dockAreaOverlay();
1715         dropOverlay->setAllowedAreas(DockArea->allowedAreas());
1716         dropArea = dropOverlay->showOverlay(DockArea);
1717         if (ContainerDropArea != InvalidDockWidgetArea &&
1718             ContainerDropArea != dropArea)
1719         {
1720             dropArea = InvalidDockWidgetArea;
1721         }
1722 
1723         if (dropArea != InvalidDockWidgetArea)
1724         {
1725             ADS_PRINT("Dock Area Drop Content: " << dropArea);
1726             int TabIndex = d->DockManager->dockAreaOverlay()->tabIndexUnderCursor();
1727             d->dropIntoSection(FloatingWidget, DockArea, dropArea, TabIndex);
1728             Dropped = true;
1729         }
1730     }
1731 
1732     // mouse is over container or auto hide side bar
1733     if (InvalidDockWidgetArea == dropArea && InvalidDockWidgetArea != ContainerDropArea)
1734     {
1735         if (internal::isSideBarArea(ContainerDropArea))
1736         {
1737             ADS_PRINT("Container Drop Content: " << ContainerDropArea);
1738             d->dropIntoAutoHideSideBar(FloatingWidget, ContainerDropArea);
1739         }
1740         else
1741         {
1742             ADS_PRINT("Container Drop Content: " << ContainerDropArea);
1743             d->dropIntoContainer(FloatingWidget, ContainerDropArea);
1744         }
1745         Dropped = true;
1746     }
1747 
1748     // Remove the auto hide widgets from the FloatingWidget and insert
1749     // them into this widget
1750     for (auto AutohideWidget : FloatingWidget->dockContainer()->autoHideWidgets())
1751     {
1752         auto SideBar = autoHideSideBar(AutohideWidget->sideBarLocation());
1753         SideBar->addAutoHideWidget(AutohideWidget);
1754     }
1755 
1756     if (Dropped)
1757     { 
1758         // Fix https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351
1759         FloatingWidget->hideAndDeleteLater();
1760 
1761         // If we dropped a floating widget with only one single dock widget, then we
1762         // drop a top level widget that changes from floating to docked now
1763         CDockWidget::emitTopLevelEventForWidget(SingleDroppedDockWidget, false);
1764 
1765         // If there was a top level widget before the drop, then it is not top
1766         // level widget anymore
1767         CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false);
1768     }
1769 
1770     window()->activateWindow();
1771     if (SingleDroppedDockWidget)
1772     {
1773         d->DockManager->notifyWidgetOrAreaRelocation(SingleDroppedDockWidget);
1774     }
1775     d->DockManager->notifyFloatingWidgetDrop(FloatingWidget);
1776 }
1777 
1778 
1779 //============================================================================
1780 void CDockContainerWidget::dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget,
1781     int TabIndex)
1782 {
1783     CDockWidget* SingleDockWidget = topLevelDockWidget();
1784     if (TargetAreaWidget)
1785     {
1786         d->moveToNewSection(Widget, TargetAreaWidget, DropArea, TabIndex);
1787     }
1788     else if (internal::isSideBarArea(DropArea))
1789     {
1790         d->moveToAutoHideSideBar(Widget, DropArea, TabIndex);
1791     }
1792     else
1793     {
1794         d->moveToContainer(Widget, DropArea);
1795     }
1796 
1797     // If there was a top level widget before the drop, then it is not top
1798     // level widget anymore
1799     CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false);
1800 
1801     window()->activateWindow();
1802     d->DockManager->notifyWidgetOrAreaRelocation(Widget);
1803 }
1804 
1805 
1806 //============================================================================
1807 QList<CDockAreaWidget*> CDockContainerWidget::openedDockAreas() const
1808 {
1809     QList<CDockAreaWidget*> Result;
1810     for (auto DockArea : d->DockAreas)
1811     {
1812         if (DockArea && !DockArea->isHidden())
1813         {
1814             Result.append(DockArea);
1815         }
1816     }
1817 
1818     return Result;
1819 }
1820 
1821 
1822 //============================================================================
1823 QList<CDockWidget*> CDockContainerWidget::openedDockWidgets() const
1824 {
1825     QList<CDockWidget*> DockWidgetList;
1826     for (auto DockArea : d->DockAreas)
1827     {
1828         if (DockArea && !DockArea->isHidden())
1829         {
1830             DockWidgetList.append(DockArea->openedDockWidgets());
1831         }
1832     }
1833 
1834     return DockWidgetList;
1835 }
1836 
1837 
1838 //============================================================================
1839 bool CDockContainerWidget::hasOpenDockAreas() const
1840 {
1841     for (auto DockArea : d->DockAreas)
1842     {
1843         if (DockArea && !DockArea->isHidden())
1844         {
1845             return true;
1846         }
1847     }
1848 
1849     return false;
1850 }
1851 
1852 
1853 //============================================================================
1854 void CDockContainerWidget::saveState(QXmlStreamWriter& s) const
1855 {
1856     ADS_PRINT("CDockContainerWidget::saveState isFloating "
1857         << isFloating());
1858 
1859     s.writeStartElement("Container");
1860     s.writeAttribute("Floating", QString::number(isFloating() ? 1 : 0));
1861     if (isFloating())
1862     {
1863         CFloatingDockContainer* FloatingWidget = floatingWidget();
1864         QByteArray Geometry = FloatingWidget->saveGeometry();
1865 #if QT_VERSION < 0x050900
1866         s.writeTextElement("Geometry", qByteArrayToHex(Geometry, ' '));
1867 #else
1868         s.writeTextElement("Geometry", Geometry.toHex(' '));
1869 #endif
1870     }
1871     d->saveChildNodesState(s, d->RootSplitter);
1872     d->saveAutoHideWidgetsState(s);
1873     s.writeEndElement();
1874 }
1875 
1876 
1877 //============================================================================
1878 bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing)
1879 {
1880     bool IsFloating = s.attributes().value("Floating").toInt();
1881     ADS_PRINT("Restore CDockContainerWidget Floating" << IsFloating);
1882 
1883     QWidget* NewRootSplitter {};
1884     if (!Testing)
1885     {
1886         d->VisibleDockAreaCount = -1;// invalidate the dock area count
1887         d->DockAreas.clear();
1888         std::fill(std::begin(d->LastAddedAreaCache),std::end(d->LastAddedAreaCache), nullptr);
1889     }
1890 
1891     if (IsFloating)
1892     {
1893         ADS_PRINT("Restore floating widget");
1894         if (!s.readNextStartElement() || s.name() != QLatin1String("Geometry"))
1895         {
1896             return false;
1897         }
1898 
1899         QByteArray GeometryString = s.readElementText(CDockingStateReader::ErrorOnUnexpectedElement).toLocal8Bit();
1900         QByteArray Geometry = QByteArray::fromHex(GeometryString);
1901         if (Geometry.isEmpty())
1902         {
1903             return false;
1904         }
1905 
1906         if (!Testing)
1907         {
1908             CFloatingDockContainer* FloatingWidget = floatingWidget();
1909             if (FloatingWidget)
1910             {
1911                 FloatingWidget->restoreGeometry(Geometry);
1912             }
1913         }
1914     }
1915 
1916     if (!d->restoreChildNodes(s, NewRootSplitter, Testing))
1917     {
1918         return false;
1919     }
1920 
1921     if (Testing)
1922     {
1923         return true;
1924     }
1925 
1926     // If the root splitter is empty, rostoreChildNodes returns a 0 pointer
1927     // and we need to create a new empty root splitter
1928     if (!NewRootSplitter)
1929     {
1930         NewRootSplitter = d->newSplitter(Qt::Horizontal);
1931     }
1932 
1933     QLayoutItem* li = d->Layout->replaceWidget(d->RootSplitter, NewRootSplitter);
1934     auto OldRoot = d->RootSplitter;
1935     d->RootSplitter = qobject_cast<CDockSplitter*>(NewRootSplitter);
1936     OldRoot->deleteLater();
1937     delete li;
1938 
1939     return true;
1940 }
1941 
1942 
1943 //============================================================================
1944 CDockSplitter* CDockContainerWidget::rootSplitter() const
1945 {
1946     return d->RootSplitter;
1947 }
1948 
1949 
1950 //============================================================================
1951 void CDockContainerWidget::createRootSplitter()
1952 {
1953     if (d->RootSplitter)
1954     {
1955         return;
1956     }
1957     d->RootSplitter = d->newSplitter(Qt::Horizontal);
1958     d->Layout->addWidget(d->RootSplitter, 1, 1); // Add it to the center - the 0 and 2 indexes are used for the SideTabBar widgets
1959 }
1960 
1961 
1962 //============================================================================
1963 void CDockContainerWidget::createSideTabBarWidgets()
1964 {
1965     if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled))
1966     {
1967         return;
1968     }
1969 
1970     {
1971         auto Area = SideBarLocation::SideBarLeft;
1972         d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area);
1973         d->Layout->addWidget(d->SideTabBarWidgets[Area], 1, 0);
1974     }
1975 
1976     {
1977         auto Area = SideBarLocation::SideBarRight;
1978         d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area);
1979         d->Layout->addWidget(d->SideTabBarWidgets[Area], 1, 2);
1980     }
1981 
1982     {
1983         auto Area = SideBarLocation::SideBarBottom;
1984         d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area);
1985         d->Layout->addWidget(d->SideTabBarWidgets[Area], 2, 1);
1986     }
1987 
1988     {
1989         auto Area = SideBarLocation::SideBarTop;
1990         d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area);
1991         d->Layout->addWidget(d->SideTabBarWidgets[Area], 0, 1);
1992     }
1993 }
1994 
1995 
1996 //============================================================================
1997 void CDockContainerWidget::dumpLayout()
1998 {
1999 #if (ADS_DEBUG_LEVEL > 0)
2000     qDebug("\n\nDumping layout --------------------------");
2001     std::cout << "\n\nDumping layout --------------------------" << std::endl;
2002     d->dumpRecursive(0, d->RootSplitter);
2003     qDebug("--------------------------\n\n");
2004     std::cout << "--------------------------\n\n" << std::endl;
2005 #endif
2006 }
2007 
2008 
2009 //============================================================================
2010 CDockAreaWidget* CDockContainerWidget::lastAddedDockAreaWidget(DockWidgetArea area) const
2011 {
2012     return d->LastAddedAreaCache[areaIdToIndex(area)];
2013 }
2014 
2015 
2016 //============================================================================
2017 bool CDockContainerWidget::hasTopLevelDockWidget() const
2018 {
2019     auto DockAreas = openedDockAreas();
2020     if (DockAreas.count() != 1)
2021     {
2022         return false;
2023     }
2024 
2025     return DockAreas[0]->openDockWidgetsCount() == 1;
2026 }
2027 
2028 
2029 //============================================================================
2030 CDockWidget* CDockContainerWidget::topLevelDockWidget() const
2031 {
2032     auto TopLevelDockArea = topLevelDockArea();
2033     if (!TopLevelDockArea)
2034     {
2035         return nullptr;
2036     }
2037 
2038     auto DockWidgets = TopLevelDockArea->openedDockWidgets();
2039     if (DockWidgets.count() != 1)
2040     {
2041         return nullptr;
2042     }
2043 
2044     return DockWidgets[0];
2045 
2046 }
2047 
2048 
2049 //============================================================================
2050 CDockAreaWidget* CDockContainerWidget::topLevelDockArea() const
2051 {
2052     auto DockAreas = openedDockAreas();
2053     if (DockAreas.count() != 1)
2054     {
2055         return nullptr;
2056     }
2057 
2058     return DockAreas[0];
2059 }
2060 
2061 
2062 //============================================================================
2063 QList<CDockWidget*> CDockContainerWidget::dockWidgets() const
2064 {
2065     QList<CDockWidget*> Result;
2066     for (const auto& DockArea : d->DockAreas)
2067     {
2068         if (!DockArea)
2069         {
2070             continue;
2071         }
2072         Result.append(DockArea->dockWidgets());
2073     }
2074 
2075     return Result;
2076 }
2077 
2078 //============================================================================
2079 void CDockContainerWidget::updateSplitterHandles(QSplitter* splitter)
2080 {
2081     d->updateSplitterHandles(splitter);
2082 }
2083 
2084 //============================================================================
2085 void CDockContainerWidget::registerAutoHideWidget(CAutoHideDockContainer* AutohideWidget)
2086 {
2087     d->AutoHideWidgets.append(AutohideWidget);
2088     Q_EMIT autoHideWidgetCreated(AutohideWidget);
2089     ADS_PRINT("d->AutoHideWidgets.count() " << d->AutoHideWidgets.count());
2090 }
2091 
2092 //============================================================================
2093 void CDockContainerWidget::removeAutoHideWidget(CAutoHideDockContainer* AutohideWidget)
2094 {
2095     d->AutoHideWidgets.removeAll(AutohideWidget);
2096 }
2097 
2098 //============================================================================
2099 CDockWidget::DockWidgetFeatures CDockContainerWidget::features() const
2100 {
2101     CDockWidget::DockWidgetFeatures Features(CDockWidget::AllDockWidgetFeatures);
2102     for (const auto& DockArea : d->DockAreas)
2103     {
2104         if (!DockArea)
2105         {
2106             continue;
2107         }
2108         Features &= DockArea->features();
2109     }
2110 
2111     return Features;
2112 }
2113 
2114 
2115 //============================================================================
2116 CFloatingDockContainer* CDockContainerWidget::floatingWidget() const
2117 {
2118     return internal::findParent<CFloatingDockContainer*>(this);
2119 }
2120 
2121 
2122 //============================================================================
2123 void CDockContainerWidget::closeOtherAreas(CDockAreaWidget* KeepOpenArea)
2124 {
2125     for (const auto& DockArea : d->DockAreas)
2126     {
2127         if (!DockArea || DockArea == KeepOpenArea)
2128         {
2129             continue;
2130         }
2131 
2132         if (!DockArea->features(BitwiseAnd).testFlag(CDockWidget::DockWidgetClosable))
2133         {
2134             continue;
2135         }
2136 
2137         // We do not close areas with widgets with custom close handling
2138         if (DockArea->features(BitwiseOr).testFlag(CDockWidget::CustomCloseHandling))
2139         {
2140             continue;
2141         }
2142 
2143         DockArea->closeArea();
2144     }
2145 }
2146 
2147 //============================================================================
2148 CAutoHideSideBar* CDockContainerWidget::autoHideSideBar(SideBarLocation area) const
2149 {
2150     return d->SideTabBarWidgets[area];
2151 }
2152 
2153 
2154 //============================================================================
2155 QRect CDockContainerWidget::contentRect() const
2156 {
2157     if (!d->RootSplitter)
2158     {
2159         return QRect();
2160     }
2161 
2162     if (d->RootSplitter->hasVisibleContent())
2163     {
2164         return d->RootSplitter->geometry();
2165     }
2166     else
2167     {
2168         auto ContentRect = this->rect();
2169         ContentRect.adjust(
2170             autoHideSideBar(SideBarLeft)->sizeHint().width(),
2171             autoHideSideBar(SideBarTop)->sizeHint().height(),
2172             -autoHideSideBar(SideBarRight)->sizeHint().width(),
2173             -autoHideSideBar(SideBarBottom)->sizeHint().height());
2174 
2175         return ContentRect;
2176     }
2177 }
2178 
2179 
2180 //===========================================================================
2181 QRect CDockContainerWidget::contentRectGlobal() const
2182 {
2183     if (!d->RootSplitter)
2184     {
2185         return QRect();
2186     }
2187     return internal::globalGeometry(d->RootSplitter);
2188 }
2189 
2190 
2191 //===========================================================================
2192 CDockManager* CDockContainerWidget::dockManager() const
2193 {
2194     return d->DockManager;
2195 }
2196 
2197 
2198 //===========================================================================
2199 void CDockContainerWidget::handleAutoHideWidgetEvent(QEvent* e, QWidget* w)
2200 {
2201     if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideShowOnMouseOver))
2202     {
2203         return;
2204     }
2205 
2206     if (dockManager()->isRestoringState())
2207     {
2208         return;
2209     }
2210 
2211     auto AutoHideTab = qobject_cast<CAutoHideTab*>(w);
2212     if (AutoHideTab)
2213     {
2214         switch (e->type())
2215         {
2216         case QEvent::Enter:
2217              if (!AutoHideTab->dockWidget()->isVisible())
2218              {
2219                  d->DelayedAutoHideTab = AutoHideTab;
2220                  d->DelayedAutoHideShow = true;
2221                  d->DelayedAutoHideTimer.start();
2222              }
2223              else
2224              {
2225                  d->DelayedAutoHideTimer.stop();
2226              }
2227              break;
2228 
2229         case QEvent::MouseButtonPress:
2230              d->DelayedAutoHideTimer.stop();
2231              break;
2232 
2233         case QEvent::Leave:
2234              if (AutoHideTab->dockWidget()->isVisible())
2235              {
2236                  d->DelayedAutoHideTab = AutoHideTab;
2237                  d->DelayedAutoHideShow = false;
2238                  d->DelayedAutoHideTimer.start();
2239              }
2240              else
2241              {
2242                  d->DelayedAutoHideTimer.stop();
2243              }
2244              break;
2245 
2246         default:
2247             break;
2248         }
2249         return;
2250     }
2251 
2252     auto AutoHideContainer = qobject_cast<CAutoHideDockContainer*>(w);
2253     if (AutoHideContainer)
2254     {
2255         switch (e->type())
2256         {
2257         case QEvent::Enter:
2258         case QEvent::Hide:
2259              d->DelayedAutoHideTimer.stop();
2260              break;
2261 
2262         case QEvent::Leave:
2263              if (AutoHideContainer->isVisible())
2264              {
2265                  d->DelayedAutoHideTab = AutoHideContainer->autoHideTab();
2266                  d->DelayedAutoHideShow = false;
2267                  d->DelayedAutoHideTimer.start();
2268              }
2269              break;
2270 
2271         default:
2272             break;
2273         }
2274         return;
2275         return;
2276     }
2277 }
2278 } // namespace ads
2279 
2280 //---------------------------------------------------------------------------
2281 // EOF DockContainerWidget.cpp