Files
ResInsight/ApplicationLibCode/ProjectDataModel/RimDockWindowController.cpp
Magne Sjaastad 9ac6ad3bec #14903: Avoid duplicate dock window ids for copied plots
Copies and plots created from templates kept the source DockWindowId. The dock
manager identifies dock widgets by name, so the earlier dock widget was deleted on
dock state restore, leaving a dangling pointer that crashed in setActiveViewer.

- Do not copy DockWindowId; copies get a new id
- Assign a new id if a dock widget with the same name exists
- Use QPointer for the dock widget
2026-10-12 18:49:08 +02:00

253 lines
9.6 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor 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 "RimDockWindowController.h"
#include "RiaGuiApplication.h"
#include "Rim3dView.h"
#include "RimPlotWindow.h"
#include "RimProject.h"
#include "RimViewWindow.h"
#include "RiuDockWidgetTools.h"
#include "RiuMainWindow.h"
#include "RiuMainWindowBase.h"
#include "RiuPlotMainWindow.h"
#include "RiuPlotMainWindowTools.h"
#include "RiuViewer.h"
#include "DockManager.h"
#include "DockWidget.h"
// Keep "MdiWindowController" as the keyword written to the project file. Older versions of ResInsight depend on this
// keyword to create the window controller for plot windows (3D windows works with both keywords). The change to dock window was introduced
// in 2026.09. "DockWindowController" was used as the keyword name during development, and is kept for intermediate project files. Revert
// this ordering after 2-3 major releases, so the new statement becomes
// CAF_PDM_XML_SOURCE_INIT( RimDockWindowController, "DockWindowController", "MdiWindowController" );
CAF_PDM_XML_SOURCE_INIT( RimDockWindowController, "MdiWindowController", "DockWindowController" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimDockWindowController::RimDockWindowController()
{
CAF_PDM_InitField( &m_mainWindowID, "MainWindowID", RimDockWindowController::MAIN_WINDOW_ID_UNASSIGNED, "" );
CAF_PDM_InitFieldNoDefault( &m_viewToControl, "ViewToControl", "" );
m_viewToControl = nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimDockWindowController::~RimDockWindowController()
{
m_viewToControl = nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimDockWindowController::handleViewerDeletion()
{
if ( !viewPdmObject() ) return;
viewPdmObject()->m_showWindow = false;
viewPdmObject()->updateConnectedEditors();
viewPdmObject()->updateUiIconFromToggleField();
uiCapability()->updateUiIconFromToggleField();
removeWindowFromDock();
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimDockWindowController::removeWindowFromDock()
{
RiuMainWindowBase* mainWin = getMainWindow();
if ( mainWin && viewPdmObject() )
{
viewPdmObject()->setActive( false );
viewPdmObject()->deleteDockWidget();
viewPdmObject()->deleteViewWidget();
mainWin->onViewerRemoved();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimViewWindow* RimDockWindowController::viewPdmObject()
{
return m_viewToControl;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* RimDockWindowController::viewWidget()
{
if ( !viewPdmObject() ) return nullptr;
return viewPdmObject()->viewWidget();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMainWindowBase* RimDockWindowController::getMainWindow()
{
if ( RiaGuiApplication::isRunning() )
{
return RiaGuiApplication::instance()->mainWindowByID( m_mainWindowID );
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimDockWindowController::updateViewerWidget()
{
RiuMainWindowBase* mainWindow = getMainWindow();
if ( !mainWindow ) return;
if ( !viewPdmObject() ) return;
if ( viewPdmObject()->isWindowVisible() )
{
if ( !viewWidget() )
{
// Create the view widget first: if it fails, nothing has been allocated yet. Creating the
// dock widget first and having view widget creation fail afterwards would otherwise leave a
// dock widget without a dock manager, which deleteDockWidget() cannot clean up.
QWidget* viewWidget = viewPdmObject()->createViewWidget();
if ( !viewWidget ) return;
ads::CDockWidget* dockWidget = viewPdmObject()->createDockWidget();
if ( !dockWidget )
{
viewPdmObject()->deleteViewWidget();
return;
}
// The dock manager identifies dock widgets by name. A duplicate name (e.g. from a project saved with
// copied views) would unregister the existing dock widget, which is then deleted on next dock state restore.
if ( auto existing = mainWindow->dockManager()->findDockWidget( viewPdmObject()->dockWindowName() );
existing && existing != dockWidget )
{
viewPdmObject()->resetDockWindowId();
}
dockWidget->setWidget( viewWidget );
dockWidget->setObjectName( viewPdmObject()->dockWindowName() );
viewWidget->setObjectName( viewPdmObject()->dockWindowName() );
mainWindow->initializeViewer( dockWidget, viewWidget );
RiuMainWindowBase::connect( dockWidget, SIGNAL( closed() ), mainWindow, SLOT( slotDockViewerClosed() ) );
RiuMainWindowBase::connect( dockWidget,
SIGNAL( visibilityChanged( bool ) ),
mainWindow,
SLOT( slotDockViewerVisibilityChanged( bool ) ) );
viewPdmObject()->updateViewWidgetAfterCreation();
}
viewPdmObject()->updateWindowTitle();
}
else
{
viewPdmObject()->deleteDockWidget();
if ( viewWidget() )
{
viewPdmObject()->deleteViewWidget();
mainWindow->onViewerRemoved();
setViewToControl( nullptr );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimDockWindowController::setMainWindowId( int mainId )
{
m_mainWindowID = mainId;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimDockWindowController::setViewToControl( RimViewWindow* view )
{
m_viewToControl = view;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimDockWindowController::mainWindowId() const
{
return m_mainWindowID();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimDockWindowController::setAsActiveViewer( bool allowSelectionChange )
{
if ( getMainWindow() == nullptr ) return;
if ( auto pdmView = viewPdmObject() )
{
auto view3d = dynamic_cast<Rim3dView*>( pdmView );
auto viewPlot = dynamic_cast<RimPlotWindow*>( pdmView );
RiuMainWindowBase* mainWindow = RiaGuiApplication::instance()->mainWindow();
if ( viewPlot ) mainWindow = RiaGuiApplication::instance()->mainPlotWindow();
if ( !mainWindow ) return;
for ( auto view : mainWindow->viewWindows() )
{
if ( view->isActiveViewer() )
{
view->setActive( false );
view->updateWindowTitle();
}
}
pdmView->setActive( true );
pdmView->updateWindowTitle();
if ( view3d )
{
RiaApplication::instance()->setActiveReservoirView( view3d );
if ( auto mainWin = dynamic_cast<RiuMainWindow*>( mainWindow ) )
{
mainWin->refreshViewActions();
mainWin->refreshAnimationActions();
mainWin->refreshDrawStyleActions();
}
}
else if ( viewPlot )
{
if ( allowSelectionChange ) RiuPlotMainWindowTools::selectAsCurrentItem( viewPlot );
RiuPlotMainWindowTools::refreshToolbars();
}
}
}