diff --git a/.ci/travis/linux/script.sh b/.ci/travis/linux/script.sh index 7e79d8c5ae9..ee2026243c9 100755 --- a/.ci/travis/linux/script.sh +++ b/.ci/travis/linux/script.sh @@ -13,6 +13,8 @@ # # ########################################################################### +set -e + export PYTHONPATH=${HOME}/osgeo4travis/lib/python3.3/site-packages/ export PATH=${HOME}/osgeo4travis/bin:${HOME}/osgeo4travis/sbin:${HOME}/OTB-5.6.0-Linux64/bin:${PATH} export LD_LIBRARY_PATH=${HOME}/osgeo4travis/lib @@ -27,18 +29,17 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export OTB_APPLICATION_PATH=${HOME}/OTB-5.6.0-Linux64/lib/otb/applications export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so -export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i -k" +export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i" -# This works around an issue where travis would timeout on master because +# This works around an issue where travis would timeout because # when make is run inside ctest no output is generated. At the current time -# nobody know why, but at least this workaround gets travis results for master +# nobody know why, but at least this workaround gets travis results # back. Better approaches VERY welcome. -if [[ ${TRAVIS_PULL_REQUEST} == "false" ]]; -then - pushd build - $CTEST_BUILD_COMMAND - popd -fi +pushd build +echo "travis_fold:start:qgis_build" +$CTEST_BUILD_COMMAND +echo "travis_fold:end:qgis_build" +popd python ${TRAVIS_BUILD_DIR}/.ci/travis/scripts/ctest2travis.py \ xvfb-run ctest -V -E "$(cat ${DIR}/blacklist.txt | sed -r '/^(#.*?)?$/d' | paste -sd '|' -)" -S ${DIR}/../travis.ctest --output-on-failure diff --git a/python/gui/attributetable/qgsdualview.sip b/python/gui/attributetable/qgsdualview.sip index 5699eed4a08..0ae4c8e9051 100644 --- a/python/gui/attributetable/qgsdualview.sip +++ b/python/gui/attributetable/qgsdualview.sip @@ -171,13 +171,6 @@ class QgsDualView : QStackedWidget :rtype: str %End - protected: - - void columnBoxInit(); -%Docstring - Initializes widgets which depend on the attributes of this layer -%End - public slots: void setCurrentEditSelection( const QgsFeatureIds &fids ); @@ -244,6 +237,9 @@ class QgsDualView : QStackedWidget \param mode new mode %End + protected: + virtual void hideEvent( QHideEvent *event ); + }; class QgsAttributeTableAction : QAction diff --git a/src/gui/attributetable/qgsdualview.cpp b/src/gui/attributetable/qgsdualview.cpp index 20ee31d2095..9c0c1b35e87 100644 --- a/src/gui/attributetable/qgsdualview.cpp +++ b/src/gui/attributetable/qgsdualview.cpp @@ -56,7 +56,6 @@ QgsDualView::QgsDualView( QWidget *parent ) mConditionalFormatWidget->hide(); - mPreviewActionMapper = new QSignalMapper( this ); mPreviewColumnsMenu = new QMenu( this ); mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu ); @@ -66,7 +65,6 @@ QgsDualView::QgsDualView( QWidget *parent ) // Connect layer list preview signals connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder ); - connect( mPreviewActionMapper, static_cast < void ( QSignalMapper::* )( QObject * ) > ( &QSignalMapper::mapped ), this, &QgsDualView::previewColumnChanged ); connect( mFeatureList, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged ); } @@ -149,16 +147,15 @@ void QgsDualView::columnBoxInit() if ( fieldIndex == -1 ) continue; - if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() ).type() != QLatin1String( "Hidden" ) ) + QString fieldName = field.name(); + if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName ).type() != QLatin1String( "Hidden" ) ) { QIcon icon = mLayer->fields().iconForField( fieldIndex ); - QString text = field.name(); + QString text = mLayer->attributeDisplayName( fieldIndex ); // Generate action for the preview popup button of the feature list QAction *previewAction = new QAction( icon, text, mFeatureListPreviewButton ); - mPreviewActionMapper->setMapping( previewAction, previewAction ); - connect( previewAction, &QAction::triggered, this, [previewAction, this]( bool ) { this->mPreviewActionMapper->map( previewAction ); } - ); + connect( previewAction, &QAction::triggered, this, [ = ] { previewColumnChanged( previewAction, fieldName ); } ); mPreviewColumnsMenu->addAction( previewAction ); if ( text == defaultField ) @@ -168,21 +165,26 @@ void QgsDualView::columnBoxInit() } } + QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this ); + connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression ); + mFeatureListPreviewButton->addAction( sortByPreviewExpression ); + + QAction *separator = new QAction( mFeatureListPreviewButton ); + separator->setSeparator( true ); + mFeatureListPreviewButton->addAction( separator ); + restoreRecentDisplayExpressions(); + // If there is no single field found as preview if ( !mFeatureListPreviewButton->defaultAction() ) { mFeatureList->setDisplayExpression( displayExpression ); mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview ); - mDisplayExpression = mFeatureList->displayExpression(); + setDisplayExpression( mFeatureList->displayExpression() ); } else { mFeatureListPreviewButton->defaultAction()->trigger(); } - - QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this ); - connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression ); - mFeatureListPreviewButton->addAction( sortByPreviewExpression ); } void QgsDualView::setView( QgsDualView::ViewMode view ) @@ -314,6 +316,95 @@ void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest & mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel ); } +void QgsDualView::restoreRecentDisplayExpressions() +{ + const QVariantList previewExpressions = mLayer->customProperty( QStringLiteral( "dualview/previewExpressions" ) ).toList(); + + for ( const QVariant &previewExpression : previewExpressions ) + insertRecentlyUsedDisplayExpression( previewExpression.toString() ); +} + +void QgsDualView::saveRecentDisplayExpressions() const +{ + QList actions = mFeatureListPreviewButton->actions(); + + // Remove existing same action + int index = actions.indexOf( mLastDisplayExpressionAction ); + if ( index != -1 ) + { + QVariantList previewExpressions; + for ( ; index < actions.length(); ++index ) + { + QAction *action = actions.at( index ); + previewExpressions << action->property( "previewExpression" ); + } + + mLayer->setCustomProperty( QStringLiteral( "dualview/previewExpressions" ), previewExpressions ); + } +} + +void QgsDualView::setDisplayExpression( const QString &expression ) +{ + mDisplayExpression = expression; + insertRecentlyUsedDisplayExpression( expression ); +} + +void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression ) +{ + QList actions = mFeatureListPreviewButton->actions(); + + // Remove existing same action + int index = actions.indexOf( mLastDisplayExpressionAction ); + if ( index != -1 ) + { + for ( int i = 0; index + i < actions.length(); ++i ) + { + QAction *action = actions.at( index ); + if ( action->text() == expression || i >= 9 ) + { + if ( action == mLastDisplayExpressionAction ) + mLastDisplayExpressionAction = nullptr; + mFeatureListPreviewButton->removeAction( action ); + } + else + { + if ( !mLastDisplayExpressionAction ) + mLastDisplayExpressionAction = action; + } + } + } + + QString name = expression; + QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) ); + if ( expression.startsWith( QLatin1String( "COALESCE( \"" ) ) && expression.endsWith( QLatin1String( ", '' )" ) ) ) + { + name = expression.mid( 11, expression.length() - 24 ); // Numbers calculated from the COALESCE / parts + + int fieldIndex = mLayer->fields().indexOf( name ); + if ( fieldIndex != -1 ) + { + name = mLayer->attributeDisplayName( fieldIndex ); + icon = mLayer->fields().iconForField( fieldIndex ); + } + else + { + name = expression; + } + } + + QAction *previewAction = new QAction( icon, name, mFeatureListPreviewButton ); + previewAction->setProperty( "previewExpression", expression ); + connect( previewAction, &QAction::triggered, this, [expression, this]( bool ) + { + setDisplayExpression( expression ); + mFeatureListPreviewButton->setText( expression ); + } + ); + + mFeatureListPreviewButton->insertAction( mLastDisplayExpressionAction, previewAction ); + mLastDisplayExpressionAction = previewAction; +} + void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool &ok ) { if ( mLayer->isEditable() && !mAttributeForm->save() ) @@ -387,33 +478,27 @@ void QgsDualView::previewExpressionBuilder() mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup ); } - mDisplayExpression = mFeatureList->displayExpression(); + setDisplayExpression( mFeatureList->displayExpression() ); } -void QgsDualView::previewColumnChanged( QObject *action ) +void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &expression ) { - QAction *previewAction = qobject_cast< QAction * >( action ); - - if ( previewAction ) + if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '' )" ).arg( expression ) ) ) { - if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '' )" ).arg( previewAction->text() ) ) ) - { - QMessageBox::warning( this, - tr( "Could not set preview column" ), - tr( "Could not set column '%1' as preview column.\nParser error:\n%2" ) - .arg( previewAction->text(), mFeatureList->parserErrorString() ) - ); - } - else - { - mFeatureListPreviewButton->setDefaultAction( previewAction ); - mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup ); - } + QMessageBox::warning( this, + tr( "Could not set preview column" ), + tr( "Could not set column '%1' as preview column.\nParser error:\n%2" ) + .arg( previewAction->text(), mFeatureList->parserErrorString() ) + ); + } + else + { + mFeatureListPreviewButton->setText( previewAction->text() ); + mFeatureListPreviewButton->setIcon( previewAction->icon() ); + mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup ); } - mDisplayExpression = mFeatureList->displayExpression(); - - Q_ASSERT( previewAction ); + setDisplayExpression( mFeatureList->displayExpression() ); } int QgsDualView::featureCount() @@ -438,6 +523,12 @@ void QgsDualView::copyCellContent() const } } +void QgsDualView::hideEvent( QHideEvent *event ) +{ + Q_UNUSED( event ) + saveRecentDisplayExpressions(); +} + void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex ) { if ( !menu ) diff --git a/src/gui/attributetable/qgsdualview.h b/src/gui/attributetable/qgsdualview.h index 9b8b16d8c82..a64d9d70bf0 100644 --- a/src/gui/attributetable/qgsdualview.h +++ b/src/gui/attributetable/qgsdualview.h @@ -197,13 +197,6 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas */ QString sortExpression() const; - protected: - - /** - * Initializes widgets which depend on the attributes of this layer - */ - void columnBoxInit(); - public slots: /** @@ -265,6 +258,9 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas */ void formModeChanged( QgsAttributeForm::Mode mode ); + protected: + virtual void hideEvent( QHideEvent *event ) override; + private slots: void on_mFeatureList_aboutToChangeEditSelection( bool &ok ); @@ -278,7 +274,7 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas void previewExpressionBuilder(); - void previewColumnChanged( QObject *previewAction ); + void previewColumnChanged( QAction *previewAction, const QString &expression ); void viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex ); @@ -335,16 +331,26 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas void rebuildFullLayerCache(); private: + + /** + * Initializes widgets which depend on the attributes of this layer + */ + void columnBoxInit(); void initLayerCache( bool cacheGeometry ); void initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures ); + void restoreRecentDisplayExpressions(); + void saveRecentDisplayExpressions() const; + void setDisplayExpression( const QString &expression ); + void insertRecentlyUsedDisplayExpression( const QString &expression ); QgsAttributeEditorContext mEditorContext; QgsAttributeTableModel *mMasterModel = nullptr; QgsAttributeTableFilterModel *mFilterModel = nullptr; QgsFeatureListModel *mFeatureListModel = nullptr; QgsAttributeForm *mAttributeForm = nullptr; - QSignalMapper *mPreviewActionMapper = nullptr; QMenu *mPreviewColumnsMenu = nullptr; + QMenu *mPreviewActionMenu = nullptr; + QAction *mLastDisplayExpressionAction = nullptr; QMenu *mHorizontalHeaderMenu = nullptr; QgsVectorLayerCache *mLayerCache = nullptr; QgsVectorLayer *mLayer = nullptr;