32 #include <QMouseEvent>
35 #include <QRegularExpression>
39 : QToolButton( parent )
40 , mVectorLayer( layer )
43 setFocusPolicy( Qt::StrongFocus );
48 setFixedSize( 2 *
static_cast< int >( 1.25 *
iconSize / 2.0 ), 2 *
static_cast< int >(
iconSize * 1.1 / 2.0 ) );
51 setPopupMode( QToolButton::InstantPopup );
55 mDefineMenu =
new QMenu(
this );
56 connect( mDefineMenu, &QMenu::aboutToShow,
this, &QgsPropertyOverrideButton::aboutToShowMenu );
57 connect( mDefineMenu, &QMenu::triggered,
this, &QgsPropertyOverrideButton::menuActionTriggered );
58 setMenu( mDefineMenu );
60 mFieldsMenu =
new QMenu(
this );
61 mActionDataTypes =
new QAction(
this );
63 mActionDataTypes->setMenu( mFieldsMenu );
65 mActionVariables =
new QAction( tr(
"Variable" ),
this );
66 mVariablesMenu =
new QMenu(
this );
67 mActionVariables->setMenu( mVariablesMenu );
69 mActionColors =
new QAction( tr(
"Color" ),
this );
70 mColorsMenu =
new QMenu(
this );
71 mActionColors->setMenu( mColorsMenu );
73 mActionActive =
new QAction(
this );
74 QFont f = mActionActive->font();
76 mActionActive->setFont( f );
78 mActionDescription =
new QAction( tr(
"Description…" ),
this );
80 mActionCreateAuxiliaryField =
new QAction( tr(
"Store Data in the Project" ),
this );
81 mActionCreateAuxiliaryField->setCheckable(
true );
83 mActionExpDialog =
new QAction( tr(
"Edit…" ),
this );
84 mActionExpression =
nullptr;
85 mActionPasteExpr =
new QAction( tr(
"Paste" ),
this );
86 mActionCopyExpr =
new QAction( tr(
"Copy" ),
this );
87 mActionClearExpr =
new QAction( tr(
"Clear" ),
this );
88 mActionAssistant =
new QAction( tr(
"Assistant…" ),
this );
89 QFont assistantFont = mActionAssistant->font();
90 assistantFont.setBold(
true );
91 mActionAssistant->setFont( assistantFont );
92 mDefineMenu->addAction( mActionAssistant );
103 mVectorLayer = layer;
104 mAuxiliaryStorageEnabled = auxiliaryStorageEnabled;
108 mDefinition = definition;
109 mDataTypes = mDefinition.
dataType();
111 mInputDescription = mDefinition.
helpText();
112 mFullDescription.clear();
116 mDataTypesString.clear();
119 switch ( mDataTypes )
122 ts << tr(
"boolean" );
127 ts << tr(
"double" );
131 ts << tr(
"string" );
137 mDataTypesString = ts.join( QLatin1String(
", " ) );
138 mActionDataTypes->setText( tr(
"Field type: " ) + mDataTypesString );
154 mFieldNameList.clear();
155 mFieldDisplayNameList.clear();
165 bool fieldMatch =
false;
166 switch ( mDataTypes )
173 fieldMatch = f.isNumeric() || f.type() == QVariant::String;
177 fieldMatch = f.type() == QVariant::String;
183 mFieldNameList << f.name();
184 mFieldDisplayNameList << f.displayNameWithAlias();
185 mFieldIcons << fields.iconForField( idx,
true );
199 mVectorLayer = layer;
206 const auto constMSiblingWidgets = mSiblingWidgets;
207 for (
const SiblingWidget &sw : constMSiblingWidgets )
209 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
212 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
218 const auto constMSiblingWidgets = mSiblingWidgets;
219 for (
const SiblingWidget &sw : constMSiblingWidgets )
221 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
224 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
230 const auto constMSiblingWidgets = mSiblingWidgets;
231 for (
const SiblingWidget &sw : constMSiblingWidgets )
233 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
236 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
242 const auto constMSiblingWidgets = mSiblingWidgets;
243 for (
const SiblingWidget &sw : constMSiblingWidgets )
245 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
248 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
256 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
257 || event->button() == Qt::RightButton )
259 setActivePrivate( !mProperty.
isActive() );
267 QToolButton::mousePressEvent( event );
281 mFieldName =
property.field();
286 mExpressionString =
property.expressionString();
294 mExpressionString.clear();
296 mProperty = property;
303 void QgsPropertyOverrideButton::aboutToShowMenu()
305 mDefineMenu->clear();
309 bool hasExp = !mExpressionString.isEmpty();
310 QString ddTitle = tr(
"Data defined override" );
312 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
313 QFont titlefont = ddTitleAct->font();
314 titlefont.setItalic(
true );
315 ddTitleAct->setFont( titlefont );
316 ddTitleAct->setEnabled(
false );
318 bool addActiveAction =
false;
323 addActiveAction = !exp.hasParserError();
328 addActiveAction = mFieldNameList.contains( mFieldName );
331 if ( addActiveAction )
334 mDefineMenu->addAction( mActionActive );
335 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
336 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
339 if ( !mFullDescription.isEmpty() )
341 mDefineMenu->addAction( mActionDescription );
344 mDefineMenu->addSeparator();
347 if ( mAuxiliaryStorageEnabled && mVectorLayer )
349 mDefineMenu->addAction( mActionCreateAuxiliaryField );
353 mActionCreateAuxiliaryField->setEnabled(
true );
354 mActionCreateAuxiliaryField->setChecked(
false );
358 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
360 mActionCreateAuxiliaryField->setEnabled(
false );
361 mActionCreateAuxiliaryField->setChecked(
true );
365 bool fieldActive =
false;
366 if ( !mDataTypesString.isEmpty() )
368 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
369 fieldTitleAct->setFont( titlefont );
370 fieldTitleAct->setEnabled(
false );
372 mDefineMenu->addAction( mActionDataTypes );
374 mFieldsMenu->clear();
376 if ( !mFieldNameList.isEmpty() )
379 for (
int j = 0; j < mFieldNameList.count(); ++j )
381 QString fldname = mFieldNameList.at( j );
382 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
383 act->setIcon( mFieldIcons.at( j ) );
384 act->setData( QVariant( fldname ) );
385 if ( mFieldName == fldname )
387 act->setCheckable(
true );
395 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
396 act->setEnabled(
false );
399 mDefineMenu->addSeparator();
402 mFieldsMenu->menuAction()->setCheckable(
true );
405 bool colorActive =
false;
406 mColorsMenu->clear();
411 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
412 colorTitleAct->setFont( titlefont );
413 colorTitleAct->setEnabled(
false );
415 QList<QgsProjectColorScheme *> projectSchemes;
417 if ( projectSchemes.length() > 0 )
421 for (
const auto &color : colors )
423 if ( color.second.isEmpty() )
427 QAction *act = mColorsMenu->addAction( color.second );
428 act->setIcon( icon );
431 act->setCheckable(
true );
432 act->setChecked(
true );
438 if ( mColorsMenu->actions().isEmpty() )
440 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
441 act->setEnabled(
false );
444 mDefineMenu->addAction( mActionColors );
445 mColorsMenu->menuAction()->setCheckable(
true );
446 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
448 mDefineMenu->addSeparator();
451 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
452 exprTitleAct->setFont( titlefont );
453 exprTitleAct->setEnabled(
false );
455 mVariablesMenu->clear();
456 bool variableActive =
false;
457 if ( mExpressionContextGenerator )
461 const auto constVariables = variables;
462 for (
const QString &variable : constVariables )
466 if ( variable.startsWith(
'_' ) )
469 QAction *act = mVariablesMenu->addAction( variable );
470 act->setData( QVariant( variable ) );
474 act->setCheckable(
true );
475 act->setChecked(
true );
476 variableActive =
true;
481 if ( mVariablesMenu->actions().isEmpty() )
483 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
484 act->setEnabled(
false );
487 mDefineMenu->addAction( mActionVariables );
488 mVariablesMenu->menuAction()->setCheckable(
true );
489 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
493 QString expString = mExpressionString;
494 if ( expString.length() > 35 )
496 expString.truncate( 35 );
497 expString.append( QChar( 0x2026 ) );
500 expString.prepend( tr(
"Current: " ) );
502 if ( !mActionExpression )
504 mActionExpression =
new QAction( expString,
this );
505 mActionExpression->setCheckable(
true );
509 mActionExpression->setText( expString );
511 mDefineMenu->addAction( mActionExpression );
514 mDefineMenu->addAction( mActionExpDialog );
515 mDefineMenu->addAction( mActionCopyExpr );
516 mDefineMenu->addAction( mActionPasteExpr );
520 mDefineMenu->addAction( mActionExpDialog );
521 mDefineMenu->addAction( mActionPasteExpr );
524 if ( hasExp || !mFieldName.isEmpty() )
526 mDefineMenu->addSeparator();
527 mDefineMenu->addAction( mActionClearExpr );
532 mDefineMenu->addSeparator();
533 mActionAssistant->setCheckable( mProperty.
transformer() );
534 mActionAssistant->setChecked( mProperty.
transformer() );
535 mDefineMenu->addAction( mActionAssistant );
539 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
541 if ( action == mActionActive )
543 setActivePrivate( mActionActive->data().toBool() );
547 else if ( action == mActionDescription )
549 showDescriptionDialog();
551 else if ( action == mActionExpDialog )
553 showExpressionDialog();
555 else if ( action == mActionExpression )
559 setActivePrivate(
true );
564 else if ( action == mActionCopyExpr )
566 QApplication::clipboard()->setText( mExpressionString );
568 else if ( action == mActionPasteExpr )
570 QString exprString = QApplication::clipboard()->text();
571 if ( !exprString.isEmpty() )
573 mExpressionString = exprString;
576 setActivePrivate(
true );
582 else if ( action == mActionClearExpr )
584 setActivePrivate(
false );
587 mExpressionString.clear();
593 else if ( action == mActionAssistant )
597 else if ( action == mActionCreateAuxiliaryField )
601 else if ( mFieldsMenu->actions().contains( action ) )
603 if ( action->isEnabled() )
605 if ( mFieldName != action->text() )
607 mFieldName = action->data().toString();
611 setActivePrivate(
true );
617 else if ( mVariablesMenu->actions().contains( action ) )
619 if ( mExpressionString != action->text().prepend(
"@" ) )
621 mExpressionString = action->data().toString().prepend(
"@" );
625 setActivePrivate(
true );
630 else if ( mColorsMenu->actions().contains( action ) )
632 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
634 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
638 setActivePrivate(
true );
646 void QgsPropertyOverrideButton::showDescriptionDialog()
648 QgsMessageViewer *mv =
new QgsMessageViewer(
this );
649 mv->setWindowTitle( tr(
"Data Definition Description" ) );
650 mv->setMessageAsHtml( mFullDescription );
655 void QgsPropertyOverrideButton::showExpressionDialog()
664 d.setExpectedOutputFormat( mInputDescription );
665 if ( d.exec() == QDialog::Accepted )
667 mExpressionString = d.expressionText().trimmed();
671 mProperty.
setActive( !mExpressionString.isEmpty() );
681 void QgsPropertyOverrideButton::showAssistant()
701 mFieldName = this->mProperty.
field();
718 QDialog *dlg =
new QDialog(
this );
719 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
721 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
723 dlg->setLayout(
new QVBoxLayout() );
724 dlg->layout()->addWidget( widget );
725 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
726 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
727 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
728 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
729 dlg->layout()->addWidget( buttonBox );
731 if ( dlg->exec() == QDialog::Accepted )
735 mFieldName = mProperty.
field();
742 settings.
setValue( key, dlg->saveGeometry() );
746 void QgsPropertyOverrideButton::updateGui()
748 bool hasExp = !mExpressionString.isEmpty();
749 bool hasField = !mFieldName.isEmpty();
752 QString deftip = tr(
"undefined" );
758 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
759 QRegularExpressionMatch match = rx.match( mExpressionString );
760 if ( match.hasMatch() )
763 deftip = match.captured( 1 );
764 deftype = tr(
"project color" );
769 if ( exp.hasParserError() )
772 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
776 deftip = mExpressionString;
784 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
787 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
798 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
800 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
802 if ( !mUsageInfo.isEmpty() )
804 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
807 if ( !mInputDescription.isEmpty() )
809 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
812 if ( !mDataTypesString.isEmpty() )
814 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
817 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
823 if ( deftip.length() > 75 )
825 deftip.truncate( 75 );
826 deftip.append( QChar( 0x2026 ) );
829 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
831 setToolTip( mFullDescription );
835 void QgsPropertyOverrideButton::setActivePrivate(
bool active )
844 void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
846 const auto constMSiblingWidgets = mSiblingWidgets;
847 for (
const SiblingWidget &sw : constMSiblingWidgets )
849 switch ( sw.mSiblingType )
852 case SiblingCheckState:
857 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
858 if ( btn && btn->isCheckable() )
860 btn->setChecked( sw.mNatural ? state : !state );
864 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
865 if ( grpbx && grpbx->isCheckable() )
867 grpbx->setChecked( sw.mNatural ? state : !state );
874 case SiblingEnableState:
876 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
878 le->setReadOnly( sw.mNatural ? !state : state );
880 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
884 case SiblingVisibility:
886 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
890 case SiblingExpressionText:
892 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
899 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
908 case SiblingLinkedWidget:
910 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
914 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
915 QRegularExpressionMatch match = rx.match( mExpressionString );
916 if ( match.hasMatch() )
918 cb->linkToProjectColor( match.captured( 1 ) );
923 cb->linkToProjectColor( QString() );
947 mExpressionContextGenerator = generator;
952 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
954 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
957 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
959 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
971 void QgsPropertyOverrideButton::showHelp()
973 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );