///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2019- 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RiuMultiPlotBook.h" #include "RiaGuiApplication.h" #include "RiaPlotDefines.h" #include "RiaPlotWindowRedrawScheduler.h" #include "RiaPreferences.h" #include "RimContextCommandBuilder.h" #include "RimMultiPlot.h" #include "RimSummaryMultiPlot.h" #include "RimWellLogTrack.h" #include "RiuDragDrop.h" #include "RiuMainWindow.h" #include "RiuMultiPlotPage.h" #include "RiuPlotMainWindow.h" #include "RiuPlotObjectPicker.h" #include "RiuPlotWidget.h" #include "RiuSummaryMultiPlotPage.h" #include "cafCmdFeatureMenuBuilder.h" #include "cafSelectionManager.h" #include "cvfAssert.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class BookFrame : public QFrame { public: BookFrame( int margins, QWidget* parent = nullptr ) : QFrame( parent ) , m_margins( margins ) { } QSize calculateSize( int width ) const { int pageWidth = width - 2 * m_margins; QList pages = this->findChildren(); QSize fullSize( 0, 0 ); for ( auto page : pages ) { fullSize.setWidth( pageWidth ); fullSize.setHeight( fullSize.height() + m_margins + page->heightForWidth( pageWidth ) ); } QSize minSize = minimumSizeHint(); return QSize( std::max( minSize.width(), fullSize.width() ), std::max( minSize.height(), fullSize.height() ) ); } protected: QSize minimumSizeHint() const override { QList pages = this->findChildren(); QSize fullSize( 0, 0 ); for ( auto page : pages ) { fullSize.setWidth( std::max( fullSize.width(), page->minimumSizeHint().width() ) ); fullSize.setHeight( fullSize.height() + m_margins + page->minimumSizeHint().height() ); } return fullSize; } private: int m_margins; }; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuMultiPlotBook::RiuMultiPlotBook( RimMultiPlot* plotDefinition, QWidget* parent ) : QWidget( parent ) , m_plotDefinition( plotDefinition ) , m_plotTitle( "Multi Plot" ) , m_titleVisible( true ) , m_subTitlesVisible( true ) , m_previewMode( true ) , m_currentPageIndex( 0 ) , m_goToPageAfterUpdate( false ) , m_pageTimerId( -1 ) , m_pageToGoTo( 0 ) { const int spacing = 8; this->setContentsMargins( 0, 0, 0, 0 ); m_layout = new QVBoxLayout( this ); m_layout->setContentsMargins( 0, 0, 0, 0 ); m_scrollArea = new QScrollArea( this ); m_scrollArea->setFrameStyle( QFrame::NoFrame ); m_scrollArea->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_scrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); m_layout->addWidget( m_scrollArea ); m_book = new BookFrame( spacing ); m_book->setFrameStyle( QFrame::NoFrame ); m_scrollArea->setWidget( m_book ); m_scrollArea->setWidgetResizable( true ); m_bookLayout = new QVBoxLayout( m_book ); m_bookLayout->setSpacing( spacing ); m_scrollArea->setVisible( true ); m_book->setVisible( true ); m_scrollArea->viewport()->installEventFilter( this ); m_scrollArea->verticalScrollBar()->installEventFilter( this ); setAutoFillBackground( true ); this->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::MinimumExpanding ); setFocusPolicy( Qt::StrongFocus ); setAcceptDrops( true ); QSize pageSize = m_plotDefinition->pageLayout().fullRectPixels( RiaGuiApplication::applicationResolution() ).size(); applyPagePreviewBookSize( pageSize.width() ); this->setObjectName( QString( "%1" ).arg( reinterpret_cast( this ) ) ); applyLook(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuMultiPlotBook::~RiuMultiPlotBook() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimViewWindow* RiuMultiPlotBook::ownerViewWindow() const { return m_plotDefinition; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::addPlot( RiuPlotWidget* plotWidget ) { // Push the plot to the back of the list insertPlot( plotWidget, m_plotWidgets.size() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::insertPlot( RiuPlotWidget* plotWidget, size_t index ) { m_plotWidgets.insert( static_cast( index ), plotWidget ); m_goToPageAfterUpdate = true; m_pageToGoTo = -2; scheduleUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::removePlot( RiuPlotWidget* plotWidget ) { removePlotNoUpdate( plotWidget ); scheduleUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::removePlotNoUpdate( RiuPlotWidget* plotWidget ) { if ( !plotWidget ) return; int plotWidgetIdx = m_plotWidgets.indexOf( plotWidget ); CVF_ASSERT( plotWidgetIdx >= 0 ); m_plotWidgets.removeAt( plotWidgetIdx ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::removeAllPlots() { deleteAllPages(); m_plotWidgets.clear(); scheduleUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::setPlotTitle( const QString& plotTitle ) { m_plotTitle = plotTitle; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::setTitleVisible( bool visible ) { m_titleVisible = visible; for ( auto page : m_pages ) { page->setTitleVisible( visible ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::setSubTitlesVisible( bool visible ) { m_subTitlesVisible = visible; for ( auto page : m_pages ) { page->setSubTitlesVisible( visible ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::scheduleTitleUpdate() { for ( auto page : m_pages ) { page->scheduleUpdate( RiaDefines::MultiPlotPageUpdateType::TITLE ); } scheduleUpdate( RiaDefines::MultiPlotPageUpdateType::TITLE ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::setTitleFontSizes( int titleFontSize, int subTitleFontSize ) { for ( auto page : m_pages ) { page->setTitleFontSizes( titleFontSize, subTitleFontSize ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::setLegendFontSize( int legendFontSize ) { for ( auto page : m_pages ) { page->setLegendFontSize( legendFontSize ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::setAxisFontSizes( int axisTitleFontSize, int axisValueFontSize ) { for ( auto page : m_pages ) { page->setAxisFontSizes( axisTitleFontSize, axisValueFontSize ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RiuMultiPlotBook::indexOfPlotWidget( RiuPlotWidget* plotWidget ) { return m_plotWidgets.indexOf( plotWidget ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMultiPlotBook::pagePreviewModeEnabled() const { return m_previewMode; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::setPagePreviewModeEnabled( bool previewMode ) { m_previewMode = previewMode; for ( auto page : m_pages ) { page->setPagePreviewModeEnabled( pagePreviewModeEnabled() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::scheduleUpdate( RiaDefines::MultiPlotPageUpdateType whatToUpdate ) { RiaPlotWindowRedrawScheduler::instance()->scheduleMultiPlotBookUpdate( this, whatToUpdate ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::scheduleReplotOfAllPlots() { for ( RiuPlotWidget* plotWidget : visiblePlotWidgets() ) { plotWidget->scheduleReplot(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::renderTo( QPaintDevice* paintDevice ) { auto scaling = RiaDefines::scalingFactor( paintDevice ); QPainter painter( paintDevice ); auto pagedDevice = dynamic_cast( paintDevice ); if ( pagedDevice ) { bool firstPage = true; for ( RiuMultiPlotPage* page : m_pages ) { if ( !firstPage ) { pagedDevice->newPage(); } page->renderTo( &painter, scaling ); firstPage = false; } } else { if ( m_currentPageIndex < m_pages.size() ) { auto page = m_pages[m_currentPageIndex]; page->renderTo( &painter, scaling ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::contextMenuEvent( QContextMenuEvent* event ) { QMenu menu; caf::CmdFeatureMenuBuilder menuBuilder; caf::SelectionManager::instance()->setSelectedItem( ownerViewWindow() ); menuBuilder << "RicShowPlotDataFeature"; menuBuilder << "RicShowContributingWellsFromPlotFeature"; menuBuilder.appendToMenu( &menu ); if ( menu.actions().size() > 0 ) { menu.exec( event->globalPos() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::showEvent( QShowEvent* event ) { m_goToPageAfterUpdate = true; QWidget::showEvent( event ); if ( m_previewMode ) { applyPagePreviewBookSize( width() ); } else { applyBookSize( width(), height() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::hideEvent( QHideEvent* event ) { m_pageToGoTo = m_currentPageIndex; QWidget::hideEvent( event ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::resizeEvent( QResizeEvent* event ) { if ( m_previewMode ) { applyPagePreviewBookSize( event->size().width() ); } else { applyBookSize( event->size().width(), event->size().height() ); } QWidget::resizeEvent( event ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::applyPagePreviewBookSize( int frameWidth ) { for ( auto page : m_pages ) { int width = page->width(); int heightForWidth = page->heightForWidth( width ); page->resize( width, heightForWidth ); } m_book->setFixedSize( m_book->calculateSize( frameWidth ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::applyBookSize( int frameWidth, int frameHeight ) { int totalHeight = 0; for ( auto page : m_pages ) { page->resize( frameWidth, frameHeight ); totalHeight += frameHeight; } m_book->setFixedSize( QSize( frameWidth, totalHeight ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::pair RiuMultiPlotBook::rowAndColumnCount( int plotWidgetCount ) const { if ( plotWidgetCount == 0 ) { return std::make_pair( 0, 0 ); } int columnCount = std::max( 1, m_plotDefinition->columnCount() ); int rowCount = static_cast( std::ceil( plotWidgetCount / static_cast( columnCount ) ) ); return std::make_pair( rowCount, columnCount ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMultiPlotBook::showYAxis( int row, int column ) const { return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::performUpdate( RiaDefines::MultiPlotPageUpdateType whatToUpdate ) { if ( !m_plotDefinition || !m_plotDefinition->isValid() || !m_plotDefinition->showWindow() ) return; applyLook(); if ( ( ( whatToUpdate & RiaDefines::MultiPlotPageUpdateType::PLOT ) == RiaDefines::MultiPlotPageUpdateType::PLOT ) || m_pages.size() == 0 ) { deleteAllPages(); createPages(); } else if ( ( whatToUpdate & RiaDefines::MultiPlotPageUpdateType::TITLE ) == RiaDefines::MultiPlotPageUpdateType::TITLE ) { updatePageTitles(); } updateGeometry(); RimSummaryMultiPlot* multiPlot = dynamic_cast( m_plotDefinition.p() ); if ( multiPlot ) multiPlot->analyzePlotsAndAdjustAppearanceSettings(); // use a timer to trigger a viewer page change, if needed if ( m_goToPageAfterUpdate ) { m_pageTimerId = startTimer( 50 ); m_goToPageAfterUpdate = false; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::updatePageTitles() { if ( m_pages.isEmpty() ) return; if ( m_pages.size() > 1 ) { for ( int i = 0; i < m_pages.size(); ++i ) { int pageNumber = i + 1; m_pages[i]->setPlotTitle( QString( "%1 %2/%3" ).arg( m_plotTitle ).arg( pageNumber ).arg( m_pages.size() ) ); } } else { m_pages[0]->setPlotTitle( QString( "%1" ).arg( m_plotTitle ) ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::forcePerformUpdate() { performUpdate( RiaDefines::MultiPlotPageUpdateType::ALL ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::timerEvent( QTimerEvent* event ) { if ( event->timerId() == m_pageTimerId ) { killTimer( m_pageTimerId ); changeCurrentPage( m_pageToGoTo ); RiaGuiApplication::instance()->mainPlotWindow()->raise(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList> RiuMultiPlotBook::visiblePlotWidgets() const { QList> plotWidgets; for ( QPointer plotWidget : m_plotWidgets ) { CAF_ASSERT( plotWidget ); if ( plotWidget->isChecked() ) { plotWidgets.push_back( plotWidget ); } } return plotWidgets; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::deleteAllPages() { for ( RiuMultiPlotPage* page : m_pages ) { m_bookLayout->removeWidget( page ); page->removeAllPlots(); delete page; } m_pages.clear(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::createPages() { CAF_ASSERT( m_plotDefinition ); QList> plotWidgets = this->visiblePlotWidgets(); auto [rowCount, columnCount] = this->rowAndColumnCount( plotWidgets.size() ); int rowsPerPage = m_plotDefinition->rowsPerPage(); int row = 0; int column = 0; // Make sure we always add a page. For empty multi-plots we'll have an empty page with a drop target. RiuMultiPlotPage* page = createPage(); for ( int visibleIndex = 0; visibleIndex < plotWidgets.size(); ++visibleIndex ) { int expectedColSpan = static_cast( plotWidgets[visibleIndex]->colSpan() ); int colSpan = std::min( expectedColSpan, columnCount ); std::tie( row, column ) = page->findAvailableRowAndColumn( row, column, colSpan, columnCount ); if ( row >= rowsPerPage ) { page = createPage(); row = 0; column = 0; } CAF_ASSERT( plotWidgets[visibleIndex] ); page->addPlot( plotWidgets[visibleIndex] ); page->performUpdate( RiaDefines::MultiPlotPageUpdateType::ALL ); } // Set page numbers in title when there's more than one page updatePageTitles(); adjustBookFrame(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const QList>& RiuMultiPlotBook::pages() const { return m_pages; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuMultiPlotPage* RiuMultiPlotBook::createPage() { RiuMultiPlotPage* page = new RiuMultiPlotPage( m_plotDefinition, this ); applyPageSettings( page ); m_pages.push_back( page ); m_bookLayout->addWidget( page ); page->setVisible( true ); return page; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::applyPageSettings( RiuMultiPlotPage* page ) { if ( !page ) return; page->setPlotTitle( m_plotTitle ); page->setTitleFontSizes( m_plotDefinition->titleFontSize(), m_plotDefinition->subTitleFontSize() ); page->setLegendFontSize( m_plotDefinition->legendFontSize() ); page->setAxisFontSizes( m_plotDefinition->axisTitleFontSize(), m_plotDefinition->axisValueFontSize() ); page->setTitleVisible( m_titleVisible ); page->setSubTitlesVisible( m_subTitlesVisible ); page->setPagePreviewModeEnabled( m_previewMode ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::applyLook() { if ( m_previewMode ) { this->setBackgroundRole( QPalette::Dark ); m_book->setBackgroundRole( QPalette::Dark ); } else { QPalette newPalette( palette() ); newPalette.setColor( QPalette::Window, Qt::white ); setPalette( newPalette ); this->setBackgroundRole( QPalette::Window ); m_book->setBackgroundRole( QPalette::Window ); m_book->setPalette( newPalette ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::changeCurrentPage( int pageNumber ) { if ( pageNumber < -1 ) pageNumber = m_pages.size() - 1; m_currentPageIndex = pageNumber; if ( m_currentPageIndex >= m_pages.size() ) m_currentPageIndex = m_pages.size() - 1; if ( m_currentPageIndex < 0 ) m_currentPageIndex = 0; if ( !m_pages.isEmpty() ) m_scrollArea->ensureWidgetVisible( m_pages[m_currentPageIndex] ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::scrollToPlot( RiuPlotWidget* plotWidget ) { int pageNum = 0; for ( auto& page : m_pages ) { if ( page->indexOfPlotWidget( plotWidget ) >= 0 ) { changeCurrentPage( pageNum ); return; } pageNum++; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::goToNextPage() { changeCurrentPage( m_currentPageIndex + 1 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::goToPrevPage() { changeCurrentPage( m_currentPageIndex - 1 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::goToLastPage() { changeCurrentPage( m_pages.size() - 1 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::keepCurrentPageAfterUpdate() { m_goToPageAfterUpdate = true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::dragEnterEvent( QDragEnterEvent* event ) { event->acceptProposedAction(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::dropEvent( QDropEvent* event ) { std::vector objects; if ( RiuDragDrop::handleGenericDropEvent( event, objects ) ) { RimSummaryMultiPlot* multiPlot = dynamic_cast( m_plotDefinition.p() ); if ( multiPlot ) { multiPlot->handleDroppedObjects( objects ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuMultiPlotBook::eventFilter( QObject* obj, QEvent* event ) { if ( event->type() == QEvent::Wheel ) { event->accept(); return true; } return QWidget::eventFilter( obj, event ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuMultiPlotBook::adjustBookFrame() { m_book->adjustSize(); }