///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2016 Statoil ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RiuMainWindowBase.h" #include "RiaApplication.h" #include "RiaDefines.h" #include "RiaPreferences.h" #include "RiaPreferencesSystem.h" #include "RiaRegressionTestRunner.h" #include "RiaVersionInfo.h" #include "RimProject.h" #include "RimViewWindow.h" #include "RiuDockWidgetTools.h" #include "RiuDragDrop.h" #include "RiuGuiTheme.h" #include "RiuInterfaceToViewWindow.h" #include "RiuMainWindowTools.h" #include "RiuViewer.h" #include "cafCmdFeatureManager.h" #include "cafPdmObject.h" #include "cafPdmUiTreeView.h" #include "cafQTreeViewStateSerializer.h" #include "DockAreaTitleBar.h" #include "DockAreaWidget.h" #include "DockManager.h" #include "DockSplitter.h" #include "DockWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace internal { void rebalanceDockSplitters( RiuMainWindowBase* widget, std::set splitters ) { // rebalance splitters after a short delay to allow the layout to be updated before setting sizes QTimer::singleShot( 10, widget, [splitters]() { for ( QSplitter* splitter : splitters ) { QList sizes( splitter->count(), 1000 ); // size not important, just needs all to be equal splitter->setSizes( sizes ); } } ); } } // namespace internal //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuMainWindowBase::RiuMainWindowBase() : m_allowActiveViewChangeFromSelection( true ) , m_blockSubWindowActivation( false ) , m_blockSubWindowProjectTreeSelection( false ) , m_hasBeenVisible( false ) , m_isBeingDestroyed( false ) , m_hasBeenClosed( false ) , m_windowMenu( nullptr ) { ads::CDockManager::setAutoHideConfigFlags( ads::CDockManager::DefaultAutoHideConfig ); m_dockManager = new ads::CDockManager( this ); m_dockManager->setStyleSheet( "" ); if ( RiaPreferences::current()->useUndoRedo() && RiaPreferencesSystem::current()->isFeatureEnabled( "undo-redo-view" ) ) { m_undoView = new QUndoView( this ); } else { m_undoView = nullptr; } m_undoAction = new QAction( QIcon( ":/undo.png" ), tr( "Undo" ), this ); m_undoAction->setShortcut( QKeySequence::Undo ); connect( m_undoAction, SIGNAL( triggered() ), SLOT( slotUndo() ) ); m_redoAction = new QAction( QIcon( ":/redo.png" ), tr( "Redo" ), this ); m_redoAction->setShortcut( QKeySequence::Redo ); connect( m_redoAction, SIGNAL( triggered() ), SLOT( slotRedo() ) ); m_hideTabsAction = new QAction( QIcon( ":/HideTabs.svg" ), "Show/Hide Tabs", this ); m_hideTabsAction->setToolTip( "Show/Hide Tabs in Main Views" ); m_hideTabsAction->setCheckable( true ); connect( m_hideTabsAction, SIGNAL( toggled( bool ) ), SLOT( slotHideTabs( bool ) ) ); m_tileWindowsAction = new QAction( QIcon( ":/TileWindows.svg" ), "Tile Windows", this ); m_tileWindowsAction->setToolTip( "Tile All View Windows" ); connect( m_tileWindowsAction, SIGNAL( triggered() ), SLOT( tileViewWindows() ) ); m_tileHorizontallyAction = new QAction( QIcon( ":/TileHorizontally.svg" ), "Tile Windows Horizontally", this ); m_tileHorizontallyAction->setToolTip( "Tile All View Windows Horizontally" ); connect( m_tileHorizontallyAction, SIGNAL( triggered() ), SLOT( tileWindowsHorizontally() ) ); m_tileVerticallyAction = new QAction( QIcon( ":/TileVertically.svg" ), "Tile Windows Vertically", this ); m_tileVerticallyAction->setToolTip( "Tile All View Windows Vertically" ); connect( m_tileVerticallyAction, SIGNAL( triggered() ), SLOT( tileWindowsVertically() ) ); m_maximizeWindowsAction = new QAction( QIcon( ":/MaximizeWindows.svg" ), "Maximize Windows", this ); m_maximizeWindowsAction->setToolTip( "Maximize All View Windows" ); connect( m_maximizeWindowsAction, SIGNAL( triggered() ), SLOT( maximizeViewWindows() ) ); #ifdef Q_OS_WIN if ( RiaPreferences::current()->guiTheme() == RiaDefines::ThemeEnum::DARK ) { RiuMainWindowTools::setDarkTitleBarWindows( this ); } #endif } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuMainWindowBase::~RiuMainWindowBase() { for ( auto v : m_projectTreeViews ) { v->setPdmItem( nullptr ); delete v; } m_projectTreeViews.clear(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- ads::CDockManager* RiuMainWindowBase::dockManager() const { return m_dockManager; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RiuMainWindowBase::dockWidgetStateString() const { return QString::fromLatin1( m_dockManager->saveState( DOCKSTATE_VERSION ).toBase64() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMainWindowBase::restoreDockWidgetState( QString dockStateString ) { if ( dockStateString.isEmpty() ) return false; QByteArray dockState = QByteArray::fromBase64( dockStateString.toLatin1() ); return m_dockManager->restoreState( dockState, DOCKSTATE_VERSION ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMainWindowBase::restoreLastDockWidgetState() { if ( m_lastDockState.isEmpty() ) return false; return m_dockManager->restoreState( m_lastDockState, DOCKSTATE_VERSION ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setActiveViewer( QString viewerName ) { for ( auto view : viewWindows() ) { if ( !view->dockWidget() ) continue; auto dockName = view->dockWidget()->objectName(); if ( view->dockWidget() && view->dockWidget()->objectName() == viewerName ) { if ( !view->isActiveViewer() ) { view->setAsActiveViewer( !isBlockingViewSelectionOnSubWindowActivated() ); } if ( !view->dockWidget()->isCurrentTab() ) { view->dockWidget()->setAsCurrentTab(); } break; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::loadWinGeoAndDockToolBarLayout() { // Company and appname set through QCoreApplication QSettings settings; QVariant winGeo = settings.value( QString( "%1/winGeometry" ).arg( registryFolderName() ) ); QVariant toolbarLayout = settings.value( QString( "%1/toolBarLayout" ).arg( registryFolderName() ) ); if ( winGeo.isValid() ) { if ( restoreGeometry( winGeo.toByteArray() ) ) { if ( toolbarLayout.isValid() ) { restoreState( toolbarLayout.toByteArray(), 1 ); } } } if ( !RiaRegressionTestRunner::instance()->isRunningRegressionTests() ) { // Performance of m_dockManager->restoreState() is very bad is degrading as more and more regression tests are // launched. Disable restore of state for regression test. bool dockingOk = false; QVariant dockState = settings.value( QString( "%1/dockLayout" ).arg( registryFolderName() ) ); if ( dockState.isValid() ) { dockingOk = m_dockManager->restoreState( dockState.toByteArray(), DOCKSTATE_VERSION ); } if ( !dockingOk ) { m_dockManager->restoreState( RiuDockWidgetTools::defaultDockState( defaultDockStateNames()[0] ), DOCKSTATE_VERSION ); } } settings.beginGroup( registryFolderName() ); m_dockManager->loadPerspectives( settings ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString mainWindowDockWidgetSettingsKey( const QString& settingsFolderName ) { QString key = settingsFolderName + "/dockWindowVisibilies"; return key; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::saveWinGeoAndDockToolBarLayout() { // Do not save the state if the window never has been visible. This will write out a state that is not valid. // https://github.com/OPM/ResInsight/issues/9935 if ( !m_hasBeenVisible ) return; // The views were removed from docking when the window was closed, and the state was saved before that if ( m_hasBeenClosed ) return; // Company and appname set through QCoreApplication QSettings settings; QByteArray winGeo = saveGeometry(); settings.setValue( QString( "%1/winGeometry" ).arg( registryFolderName() ), winGeo ); QByteArray layout = saveState( 1 ); settings.setValue( QString( "%1/toolBarLayout" ).arg( registryFolderName() ), layout ); settings.setValue( QString( "%1/isMaximized" ).arg( registryFolderName() ), isMaximized() ); settings.setValue( QString( "%1/dockLayout" ).arg( registryFolderName() ), m_dockManager->saveState( DOCKSTATE_VERSION ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::showWindow() { // Company and appname set through QCoreApplication QSettings settings; showNormal(); QVariant isMax = settings.value( QString( "%1/isMaximized" ).arg( registryFolderName() ), false ); if ( isMax.toBool() ) { showMaximized(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RiuMainWindowBase::registryFolderName() { QString versionName( STRPRODUCTVER ); QString regFolder = QString( "%1_Qt%2/%3" ).arg( versionName ).arg( QT_VERSION_STR ).arg( mainWindowName() ); return regFolder; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::selectAsCurrentItem( const caf::PdmObject* object, bool allowActiveViewChange ) { m_allowActiveViewChangeFromSelection = allowActiveViewChange; auto tv = getTreeViewWithItem( object ); if ( tv ) { tv->selectAsCurrentItem( object ); if ( auto dw = m_dockManager->findDockWidget( tv->objectName() ) ) { dw->setAsCurrentTab(); } } m_allowActiveViewChangeFromSelection = true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::toggleItemInSelection( const caf::PdmObject* object, bool allowActiveViewChange ) { auto tv = getTreeViewWithItem( object ); if ( !tv ) return; m_allowActiveViewChangeFromSelection = allowActiveViewChange; std::vector currentSelection; tv->selectedUiItems( currentSelection ); std::vector updatedSelection; bool alreadySelected = false; for ( caf::PdmUiItem* uiItem : currentSelection ) { if ( object == uiItem ) { alreadySelected = true; } else { updatedSelection.push_back( uiItem ); } } if ( !alreadySelected ) { updatedSelection.push_back( object ); } tv->selectItems( updatedSelection ); m_allowActiveViewChangeFromSelection = true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setBlockSubWindowActivatedSignal( bool block ) { m_blockSubWindowActivation = block; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMainWindowBase::isBlockingSubWindowActivatedSignal() const { return m_blockSubWindowActivation; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setBlockViewSelectionOnSubWindowActivated( bool block ) { m_blockSubWindowProjectTreeSelection = block; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMainWindowBase::isBlockingViewSelectionOnSubWindowActivated() const { return m_blockSubWindowProjectTreeSelection; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setExpanded( const caf::PdmUiItem* uiItem, bool expanded ) { caf::PdmUiTreeView* tv = getTreeViewWithItem( uiItem ); if ( tv ) tv->setExpanded( uiItem, expanded ); } //-------------------------------------------------------------------------------------------------- /// /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotDockWidgetToggleViewActionTriggered() { if ( !sender() ) return; auto dockWidget = dynamic_cast( sender()->parent() ); if ( dockWidget ) { if ( dockWidget->isVisible() ) { // Raise the dock widget to make it visible if the widget is part of a tab widget dockWidget->raise(); } } } //-------------------------------------------------------------------------------------------------- /// /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotCentralWidgetContextMenu( const QPoint& pos ) { QMenu menu( this ); onCentralWidgetContextMenu( menu ); if ( !menu.isEmpty() && m_centralDockWidget->widget() != nullptr ) { menu.exec( m_centralDockWidget->widget()->mapToGlobal( pos ) ); } } //-------------------------------------------------------------------------------------------------- /// /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotDockViewerVisibilityChanged( bool visible ) { if ( !visible ) return; if ( auto dockWidget = dynamic_cast( sender() ) ) { for ( auto view : viewWindows() ) { if ( view->dockWidget() == dockWidget ) { view->setAsActiveViewer(); if ( !isBlockingViewSelectionOnSubWindowActivated() ) { selectAsCurrentItem( view ); } break; } } } } //-------------------------------------------------------------------------------------------------- /// /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotDockViewerClosed() { if ( auto dockWidget = dynamic_cast( sender() ) ) { auto mainWidget = dockWidget->widget(); RimViewWindow* viewWindow = RiuInterfaceToViewWindow::viewWindowFromWidget( mainWidget ); if ( !viewWindow ) { RiuViewer* viewer = mainWidget->findChild(); if ( viewer ) { viewWindow = viewer->ownerViewWindow(); } } if ( viewWindow ) { viewWindow->setShowWindow( false ); viewWindow->removeWindowFromDock(); viewWindow->updateConnectedEditors(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotRefreshHelpActions() { caf::CmdFeatureManager::instance()->action( "RicSearchHelpFeature" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotRedo() { if ( m_undoView && m_undoView->stack() && m_undoView->stack()->canRedo() ) { m_undoView->stack()->redo(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotUndo() { if ( m_undoView && m_undoView->stack() && m_undoView->stack()->canUndo() ) { m_undoView->stack()->undo(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotRefreshUndoRedoActions() { if ( !m_undoView ) return; m_redoAction->setDisabled( !m_undoView->stack()->canRedo() ); m_undoAction->setDisabled( !m_undoView->stack()->canUndo() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::createTreeViews( int numberOfTrees ) { CAF_ASSERT( m_projectTreeViews.empty() ); for ( int i = 0; i < numberOfTrees; i++ ) { auto tv = new caf::PdmUiTreeView(); caf::PdmUiDragDropInterface* dragDropInterface = new RiuDragDrop( tv ); tv->setDragDropInterface( dragDropInterface ); m_projectTreeViews.push_back( tv ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setUpCentralDockWidget() { m_centralDockWidget = RiuDockWidgetTools::createDockWidget( "", RiuDockWidgetTools::centralScreenName(), this ); QLabel* label = new QLabel(); label->setAutoFillBackground( true ); label->setStyleSheet( "QLabel { background-color: darkgrey; }" ); label->setContextMenuPolicy( Qt::CustomContextMenu ); connect( label, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( slotCentralWidgetContextMenu( const QPoint& ) ) ); m_centralDockWidget->setWidget( label ); m_centralDockWidget->setFeature( ads::CDockWidget::NoTab, true ); dockManager()->setCentralWidget( m_centralDockWidget ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmUiTreeView* RiuMainWindowBase::projectTreeView( int treeId ) { CAF_ASSERT( treeId >= 0 ); CAF_ASSERT( treeId < (int)m_projectTreeViews.size() ); return m_projectTreeViews[treeId]; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmUiTreeView* RiuMainWindowBase::getTreeViewWithItem( const caf::PdmUiItem* uiItem ) { for ( auto tv : m_projectTreeViews ) { QModelIndex qmi = tv->findModelIndex( uiItem ); if ( qmi.isValid() ) { return tv; } } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RiuMainWindowBase::projectTreeViews() { return m_projectTreeViews; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::restoreTreeViewStates( QString treeStateString, QString treeIndexeString ) { QStringList treeStates = treeStateString.split( RiaDefines::stringListSeparator() ); QStringList treeIndexes = treeIndexeString.split( RiaDefines::stringListSeparator() ); const int nTreeViews = (int)projectTreeViews().size(); if ( treeStates.size() < nTreeViews ) return; if ( treeIndexes.size() < nTreeViews ) return; for ( int treeId = 0; treeId < nTreeViews; treeId++ ) { auto tv = projectTreeView( treeId ); QString stateString = treeStates[treeId]; if ( !stateString.isEmpty() ) { tv->treeView()->collapseAll(); caf::QTreeViewStateSerializer::applyTreeViewStateFromString( tv->treeView(), stateString ); } QString currentIndexString = treeIndexes[treeId]; if ( !currentIndexString.isEmpty() ) { QModelIndex mi = caf::QTreeViewStateSerializer::getModelIndexFromString( tv->treeView()->model(), currentIndexString ); tv->treeView()->setCurrentIndex( mi ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- ads::CDockAreaWidget* RiuMainWindowBase::addTabbedWidgets( std::vector widgets, ads::DockWidgetArea whereToDock, ads::CDockAreaWidget* dockInside ) { ads::CDockAreaWidget* areaToDockIn = nullptr; for ( auto widget : widgets ) { if ( areaToDockIn ) { m_dockManager->addDockWidgetTabToArea( widget, areaToDockIn ); } else { areaToDockIn = m_dockManager->addDockWidget( whereToDock, widget, dockInside ); } } return areaToDockIn; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setDefaultDockLayout() { QAction* action = dynamic_cast( sender() ); if ( action ) { QString layoutName = action->text(); RiuDockWidgetTools::setDockLayout( this, layoutName ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setDockLayout() { QAction* action = dynamic_cast( sender() ); if ( action ) { QString layoutName = action->text(); RiuDockWidgetTools::setDockLayout( this, layoutName ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::deleteDockLayout() { QAction* action = dynamic_cast( sender() ); if ( action ) { QString name = action->text(); QString questionStr = "Are you sure you want to delete the window layout \"" + name + "\"?"; auto reply = QMessageBox::question( this, "Delete Window Layout", questionStr, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); if ( reply == QMessageBox::Yes ) { dockManager()->removePerspective( name ); QSettings settings; settings.beginGroup( registryFolderName() ); dockManager()->savePerspectives( settings ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::exportDockLayout() { QClipboard* clipboard = QApplication::clipboard(); if ( clipboard ) { QByteArray state = dockManager()->saveState( DOCKSTATE_VERSION ); QString exportStr; int i = 0; exportStr = "static const char stateData[] = {\n"; for ( unsigned char c : state ) { if ( i > 0 ) { if ( i % 25 == 0 ) exportStr += ",\n"; else exportStr += ", "; } exportStr += QString( "'\\x%1'" ).arg( c, 2, 16, QChar( '0' ) ); i++; } exportStr += "\n};\n"; clipboard->setText( exportStr ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::saveDockLayout() { bool ok = false; QString name = QInputDialog::getText( this, "Save Window Layout", "Give the window layout a name:", QLineEdit::Normal, "", &ok ); if ( ok && !name.isEmpty() ) { dockManager()->addPerspective( name ); QSettings settings; settings.beginGroup( registryFolderName() ); dockManager()->savePerspectives( settings ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::addDefaultEntriesToWindowsMenu() { QMenu* dockWindowsMenu = m_windowMenu->addMenu( QIcon( ":/window-management.svg" ), "Windows" ); auto dockMap = dockManager()->dockWidgetsMap(); auto keys = dockMap.keys(); keys.sort(); for ( auto& key : keys ) { if ( key == RiuDockWidgetTools::centralScreenName() ) continue; auto dock = dockMap[key]; dockWindowsMenu->addAction( dock->toggleViewAction() ); } m_windowMenu->addSeparator(); QAction* saveLayoutAction = m_windowMenu->addAction( "Save Window Layout..." ); connect( saveLayoutAction, SIGNAL( triggered() ), this, SLOT( saveDockLayout() ) ); QStringList defaultNames = defaultDockStateNames(); QStringList names = dockManager()->perspectiveNames(); if ( defaultNames.size() + names.size() > 0 ) { QMenu* layoutsMenu = m_windowMenu->addMenu( "Use Window Layout" ); QMenu* deleteLayoutMenu = nullptr; if ( !names.empty() ) deleteLayoutMenu = m_windowMenu->addMenu( "Delete Window Layout" ); for ( auto& defLayout : defaultNames ) { QAction* defLayoutAction = layoutsMenu->addAction( defLayout ); connect( defLayoutAction, SIGNAL( triggered() ), this, SLOT( setDefaultDockLayout() ) ); } if ( !defaultNames.empty() ) layoutsMenu->addSeparator(); for ( auto& layout : names ) { QAction* chooseLayoutAction = layoutsMenu->addAction( layout ); connect( chooseLayoutAction, SIGNAL( triggered() ), this, SLOT( setDockLayout() ) ); QAction* deleteLayoutAction = deleteLayoutMenu->addAction( layout ); connect( deleteLayoutAction, SIGNAL( triggered() ), this, SLOT( deleteDockLayout() ) ); } } if ( RiaApplication::enableDevelopmentFeatures() ) { QAction* exportLayoutAction = m_windowMenu->addAction( "Export Layout to Clipboard" ); connect( exportLayoutAction, SIGNAL( triggered() ), this, SLOT( exportDockLayout() ) ); } m_windowMenu->addSeparator(); m_windowMenu->addAction( m_tileWindowsAction ); m_windowMenu->addAction( m_tileVerticallyAction ); m_windowMenu->addAction( m_tileHorizontallyAction ); m_windowMenu->addAction( m_maximizeWindowsAction ); } //-------------------------------------------------------------------------------------------------- /// Called when the destructor of a main window starts. Used to avoid operations on a window being destroyed. //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::setBeingDestroyed() { m_isBeingDestroyed = true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMainWindowBase::isBeingDestroyed() const { return m_isBeingDestroyed; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::showEvent( QShowEvent* event ) { m_hasBeenVisible = true; QMainWindow::showEvent( event ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::closeEvent( QCloseEvent* event ) { saveWinGeoAndDockToolBarLayout(); for ( auto view : viewWindows() ) { view->removeWindowFromDock(); } m_hasBeenClosed = true; QMainWindow::closeEvent( event ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMainWindowBase::hasBeenClosed() const { return m_hasBeenClosed; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::clearClosedState() { m_hasBeenClosed = false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::slotHideTabs( bool hideTabs ) { std::set areasToToggle; std::set areasToNotToggle; for ( auto [name, widget] : dockManager()->dockWidgetsMap().asKeyValueRange() ) { // ignore the dummy central widget if ( name == RiuDockWidgetTools::centralScreenName() ) continue; // is the name a valid uuid, if so, it is a regular viewer QUuid dockId = QUuid( name ); if ( dockId.isNull() ) { // should not toggle tabs for other docked windows areasToNotToggle.insert( widget->dockAreaWidget() ); if ( areasToToggle.contains( widget->dockAreaWidget() ) ) { areasToToggle.erase( widget->dockAreaWidget() ); } } else { if ( !areasToNotToggle.contains( widget->dockAreaWidget() ) ) { areasToToggle.insert( widget->dockAreaWidget() ); } } } // toggle visibility for tab widgets in viewer-only areas for ( auto area : areasToToggle ) { if ( !area || !area->titleBar() ) continue; if ( hideTabs ) { area->titleBar()->hide(); } else { area->titleBar()->show(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RiuMainWindowBase::removeActiveViewWindows() { std::vector activeWindows; // remove all visible views from central dock area for ( auto view : viewWindows() ) { if ( !view->dockWidget() ) continue; if ( !view->showWindow() ) continue; activeWindows.push_back( view ); m_dockManager->removeDockWidget( view->dockWidget() ); } return activeWindows; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::tileViewWindows() { // predefined grid size map for tiling of windows: number of views -> (columns, rows) in grid, max 25 views static std::map> gridSizeMap = { { 1, { 1, 1 } }, { 2, { 2, 1 } }, { 3, { 3, 1 } }, { 4, { 2, 2 } }, { 5, { 3, 2 } }, { 6, { 3, 2 } }, { 7, { 3, 3 } }, { 8, { 3, 3 } }, { 9, { 3, 3 } }, { 10, { 4, 3 } }, { 11, { 4, 3 } }, { 12, { 4, 3 } }, { 13, { 4, 4 } }, { 14, { 4, 4 } }, { 15, { 4, 4 } }, { 16, { 4, 4 } }, { 17, { 5, 4 } }, { 18, { 5, 4 } }, { 19, { 5, 4 } }, { 20, { 5, 4 } }, { 21, { 5, 5 } }, { 22, { 5, 5 } }, { 23, { 5, 5 } }, { 24, { 5, 5 } }, { 25, { 5, 5 } } }; // stores the views to tile std::vector windowsToTile = removeActiveViewWindows(); // redock views in a grid layout const int nViews = (int)windowsToTile.size(); const int nCols = gridSizeMap[nViews].first; const int nRows = gridSizeMap[nViews].second; std::vector> areas( nRows, std::vector( nCols ) ); int viewIndex = 0; for ( int row = 0; row < nRows; row++ ) { for ( int col = 0; col < nCols; col++ ) { if ( viewIndex >= nViews ) break; if ( viewIndex >= 25 ) // limit to 25 views, see map above { windowsToTile[viewIndex++]->removeWindowFromDock(); continue; } auto view = windowsToTile[viewIndex++]; auto dock = view->dockWidget(); if ( row == 0 && col == 0 ) { areas[row][col] = m_dockManager->addDockWidget( ads::CenterDockWidgetArea, dock, m_dockManager->centralWidget()->dockAreaWidget() ); } else if ( row == 0 ) { areas[row][col] = m_dockManager->addDockWidget( ads::RightDockWidgetArea, dock, areas[row][col - 1] ); } else { areas[row][col] = m_dockManager->addDockWidget( ads::BottomDockWidgetArea, dock, areas[row - 1][col] ); } } } // build unique list of splitters used in the areas created std::set splitters; for ( auto arealist : areas ) { for ( auto area : arealist ) { if ( area && area->parentSplitter() ) { splitters.insert( area->parentSplitter() ); } } } if ( m_dockManager->centralWidget() && m_dockManager->centralWidget()->dockAreaWidget() ) { splitters.insert( m_dockManager->centralWidget()->dockAreaWidget()->parentSplitter() ); } internal::rebalanceDockSplitters( this, splitters ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::maximizeViewWindows() { // remove all active views from dock manager std::vector activeViews = removeActiveViewWindows(); // add all views to the center area for ( auto view : activeViews ) { m_dockManager->addDockWidget( ads::DockWidgetArea::CenterDockWidgetArea, view->dockWidget(), m_dockManager->centralWidget()->dockAreaWidget() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::tileWindowsHorizontally() { tileWindows( ads::RightDockWidgetArea ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::tileWindowsVertically() { tileWindows( ads::BottomDockWidgetArea ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMainWindowBase::tileWindows( ads::DockWidgetArea whereToDock ) { // stores the views to tile std::vector windowsToTile = removeActiveViewWindows(); const int nViews = (int)windowsToTile.size(); std::vector areas( nViews ); int viewIndex = 0; for ( int i = 0; i < nViews; i++ ) { auto view = windowsToTile[viewIndex++]; auto dock = view->dockWidget(); if ( i == 0 ) { areas[i] = m_dockManager->addDockWidget( ads::CenterDockWidgetArea, dock, m_dockManager->centralWidget()->dockAreaWidget() ); } else { areas[i] = m_dockManager->addDockWidget( whereToDock, dock, areas[i - 1] ); } } // build unique list of splitters used in the areas created std::set splitters; for ( auto area : areas ) { if ( area && area->parentSplitter() ) { splitters.insert( area->parentSplitter() ); } } if ( m_dockManager->centralWidget() && m_dockManager->centralWidget()->dockAreaWidget() ) { splitters.insert( m_dockManager->centralWidget()->dockAreaWidget()->parentSplitter() ); } internal::rebalanceDockSplitters( this, splitters ); }