1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 Uwe Kindler
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 or (at your option) any later version.
19 ** The licenses are as published by the Free Software Foundation
20 ** and appearing in the file LICENSE.LGPLv21 included in the packaging
21 ** of this file. Please review the following information to ensure
22 ** the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 3 or (at your option) any later version
28 ** approved by the KDE Free Qt Foundation. The licenses are as published by
29 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
30 ** included in the packaging of this file. Please review the following
31 ** information to ensure the GNU General Public License requirements will
32 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
33 **
34 ****************************************************************************/
35 
36 #pragma once
37 
38 #include "ads_globals.h"
39 #include "dockcontainerwidget.h"
40 #include "dockwidget.h"
41 #include "floatingdockcontainer.h"
42 
43 #include <utils/persistentsettings.h>
44 
45 #include <QByteArray>
46 #include <QDateTime>
47 #include <QFlags>
48 #include <QList>
49 #include <QMap>
50 #include <QString>
51 #include <QStringList>
52 #include <QtGui/QIcon>
53 #include <qobjectdefs.h>
54 
55 QT_BEGIN_NAMESPACE
56 class QMenu;
57 class QSettings;
58 QT_END_NAMESPACE
59 
60 namespace ADS {
61 
62 namespace Constants {
63 const char DEFAULT_WORKSPACE[] = "Essentials"; // This needs to align with a name of the shipped presets
64 const char STARTUP_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/StartupWorkspace";
65 const char AUTO_RESTORE_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/AutoRestoreLastWorkspace";
66 } // namespace Constants
67 
68 class DockManagerPrivate;
69 class FloatingDockContainer;
70 class FloatingDockContainerPrivate;
71 class DockComponentsFactory;
72 class DockContainerWidget;
73 class DockContainerWidgetPrivate;
74 class DockOverlay;
75 class DockAreaTabBar;
76 class DockWidgetTab;
77 class DockWidgetTabPrivate;
78 struct DockAreaWidgetPrivate;
79 class IconProvider;
80 
81 /**
82  * The central dock manager that maintains the complete docking system.
83  * With the configuration flags you can globally control the functionality
84  * of the docking system. The dock manager uses an internal stylesheet to
85  * style its components like splitters, tabs and buttons. If you want to
86  * disable this stylesheet because your application uses its own,
87  * just call the function for settings the stylesheet with an empty
88  * string.
89  * \code
90  * dockManager->setStyleSheet("");
91  * \endcode
92  **/
93 class ADS_EXPORT DockManager : public DockContainerWidget
94 {
95     Q_OBJECT
96 private:
97     DockManagerPrivate *d; ///< private data (pimpl)
98     friend class DockManagerPrivate;
99     friend class FloatingDockContainer;
100     friend class FloatingDockContainerPrivate;
101     friend class DockContainerWidget;
102     friend class DockContainerWidgetPrivate;
103     friend class DockAreaTabBar;
104     friend class DockWidgetTab;
105     friend struct DockAreaWidgetPrivate;
106     friend class DockWidgetTabPrivate;
107     friend class FloatingDragPreview;
108     friend class FloatingDragPreviewPrivate;
109     friend class DockAreaTitleBar;
110 
111 protected:
112     /**
113      * Registers the given floating widget in the internal list of
114      * floating widgets
115      */
116     void registerFloatingWidget(FloatingDockContainer *floatingWidget);
117 
118     /**
119      * Remove the given floating widget from the list of registered floating
120      * widgets
121      */
122     void removeFloatingWidget(FloatingDockContainer *floatingWidget);
123 
124     /**
125      * Registers the given dock container widget
126      */
127     void registerDockContainer(DockContainerWidget *dockContainer);
128 
129     /**
130      * Remove dock container from the internal list of registered dock
131      * containers
132      */
133     void removeDockContainer(DockContainerWidget *dockContainer);
134 
135     /**
136      * Overlay for containers
137      */
138     DockOverlay *containerOverlay() const;
139 
140     /**
141      * Overlay for dock areas
142      */
143     DockOverlay *dockAreaOverlay() const;
144 
145     /**
146      * A container needs to call this function if a widget has been dropped
147      * into it
148      */
149     void notifyWidgetOrAreaRelocation(QWidget *droppedWidget);
150 
151     /**
152      * This function is called, if a floating widget has been dropped into
153      * an new position.
154      * When this function is called, all dock widgets of the FloatingWidget
155      * are already inserted into its new position
156      */
157     void notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget);
158 
159     /**
160      * This function is called, if the given DockWidget has been relocated from
161      * the old container ContainerOld to the new container DockWidget->dockContainer()
162      */
163     void notifyDockWidgetRelocation(DockWidget *dockWidget, DockContainerWidget *containerOld);
164 
165     /**
166      * This function is called, if the given DockAreahas been relocated from
167      * the old container ContainerOld to the new container DockArea->dockContainer()
168      */
169     void notifyDockAreaRelocation(DockAreaWidget *dockArea, DockContainerWidget *containerOld);
170 
171     /**
172      * Show the floating widgets that has been created floating
173      */
174     void showEvent(QShowEvent *event) override;
175 
176 public:
177     using Super = DockContainerWidget;
178 
179     /**
180      * These global configuration flags configure some global dock manager settings.
181      * Set the dock manager flags, before you create the dock manager instance.
182      */
183     enum eConfigFlag {
184         ActiveTabHasCloseButton
185         = 0x0001, //!< If this flag is set, the active tab in a tab area has a close button
186         DockAreaHasCloseButton
187         = 0x0002, //!< If the flag is set each dock area has a close button
188         DockAreaCloseButtonClosesTab
189         = 0x0004, //!< If the flag is set, the dock area close button closes the active tab, if not set, it closes the complete dock area
190         OpaqueSplitterResize
191         = 0x0008, //!< See QSplitter::setOpaqueResize() documentation
192         XmlAutoFormattingEnabled
193         = 0x0010, //!< If enabled, the XML writer automatically adds line-breaks and indentation to empty sections between elements (ignorable whitespace).
194         XmlCompressionEnabled
195         = 0x0020, //!< If enabled, the XML output will be compressed and is not human readable anymore
196         TabCloseButtonIsToolButton
197         = 0x0040, //! If enabled the tab close buttons will be QToolButtons instead of QPushButtons - disabled by default
198         AllTabsHaveCloseButton
199         = 0x0080, //!< if this flag is set, then all tabs that are closable show a close button
200         RetainTabSizeWhenCloseButtonHidden
201         = 0x0100, //!< if this flag is set, the space for the close button is reserved even if the close button is not visible
202         OpaqueUndocking
203         = 0x0200, ///< If enabled, the widgets are immediately undocked into floating widgets, if disabled, only a draw preview is undocked and the real undocking is deferred until the mouse is released
204         DragPreviewIsDynamic
205         = 0x0400, ///< If opaque undocking is disabled, this flag defines the behavior of the drag preview window, if this flag is enabled, the preview will be adjusted dynamically to the drop area
206         DragPreviewShowsContentPixmap
207         = 0x0800, ///< If opaque undocking is disabled, the created drag preview window shows a copy of the content of the dock widget / dock are that is dragged
208         DragPreviewHasWindowFrame
209         = 0x1000, ///< If opaque undocking is disabled, then this flag configures if the drag preview is frameless or looks like a real window
210         AlwaysShowTabs
211         = 0x2000, ///< If this option is enabled, the tab of a dock widget is always displayed - even if it is the only visible dock widget in a floating widget.
212         DockAreaHasUndockButton
213         = 0x4000, //!< If the flag is set each dock area has an undock button
214         DockAreaHasTabsMenuButton
215         = 0x8000, //!< If the flag is set each dock area has a tabs menu button
216         DockAreaHideDisabledButtons
217         = 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the tollbar at all (enabling them will bring them back)
218         DockAreaDynamicTabsMenuButtonVisibility
219         = 0x20000, //!< If the flag is set, the tabs menu button will be shown only when it is required - that means, if the tabs are elided. If the tabs are not elided, it is hidden
220         FloatingContainerHasWidgetTitle
221         = 0x40000, //!< If set, the Floating Widget window title reflects the title of the current dock widget otherwise it displays application name as window title
222         FloatingContainerHasWidgetIcon
223         = 0x80000, //!< If set, the Floating Widget icon reflects the icon of the current dock widget otherwise it displays application icon
224         HideSingleCentralWidgetTitleBar
225         = 0x100000, //!< If there is only one single visible dock widget in the main dock container (the dock manager) and if this flag is set, then the titlebar of this dock widget will be hidden
226                     //!< this only makes sense for non draggable and non floatable widgets and enables the creation of some kind of "central" widget
227         FocusHighlighting
228         = 0x200000, //!< enables styling of focused dock widget tabs or floating widget titlebar
229         EqualSplitOnInsertion
230         = 0x400000, ///!< if enabled, the space is equally distributed to all widgets in a splitter
231 
232         DefaultDockAreaButtons = DockAreaHasCloseButton
233                                | DockAreaHasUndockButton
234                                | DockAreaHasTabsMenuButton,///< default configuration of dock area title bar buttons
235 
236         DefaultBaseConfig = DefaultDockAreaButtons
237                           | ActiveTabHasCloseButton
238                           | XmlCompressionEnabled
239                           | FloatingContainerHasWidgetTitle,///< default base configuration settings
240 
241         DefaultOpaqueConfig = DefaultBaseConfig
242                             | OpaqueSplitterResize
243                             | OpaqueUndocking, ///< the default configuration with opaque operations - this may cause issues if ActiveX or Qt 3D windows are involved
244 
245         DefaultNonOpaqueConfig = DefaultBaseConfig
246                                | DragPreviewShowsContentPixmap, ///< the default configuration for non opaque operations
247 
248         NonOpaqueWithWindowFrame = DefaultNonOpaqueConfig
249                                  | DragPreviewHasWindowFrame ///< the default configuration for non opaque operations that show a real window with frame
250     };
251     Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag)
252 
253     /**
254      * Default Constructor.
255      * If the given parent is a QMainWindow, the dock manager sets itself as the
256      * central widget.
257      * Before you create any dock widgets, you should properly setup the
258      * configuration flags via setConfigFlags().
259      */
260     DockManager(QWidget *parent = nullptr);
261 
262     /**
263      * Virtual Destructor
264      */
265     ~DockManager() override;
266 
267     /**
268      * This function returns the global configuration flags.
269      */
270     static ConfigFlags configFlags();
271 
272     /**
273      * Sets the global configuration flags for the whole docking system.
274      * Call this function before you create the dock manager and before
275      * your create the first dock widget.
276      */
277     static void setConfigFlags(const ConfigFlags flags);
278 
279     /**
280      * Set a certain config flag.
281      * \see setConfigFlags()
282      */
283     static void setConfigFlag(eConfigFlag flag, bool on = true);
284 
285     /**
286      * Returns true if the given config flag is set.
287      */
288     static bool testConfigFlag(eConfigFlag flag);
289 
290     /**
291      * Returns the global icon provider.
292      * The icon provider enables the use of custom icons in case using
293      * styleheets for icons is not an option.
294      */
295     static IconProvider &iconProvider();
296 
297     /**
298      * The distance the user needs to move the mouse with the left button
299      * hold down before a dock widget start floating.
300      */
301     static int startDragDistance();
302 
303     /**
304      * Helper function to set focus depending on the configuration of the
305      * FocusStyling flag
306      */
307     template <class QWidgetPtr>
setWidgetFocus(QWidgetPtr widget)308     static void setWidgetFocus(QWidgetPtr widget)
309     {
310         if (!DockManager::testConfigFlag(DockManager::FocusHighlighting))
311             return;
312 
313         widget->setFocus(Qt::OtherFocusReason);
314     }
315 
316     /**
317      * Set the QtCreator settings.
318      */
319     void setSettings(QSettings *settings);
320 
321     /**
322      * Set the path to the workspace presets folder.
323      */
324     void setWorkspacePresetsPath(const QString &path);
325 
326     void initialize();
327 
328     /**
329      * Adds dockwidget into the given area.
330      * If DockAreaWidget is not null, then the area parameter indicates the area
331      * into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will
332      * be dropped into the container. If you would like to add a dock widget
333      * tabified, then you need to add it to an existing dock area object
334      * into the CenterDockWidgetArea. The following code shows this:
335      * \code
336      * DockManager->addDockWidget(ads::CenterDockWidgetArea, NewDockWidget,
337      *         ExisitingDockArea);
338      * \endcode
339      * \return Returns the dock area widget that contains the new DockWidget
340      */
341     DockAreaWidget *addDockWidget(DockWidgetArea area,
342                                   DockWidget *dockWidget,
343                                   DockAreaWidget *dockAreaWidget = nullptr);
344 
345     /**
346      * This function will add the given Dockwidget to the given dock area as
347      * a new tab.
348      * If no dock area widget exists for the given area identifier, a new
349      * dock area widget is created.
350      */
351     DockAreaWidget *addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget);
352 
353     /**
354      * This function will add the given Dockwidget to the given DockAreaWidget
355      * as a new tab.
356      */
357     DockAreaWidget *addDockWidgetTabToArea(DockWidget *dockWidget, DockAreaWidget *dockAreaWidget);
358 
359     /**
360      * Adds the given DockWidget floating and returns the created
361      * CFloatingDockContainer instance.
362      */
363     FloatingDockContainer *addDockWidgetFloating(DockWidget *dockWidget);
364 
365     /**
366      * Searches for a registered doc widget with the given ObjectName
367      * \return Return the found dock widget or nullptr if a dock widget with the
368      * given name is not registered.
369      */
370     DockWidget *findDockWidget(const QString &objectName) const;
371 
372     /**
373      * Remove the given DockWidget from the dock manager.
374      */
375     void removeDockWidget(DockWidget *dockWidget);
376 
377     /**
378      * This function returns a readable reference to the internal dock
379      * widgets map so that it is possible to iterate over all dock widgets.
380      */
381     QMap<QString, DockWidget *> dockWidgetsMap() const;
382 
383     /**
384      * Returns the list of all active and visible dock containers
385      * Dock containers are the main dock manager and all floating widgets.
386      */
387     const QList<DockContainerWidget *> dockContainers() const;
388 
389     /**
390      * Returns the list of all floating widgets.
391      */
392     const QList<QPointer<FloatingDockContainer> > floatingWidgets() const;
393 
394     /**
395      * This function always return 0 because the main window is always behind
396      * any floating widget.
397      */
398     unsigned int zOrderIndex() const override;
399 
400     /**
401      * Saves the current state of the dockmanger and all its dock widgets
402      * into the returned QByteArray.
403      * The XmlMode enables / disables the auto formatting for the XmlStreamWriter.
404      * If auto formatting is enabled, the output is intended and line wrapped.
405      * The XmlMode XmlAutoFormattingDisabled is better if you would like to have
406      * a more compact XML output - i.e. for storage in ini files.
407      * The version number is stored as part of the data.
408      * To restore the saved state, pass the return value and version number
409      * to restoreState().
410      * \see restoreState()
411      */
412     QByteArray saveState(int version = 0) const;
413 
414     /**
415      * Restores the state of this dockmanagers dockwidgets.
416      * The version number is compared with that stored in state. If they do
417      * not match, the dockmanager's state is left unchanged, and this function
418      * returns false; otherwise, the state is restored, and this function
419      * returns true.
420      * \see saveState()
421      */
422     bool restoreState(const QByteArray &state, int version = 0);
423 
424     /**
425      * This function returns true between the restoringState() and
426      * stateRestored() signals.
427      */
428     bool isRestoringState() const;
429 
430     /**
431      * Request a focus change to the given dock widget.
432      * This function only has an effect, if the flag CDockManager::FocusStyling
433      * is enabled
434      */
435     void setDockWidgetFocused(DockWidget *dockWidget);
436 
437 signals:
438     /**
439      * This signal is emitted if the list of workspaces changed.
440      */
441     void workspaceListChanged();
442 
443     /**
444      * This signal is emitted if workspaces have been removed.
445      */
446     void workspacesRemoved();
447 
448     /**
449      * This signal is emitted, if the restore function is called, just before
450      * the dock manager starts restoring the state.
451      * If this function is called, nothing has changed yet.
452      */
453     void restoringState();
454 
455     /**
456      * This signal is emitted if the state changed in restoreState.
457      * The signal is emitted if the restoreState() function is called or
458      * if the openWorkspace() function is called.
459      */
460     void stateRestored();
461 
462     /**
463      * This signal is emitted, if the dock manager starts opening a
464      * workspace.
465      * Opening a workspace may take more than a second if there are
466      * many complex widgets. The application may use this signal
467      * to show some progress indicator or to change the mouse cursor
468      * into a busy cursor.
469      */
470     void openingWorkspace(const QString &workspaceName);
471 
472     /**
473      * This signal is emitted if the dock manager finished opening a
474      * workspace.
475      */
476     void workspaceOpened(const QString &workspaceName);
477 
478     /**
479      * This signal is emitted, if a new floating widget has been created.
480      * An application can use this signal to e.g. subscribe to events of
481      * the newly created window.
482      */
483     void floatingWidgetCreated(FloatingDockContainer *floatingWidget);
484 
485     /**
486      * This signal is emitted, if a new DockArea has been created.
487      * An application can use this signal to set custom icons or custom
488      * tooltips for the DockArea buttons.
489      */
490     void dockAreaCreated(DockAreaWidget *dockArea);
491 
492     /**
493      * This signal is emitted just before removal of the given DockWidget.
494      */
495     void dockWidgetAboutToBeRemoved(DockWidget *dockWidget);
496 
497     /**
498      * This signal is emitted if a dock widget has been removed with the remove
499      * removeDockWidget() function.
500      * If this signal is emitted, the dock widget has been removed from the
501      * docking system but it is not deleted yet.
502      */
503     void dockWidgetRemoved(DockWidget *dockWidget);
504 
505     /**
506      * This signal is emitted if the focused dock widget changed.
507      * Both old and now can be nullptr.
508      * The focused dock widget is the one that is highlighted in the GUI
509      */
510     void focusedDockWidgetChanged(DockWidget *old, DockWidget *now);
511 
512 public:
513     void showWorkspaceMananger();
514 
515     // higher level workspace management
516     QString activeWorkspace() const;
517     QString lastWorkspace() const;
518     bool autoRestorLastWorkspace() const;
519     QString workspaceFileExtension() const;
520     QStringList workspaces();
521     QSet<QString> workspacePresets() const;
522     QDateTime workspaceDateTime(const QString &workspace) const;
523     Utils::FilePath workspaceNameToFilePath(const QString &workspaceName) const;
524     QString fileNameToWorkspaceName(const QString &fileName) const;
525     QString workspaceNameToFileName(const QString &workspaceName) const;
526 
527     bool createWorkspace(const QString &workspace);
528 
529     bool openWorkspace(const QString &workspace);
530     bool reloadActiveWorkspace();
531 
532     bool confirmWorkspaceDelete(const QStringList &workspaces);
533     bool deleteWorkspace(const QString &workspace);
534     void deleteWorkspaces(const QStringList &workspaces);
535 
536     bool cloneWorkspace(const QString &original, const QString &clone);
537     bool renameWorkspace(const QString &original, const QString &newName);
538 
539     bool resetWorkspacePreset(const QString &workspace);
540 
541     bool save();
542 
543     bool isWorkspacePreset(const QString &workspace) const;
544 
545     void setModeChangeState(bool value);
546     bool isModeChangeState() const;
547 
548     void importWorkspace(const QString &workspace);
549     void exportWorkspace(const QString &target, const QString &workspace);
550 
551 signals:
552     void aboutToUnloadWorkspace(QString workspaceName);
553     void aboutToLoadWorkspace(QString workspaceName);
554     void workspaceLoaded(QString workspaceName);
555     void workspaceReloaded(QString workspaceName);
556     void aboutToSaveWorkspace();
557 
558 private:
559     bool write(const QString &workspace, const QByteArray &data, QString *errorString) const;
560     bool write(const QString &workspace, const QByteArray &data, QWidget *parent) const;
561 
562     QByteArray loadWorkspace(const QString &workspace) const;
563 
564     void syncWorkspacePresets();
565 
566     void saveStartupWorkspace();
567 }; // class DockManager
568 
569 } // namespace ADS
570