ResInsight/ApplicationLibCode/UserInterface/RiuMainWindowBase.cpp
Magne Sjaastad 8dfaae512f Do not block sub window activation by default
If blocking is set on by default, clicking on MDI windows does not select the corresponding item in project tree.
2022-11-04 15:53:54 +01:00

778 lines
28 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 "RiaVersionInfo.h"
#include "RiuDockWidgetTools.h"
#include "RiuDragDrop.h"
#include "RiuMdiArea.h"
#include "RiuMdiSubWindow.h"
#include "RimProject.h"
#include "RimViewWindow.h"
#include "cafCmdFeatureManager.h"
#include "cafPdmObject.h"
#include "cafPdmUiTreeView.h"
#include "cafQTreeViewStateSerializer.h"
#include "DockWidget.h"
#include <QAction>
#include <QClipboard>
#include <QInputDialog>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QMenu>
#include <QMessageBox>
#include <QSettings>
#include <QTreeView>
#include <QUndoStack>
#include <QUndoView>
#define DOCKSTATE_VERSION 2
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMainWindowBase::RiuMainWindowBase()
: m_allowActiveViewChangeFromSelection( true )
, m_showFirstVisibleWindowMaximized( true )
, m_blockSubWindowActivation( false )
, m_blockSubWindowProjectTreeSelection( false )
, m_windowMenu( nullptr )
, m_mdiArea( nullptr )
{
m_dockManager = new ads::CDockManager( this );
if ( RiaPreferences::current()->useUndoRedo() && RiaApplication::enableDevelopmentFeatures() )
{
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() ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMainWindowBase::~RiuMainWindowBase()
{
for ( auto v : m_projectTreeViews )
{
delete v;
}
m_projectTreeViews.clear();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
ads::CDockManager* RiuMainWindowBase::dockManager() const
{
return m_dockManager;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMdiArea* RiuMainWindowBase::mdiArea()
{
return m_mdiArea;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QMdiSubWindow* RiuMainWindowBase::createViewWindow()
{
RiuMdiSubWindow* subWin =
new RiuMdiSubWindow( nullptr, Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint | Qt::WindowMaximizeButtonHint );
subWin->setAttribute( Qt::WA_DeleteOnClose ); // Make sure the contained widget is destroyed when the MDI window
// is closed
return subWin;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimMdiWindowGeometry RiuMainWindowBase::windowGeometryForViewer( QWidget* viewer )
{
RiuMdiSubWindow* mdiWindow = dynamic_cast<RiuMdiSubWindow*>( findMdiSubWindow( viewer ) );
if ( mdiWindow )
{
return mdiWindow->windowGeometry();
}
RimMdiWindowGeometry geo;
return geo;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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() ) );
QVariant dockState = settings.value( QString( "%1/dockLayout" ).arg( registryFolderName() ) );
if ( winGeo.isValid() )
{
if ( restoreGeometry( winGeo.toByteArray() ) )
{
if ( toolbarLayout.isValid() )
{
restoreState( toolbarLayout.toByteArray(), 0 );
}
}
}
bool dockingOk = false;
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()
{
// Company and appname set through QCoreApplication
QSettings settings;
QByteArray winGeo = saveGeometry();
settings.setValue( QString( "%1/winGeometry" ).arg( registryFolderName() ), winGeo );
QByteArray layout = saveState( 0 );
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 );
ads::CDockWidget* dw = dynamic_cast<ads::CDockWidget*>( tv->parentWidget() );
if ( dw )
{
dw->show();
}
}
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::enableShowFirstVisibleMdiWindowMaximized( bool enable )
{
m_showFirstVisibleWindowMaximized = enable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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::removeViewerFromMdiArea( RiuMdiArea* mdiArea, QWidget* viewer )
{
bool removedSubWindowWasActive = false;
QMdiSubWindow* subWindowBeingClosed = findMdiSubWindow( viewer );
bool wasMaximized = subWindowBeingClosed->isMaximized();
if ( subWindowBeingClosed )
{
if ( subWindowBeingClosed->isActiveWindow() )
{
// If we are removing the active window, we will need a new active window
// Start by making the window inactive so Qt doesn't pick the active window itself
mdiArea->setActiveSubWindow( nullptr );
removedSubWindowWasActive = true;
}
mdiArea->removeSubWindow( subWindowBeingClosed );
// These two lines had to be introduced after themes was used
// Probably related to polish/unpolish of widgets in an MDI setting
// https://github.com/OPM/ResInsight/issues/6676
subWindowBeingClosed->hide();
subWindowBeingClosed->deleteLater();
}
QList<QMdiSubWindow*> subWindowList = mdiArea->subWindowList( QMdiArea::ActivationHistoryOrder );
if ( !subWindowList.empty() )
{
if ( removedSubWindowWasActive )
{
mdiArea->setActiveSubWindow( nullptr );
// Make the last activated window the current activated one
mdiArea->setActiveSubWindow( subWindowList.back() );
}
if ( wasMaximized && mdiArea->currentSubWindow() )
{
mdiArea->currentSubWindow()->showMaximized();
}
mdiArea->applyTiling();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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::initializeSubWindow( RiuMdiArea* mdiArea,
QMdiSubWindow* mdiSubWindow,
const QPoint& subWindowPos,
const QSize& subWindowSize )
{
bool initialStateMaximized = false;
auto initialState3dWindow = RimProject::current()->subWindowsTileMode3DWindow();
auto initialStatePlotWindow = RimProject::current()->subWindowsTileModePlotWindow();
if ( m_showFirstVisibleWindowMaximized && mdiArea->subWindowList().empty() )
{
// Show first 3D view maximized
initialStateMaximized = true;
}
if ( mdiArea->currentSubWindow() && mdiArea->currentSubWindow()->isMaximized() )
{
initialStateMaximized = true;
}
mdiArea->addSubWindow( mdiSubWindow );
if ( subWindowPos.x() > -1 )
{
mdiSubWindow->move( subWindowPos );
}
mdiSubWindow->resize( subWindowSize );
if ( initialStateMaximized )
{
mdiSubWindow->showMaximized();
}
else
{
mdiSubWindow->showNormal();
if ( !isBlockingSubWindowActivatedSignal() )
{
RimProject::current()->setSubWindowsTileMode3DWindow( initialState3dWindow );
RimProject::current()->setSubWindowsTileModePlotWindow( initialStatePlotWindow );
}
mdiArea->applyTiling();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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 )
{
CVF_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 );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmUiTreeView* RiuMainWindowBase::projectTreeView( int treeId )
{
CVF_ASSERT( treeId >= 0 );
CVF_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*>( this->sender() );
if ( action )
{
QString layoutName = action->text();
QByteArray state = RiuDockWidgetTools::defaultDockState( layoutName );
dockManager()->restoreState( state, DOCKSTATE_VERSION );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMainWindowBase::setDockLayout()
{
QAction* action = dynamic_cast<QAction*>( this->sender() );
if ( action )
{
QString layoutName = action->text();
dockManager()->openPerspective( layoutName );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMainWindowBase::deleteDockLayout()
{
QAction* action = dynamic_cast<QAction*>( this->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 )
{
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.size() > 0 ) 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.size() > 0 ) 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() ) );
}
}
#ifdef _DEBUG
QAction* exportLayoutAction = m_windowMenu->addAction( "Export Layout to Clipboard" );
connect( exportLayoutAction, SIGNAL( triggered() ), this, SLOT( exportDockLayout() ) );
#endif
m_windowMenu->addSeparator();
QAction* cascadeWindowsAction = new QAction( "Cascade Windows", this );
connect( cascadeWindowsAction, SIGNAL( triggered() ), m_mdiArea, SLOT( cascadeSubWindows() ) );
QAction* closeAllSubWindowsAction = new QAction( "Close All Windows", this );
connect( closeAllSubWindowsAction, SIGNAL( triggered() ), m_mdiArea, SLOT( closeAllSubWindows() ) );
caf::CmdFeatureManager* cmdFeatureMgr = caf::CmdFeatureManager::instance();
auto featureNames = windowsMenuFeatureNames();
for ( const auto& name : featureNames )
{
m_windowMenu->addAction( cmdFeatureMgr->action( name ) );
}
m_windowMenu->addAction( cascadeWindowsAction );
m_windowMenu->addAction( closeAllSubWindowsAction );
}