mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
WA_DeleteOnClose left RiuMainWindow::instance() and RiuPlotMainWindow::instance() null after the user closed a window, so any unguarded use crashed. Keep both windows alive for the application lifetime and rebuild their views when a closed window is shown again. Fixes #14899.
1116 lines
40 KiB
C++
1116 lines
40 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 <http://www.gnu.org/licenses/gpl.html>
|
|
// 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 <QAction>
|
|
#include <QApplication>
|
|
#include <QClipboard>
|
|
#include <QInputDialog>
|
|
#include <QLabel>
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
#include <QSettings>
|
|
#include <QTextEdit>
|
|
#include <QTimer>
|
|
#include <QTreeView>
|
|
#include <QUndoStack>
|
|
#include <QUndoView>
|
|
#include <QUuid>
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
namespace internal
|
|
{
|
|
void rebalanceDockSplitters( RiuMainWindowBase* widget, std::set<ads::CDockSplitter*> 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<int> 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<caf::PdmUiItem*> currentSelection;
|
|
tv->selectedUiItems( currentSelection );
|
|
std::vector<const caf::PdmUiItem*> 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<ads::CDockWidget*>( 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<ads::CDockWidget*>( sender() ) )
|
|
{
|
|
for ( auto view : viewWindows() )
|
|
{
|
|
if ( view->dockWidget() == dockWidget )
|
|
{
|
|
view->setAsActiveViewer();
|
|
if ( !isBlockingViewSelectionOnSubWindowActivated() )
|
|
{
|
|
selectAsCurrentItem( view );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiuMainWindowBase::slotDockViewerClosed()
|
|
{
|
|
if ( auto dockWidget = dynamic_cast<ads::CDockWidget*>( sender() ) )
|
|
{
|
|
auto mainWidget = dockWidget->widget();
|
|
RimViewWindow* viewWindow = RiuInterfaceToViewWindow::viewWindowFromWidget( mainWidget );
|
|
if ( !viewWindow )
|
|
{
|
|
RiuViewer* viewer = mainWidget->findChild<RiuViewer*>();
|
|
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<caf::PdmUiTreeView*> 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<ads::CDockWidget*> 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<QAction*>( sender() );
|
|
if ( action )
|
|
{
|
|
QString layoutName = action->text();
|
|
RiuDockWidgetTools::setDockLayout( this, layoutName );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiuMainWindowBase::setDockLayout()
|
|
{
|
|
QAction* action = dynamic_cast<QAction*>( sender() );
|
|
if ( action )
|
|
{
|
|
QString layoutName = action->text();
|
|
RiuDockWidgetTools::setDockLayout( this, layoutName );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiuMainWindowBase::deleteDockLayout()
|
|
{
|
|
QAction* action = dynamic_cast<QAction*>( 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<ads::CDockAreaWidget*> areasToToggle;
|
|
std::set<ads::CDockAreaWidget*> 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<RimViewWindow*> RiuMainWindowBase::removeActiveViewWindows()
|
|
{
|
|
std::vector<RimViewWindow*> 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<int, std::pair<int, int>> 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<RimViewWindow*> 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<std::vector<ads::CDockAreaWidget*>> areas( nRows, std::vector<ads::CDockAreaWidget*>( 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<ads::CDockSplitter*> 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<RimViewWindow*> 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<RimViewWindow*> windowsToTile = removeActiveViewWindows();
|
|
|
|
const int nViews = (int)windowsToTile.size();
|
|
|
|
std::vector<ads::CDockAreaWidget*> 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<ads::CDockSplitter*> 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 );
|
|
}
|