///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2015- Statoil ASA // Copyright (C) 2015- Ceetron Solutions AS // // 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 "RiuGridPlotWindow.h" #include "RiaApplication.h" #include "RiaPlotWindowRedrawScheduler.h" #include "RiaPreferences.h" #include "WellLogCommands/RicWellLogPlotTrackFeatureImpl.h" #include "RimContextCommandBuilder.h" #include "RimGridPlotWindow.h" #include "RimWellLogTrack.h" #include "RiuMainWindow.h" #include "RiuPlotMainWindow.h" #include "RiuPlotObjectPicker.h" #include "RiuQwtPlotLegend.h" #include "RiuQwtPlotWidget.h" #include "cafCmdFeatureMenuBuilder.h" #include "cafSelectionManager.h" #include "cvfAssert.h" #include "qwt_legend.h" #include "qwt_plot_layout.h" #include "qwt_scale_draw.h" #include #include #include #include #include #include #include #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuGridPlotWindow::RiuGridPlotWindow( RimGridPlotWindow* plotDefinition, QWidget* parent ) : QWidget( parent ) { Q_ASSERT( plotDefinition ); m_plotDefinition = plotDefinition; m_layout = new QVBoxLayout( this ); m_layout->setMargin( 0 ); m_layout->setSpacing( 2 ); m_plotTitle = createTitleLabel(); m_layout->addWidget( m_plotTitle ); m_plotLayout = new QHBoxLayout; m_layout->addLayout( m_plotLayout ); m_plotWidgetFrame = new QFrame; m_plotWidgetFrame->setVisible( true ); m_plotLayout->addWidget( m_plotWidgetFrame, 1 ); m_gridLayout = new QGridLayout( m_plotWidgetFrame ); m_gridLayout->setMargin( 0 ); m_gridLayout->setSpacing( 2 ); QPalette newPalette( palette() ); newPalette.setColor( QPalette::Background, Qt::white ); setPalette( newPalette ); setAutoFillBackground( true ); m_scrollBar = new QScrollBar( nullptr ); m_scrollBar->setOrientation( Qt::Vertical ); m_scrollBar->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); m_scrollBarLayout = new QVBoxLayout; m_scrollBarLayout->addWidget( m_scrollBar, 0 ); new RiuPlotObjectPicker( m_plotTitle, m_plotDefinition ); this->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::MinimumExpanding ); setFocusPolicy( Qt::StrongFocus ); setAcceptDrops( true ); RiaApplication* app = RiaApplication::instance(); int defaultFontSize = RiaFontCache::pointSizeFromFontSizeEnum( app->preferences()->defaultPlotFontSize() ); setFontSize( defaultFontSize ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuGridPlotWindow::~RiuGridPlotWindow() { if ( m_plotDefinition ) { m_plotDefinition->detachAllCurves(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimGridPlotWindow* RiuGridPlotWindow::ownerPlotDefinition() { return m_plotDefinition; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimViewWindow* RiuGridPlotWindow::ownerViewWindow() const { return m_plotDefinition; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::addPlot( RiuQwtPlotWidget* plotWidget ) { // Insert the plot to the left of the scroll bar insertPlot( plotWidget, m_plotWidgets.size() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::insertPlot( RiuQwtPlotWidget* plotWidget, size_t index ) { plotWidget->setDraggable( true ); // Becomes draggable when added to a grid plot window m_plotWidgets.insert( static_cast( index ), plotWidget ); RiuQwtPlotLegend* legend = new RiuQwtPlotLegend( this ); int legendColumns = 1; if ( m_plotDefinition->legendsHorizontal() ) { legendColumns = 0; // unlimited } legend->setMaxColumns( legendColumns ); legend->horizontalScrollBar()->setVisible( false ); legend->verticalScrollBar()->setVisible( false ); legend->connect( plotWidget, SIGNAL( legendDataChanged( const QVariant&, const QList& ) ), SLOT( updateLegend( const QVariant&, const QList& ) ) ); legend->contentsWidget()->layout()->setAlignment( Qt::AlignBottom | Qt::AlignHCenter ); plotWidget->updateLegend(); m_legends.insert( static_cast( index ), legend ); m_legendColumns.insert( static_cast( index ), -1 ); scheduleUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::removePlot( RiuQwtPlotWidget* plotWidget ) { if ( !plotWidget ) return; int plotWidgetIdx = m_plotWidgets.indexOf( plotWidget ); CVF_ASSERT( plotWidgetIdx >= 0 ); m_plotWidgets.removeAt( plotWidgetIdx ); plotWidget->setParent( nullptr ); RiuQwtPlotLegend* legend = m_legends[plotWidgetIdx]; m_legends.removeAt( plotWidgetIdx ); m_legendColumns.removeAt( plotWidgetIdx ); delete legend; scheduleUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::setPlotTitle( const QString& plotTitle ) { m_plotTitle->setText( plotTitle ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RiuGridPlotWindow::preferredWidth() const { int titleWidth = 0; if ( m_plotTitle && m_plotTitle->isVisible() ) { titleWidth = m_plotTitle->width(); } QList> visiblePlotWidgets = this->visiblePlotWidgets(); auto rowAndColumnCount = this->rowAndColumnCount( visiblePlotWidgets.size() ); int sumColumnWidths = 0; for ( int visibleIndex = 0; visibleIndex < rowAndColumnCount.second; ++visibleIndex ) { sumColumnWidths += visiblePlotWidgets[visibleIndex]->width(); } return std::max( titleWidth, sumColumnWidths ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::setTitleVisible( bool visible ) { m_plotTitle->setVisible( visible ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::setScrollbarVisible( bool visible ) { m_scrollBar->setVisible( visible ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::setSelectionsVisible( bool visible ) { for ( RiuQwtPlotWidget* plotWidget : m_plotWidgets ) { if ( visible && caf::SelectionManager::instance()->isSelected( plotWidget->plotOwner(), 0 ) ) { plotWidget->setWidgetState( RiuWidgetStyleSheet::SELECTED ); } else { plotWidget->setWidgetState( RiuWidgetStyleSheet::DEFAULT ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::setFontSize( int fontSize ) { QFont font = m_plotTitle->font(); font.setPointSize( fontSize + 1 ); font.setBold( true ); m_plotTitle->setFont( font ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RiuGridPlotWindow::fontSize() const { return m_plotTitle->font().pointSize() - 1; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RiuGridPlotWindow::indexOfPlotWidget( RiuQwtPlotWidget* plotWidget ) { return m_plotWidgets.indexOf( plotWidget ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::scheduleUpdate() { RiaPlotWindowRedrawScheduler::instance()->schedulePlotWindowUpdate( this ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::scheduleReplotOfAllPlots() { for ( RiuQwtPlotWidget* plotWidget : visiblePlotWidgets() ) { plotWidget->scheduleReplot(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::contextMenuEvent( QContextMenuEvent* event ) { QMenu menu; caf::CmdFeatureMenuBuilder menuBuilder; caf::SelectionManager::instance()->setSelectedItem( ownerPlotDefinition() ); menuBuilder << "RicShowPlotDataFeature"; menuBuilder << "RicShowContributingWellsFromPlotFeature"; menuBuilder.appendToMenu( &menu ); if ( menu.actions().size() > 0 ) { menu.exec( event->globalPos() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::keyPressEvent( QKeyEvent* keyEvent ) { m_plotDefinition->handleKeyPressEvent( keyEvent ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QLabel* RiuGridPlotWindow::createTitleLabel() const { QLabel* plotTitle = new QLabel( "PLOT TITLE HERE", nullptr ); plotTitle->setVisible( m_plotDefinition->isPlotTitleVisible() ); plotTitle->setAlignment( Qt::AlignHCenter ); plotTitle->setWordWrap( true ); plotTitle->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); return plotTitle; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::resizeEvent( QResizeEvent* event ) { QWidget::resizeEvent( event ); bool needsUpdate = false; for ( int i = 0; i < m_legends.size(); ++i ) { if ( m_legends[i]->isVisible() ) { int columnCount = m_legends[i]->columnCount(); if ( columnCount != m_legendColumns[i] ) { int oldColumnCount = m_legendColumns[i]; m_legendColumns[i] = columnCount; if ( oldColumnCount != -1 ) { needsUpdate = true; } } } } if ( needsUpdate ) { performUpdate(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::showEvent( QShowEvent* event ) { QWidget::showEvent( event ); performUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::dragEnterEvent( QDragEnterEvent* event ) { if ( this->geometry().contains( event->pos() ) ) { event->acceptProposedAction(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::dragMoveEvent( QDragMoveEvent* event ) { if ( this->geometry().contains( event->pos() ) ) { RiuQwtPlotWidget* source = dynamic_cast( event->source() ); if ( source ) { QRect originalGeometry = source->geometry(); QPoint offset = source->dragStartPosition(); QRect newRect( event->pos() - offset, originalGeometry.size() ); QList> visiblePlotWidgets = this->visiblePlotWidgets(); int insertBeforeIndex = visiblePlotWidgets.size(); for ( int visibleIndex = 0; visibleIndex < visiblePlotWidgets.size(); ++visibleIndex ) { visiblePlotWidgets[visibleIndex]->setWidgetState( RiuWidgetStyleSheet::DEFAULT ); if ( visiblePlotWidgets[visibleIndex]->frameIsInFrontOfThis( newRect ) ) { insertBeforeIndex = std::min( insertBeforeIndex, visibleIndex ); } } if ( insertBeforeIndex >= 0 && insertBeforeIndex < visiblePlotWidgets.size() ) { visiblePlotWidgets[insertBeforeIndex]->setWidgetState( RiuWidgetStyleSheet::DRAG_TARGET_BEFORE ); } if ( insertBeforeIndex > 0 ) { int insertAfterIndex = insertBeforeIndex - 1; visiblePlotWidgets[insertAfterIndex]->setWidgetState( RiuWidgetStyleSheet::DRAG_TARGET_AFTER ); event->acceptProposedAction(); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::dragLeaveEvent( QDragLeaveEvent* event ) { for ( int tIdx = 0; tIdx < m_plotWidgets.size(); ++tIdx ) { m_plotWidgets[tIdx]->setWidgetState( RiuWidgetStyleSheet::DEFAULT ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::dropEvent( QDropEvent* event ) { for ( int tIdx = 0; tIdx < m_plotWidgets.size(); ++tIdx ) { m_plotWidgets[tIdx]->setWidgetState( RiuWidgetStyleSheet::DEFAULT ); } if ( this->geometry().contains( event->pos() ) ) { RiuQwtPlotWidget* source = dynamic_cast( event->source() ); if ( source ) { event->acceptProposedAction(); QRect originalGeometry = source->geometry(); QPoint offset = source->dragStartPosition(); QRect newRect( event->pos() - offset, originalGeometry.size() ); int beforeIndex = m_plotWidgets.size(); for ( int tIdx = 0; tIdx < m_plotWidgets.size(); ++tIdx ) { if ( m_plotWidgets[tIdx]->isVisible() ) { if ( m_plotWidgets[tIdx]->frameIsInFrontOfThis( newRect ) ) { beforeIndex = tIdx; break; } } } RimPlotInterface* insertAfter = nullptr; if ( beforeIndex > 0 ) { insertAfter = m_plotWidgets[beforeIndex - 1]->plotDefinition(); } RimPlotInterface* plotWidget = source->plotDefinition(); if ( insertAfter != plotWidget ) { RicWellLogPlotTrackFeatureImpl::movePlotsToGridPlotWindow( m_plotDefinition, { plotWidget }, insertAfter ); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::pair RiuGridPlotWindow::rowAndColumnCount( int plotWidgetCount ) const { int columnCount = std::max( 1, std::min( m_plotDefinition->columnCount(), plotWidgetCount ) ); int rowCount = static_cast( std::ceil( plotWidgetCount / static_cast( columnCount ) ) ); return std::make_pair( rowCount, columnCount ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::onSelectionManagerSelectionChanged( const std::set& changedSelectionLevels ) { for ( RiuQwtPlotWidget* plotWidget : m_plotWidgets ) { bool isSelected = false; for ( int changedLevel : changedSelectionLevels ) { isSelected = isSelected || caf::SelectionManager::instance()->isSelected( plotWidget->plotOwner(), changedLevel ); } if ( isSelected ) { plotWidget->setWidgetState( RiuWidgetStyleSheet::SELECTED ); } else { plotWidget->setWidgetState( RiuWidgetStyleSheet::DEFAULT ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::performUpdate() { reinsertPlotWidgetsAndScrollbar(); alignCanvasTopsAndScrollbar(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::alignCanvasTopsAndScrollbar() { CVF_ASSERT( m_legends.size() == m_plotWidgets.size() ); QList> plotWidgets = visiblePlotWidgets(); if ( plotWidgets.empty() ) return; auto rowAndColumnCount = this->rowAndColumnCount( plotWidgets.size() ); std::vector maxExtents( rowAndColumnCount.first, 0.0 ); for ( int visibleIndex = 0; visibleIndex < plotWidgets.size(); ++visibleIndex ) { int row = visibleIndex / rowAndColumnCount.second; QFont font = m_plotWidgets[visibleIndex]->axisFont( QwtPlot::xTop ); maxExtents[row] = std::max( maxExtents[row], plotWidgets[visibleIndex]->axisScaleDraw( QwtPlot::xTop )->extent( font ) ); } for ( int visibleIndex = 0; visibleIndex < plotWidgets.size(); ++visibleIndex ) { int row = visibleIndex / rowAndColumnCount.second; plotWidgets[visibleIndex]->axisScaleDraw( QwtPlot::xTop )->setMinimumExtent( maxExtents[row] ); } m_scrollBarLayout->setContentsMargins( 0, maxExtents[0], 0, 0 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::reinsertPlotWidgetsAndScrollbar() { clearGridLayout(); for ( int tIdx = 0; tIdx < m_plotWidgets.size(); ++tIdx ) { m_plotWidgets[tIdx]->hide(); m_legends[tIdx]->hide(); } QList> plotWidgets = this->visiblePlotWidgets(); QList> legends = this->visibleLegends(); auto rowAndColumnCount = this->rowAndColumnCount( plotWidgets.size() ); for ( int visibleIndex = 0; visibleIndex < plotWidgets.size(); ++visibleIndex ) { int row = visibleIndex / rowAndColumnCount.second; int column = visibleIndex % rowAndColumnCount.second; m_gridLayout->addWidget( legends[visibleIndex], 2 * row, column ); m_gridLayout->addWidget( plotWidgets[visibleIndex], 2 * row + 1, column ); if ( m_plotDefinition->legendsVisible() ) { int legendColumns = 1; if ( m_plotDefinition->legendsHorizontal() ) { legendColumns = 0; // unlimited } legends[visibleIndex]->setMaxColumns( legendColumns ); int minimumHeight = legends[visibleIndex]->heightForWidth( plotWidgets[visibleIndex]->width() ); legends[visibleIndex]->setMinimumHeight( minimumHeight ); QFont legendFont = legends[visibleIndex]->font(); legendFont.setPointSize( fontSize() - 1 ); legends[visibleIndex]->setFont( legendFont ); legends[visibleIndex]->show(); } else { legends[visibleIndex]->hide(); } plotWidgets[visibleIndex]->setAxisLabelsAndTicksEnabled( QwtPlot::yLeft, column == 0 ); plotWidgets[visibleIndex]->setAxisTitleEnabled( QwtPlot::yLeft, column == 0 ); plotWidgets[visibleIndex]->show(); int widthScaleFactor = plotWidgets[visibleIndex]->widthScaleFactor(); if ( column == 0 ) { widthScaleFactor += 1; // Give it a bit extra room due to axis } m_gridLayout->setColumnStretch( column, std::max( m_gridLayout->columnStretch( column ), plotWidgets[visibleIndex]->widthScaleFactor() ) ); m_gridLayout->setRowStretch( 2 * row + 1, 1 ); } m_gridLayout->addLayout( m_scrollBarLayout, 1, rowAndColumnCount.second, rowAndColumnCount.first * 2 - 1, 1 ); m_gridLayout->setColumnStretch( rowAndColumnCount.second, 0 ); m_scrollBar->setVisible( plotWidgets.size() > 0 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridPlotWindow::clearGridLayout() { if ( m_gridLayout ) { for ( int tIdx = 0; tIdx < m_plotWidgets.size(); ++tIdx ) { m_gridLayout->removeWidget( m_legends[tIdx] ); m_gridLayout->removeWidget( m_plotWidgets[tIdx] ); } QLayoutItem* item; while ( ( item = m_gridLayout->takeAt( 0 ) ) != 0 ) { } QWidget().setLayout( m_gridLayout ); delete m_gridLayout; m_gridLayout = new QGridLayout( m_plotWidgetFrame ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList> RiuGridPlotWindow::visiblePlotWidgets() const { QList> plotWidgets; for ( QPointer plotWidget : m_plotWidgets ) { if ( plotWidget->isChecked() ) { plotWidgets.push_back( plotWidget ); } } return plotWidgets; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList> RiuGridPlotWindow::visibleLegends() const { QList> legends; for ( int i = 0; i < m_plotWidgets.size(); ++i ) { if ( m_plotWidgets[i]->isChecked() ) { legends.push_back( m_legends[i] ); } } return legends; }