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 mFieldTypeList.clear();
163 bool fieldMatch =
false;
166 switch ( mDataTypes )
173 fieldMatch = f.isNumeric() || f.type() == QVariant::String;
177 fieldMatch = f.type() == QVariant::String;
183 case QVariant::String:
184 fieldType = tr(
"string" );
187 fieldType = tr(
"integer" );
189 case QVariant::LongLong:
190 fieldType = tr(
"integer64" );
192 case QVariant::Double:
193 fieldType = tr(
"double" );
196 fieldType = tr(
"boolean" );
199 fieldType = tr(
"unknown type" );
203 mFieldNameList << f.name();
204 mFieldTypeList << fieldType;
217 mVectorLayer = layer;
222 const auto constMSiblingWidgets = mSiblingWidgets;
223 for (
const SiblingWidget &sw : constMSiblingWidgets )
225 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
228 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
234 const auto constMSiblingWidgets = mSiblingWidgets;
235 for (
const SiblingWidget &sw : constMSiblingWidgets )
237 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
240 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
246 const auto constMSiblingWidgets = mSiblingWidgets;
247 for (
const SiblingWidget &sw : constMSiblingWidgets )
249 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
252 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
258 const auto constMSiblingWidgets = mSiblingWidgets;
259 for (
const SiblingWidget &sw : constMSiblingWidgets )
261 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
264 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
272 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
273 || event->button() == Qt::RightButton )
275 setActivePrivate( !mProperty.
isActive() );
283 QToolButton::mousePressEvent( event );
297 mFieldName =
property.field();
302 mExpressionString =
property.expressionString();
310 mExpressionString.clear();
312 mProperty = property;
319 void QgsPropertyOverrideButton::aboutToShowMenu()
321 mDefineMenu->clear();
325 bool hasExp = !mExpressionString.isEmpty();
326 QString ddTitle = tr(
"Data defined override" );
328 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
329 QFont titlefont = ddTitleAct->font();
330 titlefont.setItalic(
true );
331 ddTitleAct->setFont( titlefont );
332 ddTitleAct->setEnabled(
false );
334 bool addActiveAction =
false;
339 addActiveAction = !exp.hasParserError();
344 addActiveAction = mFieldNameList.contains( mFieldName );
347 if ( addActiveAction )
350 mDefineMenu->addAction( mActionActive );
351 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
352 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
355 if ( !mFullDescription.isEmpty() )
357 mDefineMenu->addAction( mActionDescription );
360 mDefineMenu->addSeparator();
363 if ( mAuxiliaryStorageEnabled && mVectorLayer )
365 mDefineMenu->addAction( mActionCreateAuxiliaryField );
369 mActionCreateAuxiliaryField->setEnabled(
true );
370 mActionCreateAuxiliaryField->setChecked(
false );
374 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
376 mActionCreateAuxiliaryField->setEnabled(
false );
377 mActionCreateAuxiliaryField->setChecked(
true );
381 bool fieldActive =
false;
382 if ( !mDataTypesString.isEmpty() )
384 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
385 fieldTitleAct->setFont( titlefont );
386 fieldTitleAct->setEnabled(
false );
388 mDefineMenu->addAction( mActionDataTypes );
390 mFieldsMenu->clear();
392 if ( !mFieldNameList.isEmpty() )
395 for (
int j = 0; j < mFieldNameList.count(); ++j )
397 QString fldname = mFieldNameList.at( j );
398 QAction *act = mFieldsMenu->addAction( fldname +
" (" + mFieldTypeList.at( j ) +
')' );
399 act->setData( QVariant( fldname ) );
400 if ( mFieldName == fldname )
402 act->setCheckable(
true );
410 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
411 act->setEnabled(
false );
414 mDefineMenu->addSeparator();
417 mFieldsMenu->menuAction()->setCheckable(
true );
420 bool colorActive =
false;
421 mColorsMenu->clear();
426 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
427 colorTitleAct->setFont( titlefont );
428 colorTitleAct->setEnabled(
false );
430 QList<QgsProjectColorScheme *> projectSchemes;
432 if ( projectSchemes.length() > 0 )
436 for (
const auto &color : colors )
438 if ( color.second.isEmpty() )
442 QAction *act = mColorsMenu->addAction( color.second );
443 act->setIcon( icon );
446 act->setCheckable(
true );
447 act->setChecked(
true );
453 if ( mColorsMenu->actions().isEmpty() )
455 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
456 act->setEnabled(
false );
459 mDefineMenu->addAction( mActionColors );
460 mColorsMenu->menuAction()->setCheckable(
true );
461 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
463 mDefineMenu->addSeparator();
466 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
467 exprTitleAct->setFont( titlefont );
468 exprTitleAct->setEnabled(
false );
470 mVariablesMenu->clear();
471 bool variableActive =
false;
472 if ( mExpressionContextGenerator )
476 const auto constVariables = variables;
477 for (
const QString &variable : constVariables )
481 if ( variable.startsWith(
'_' ) )
484 QAction *act = mVariablesMenu->addAction( variable );
485 act->setData( QVariant( variable ) );
489 act->setCheckable(
true );
490 act->setChecked(
true );
491 variableActive =
true;
496 if ( mVariablesMenu->actions().isEmpty() )
498 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
499 act->setEnabled(
false );
502 mDefineMenu->addAction( mActionVariables );
503 mVariablesMenu->menuAction()->setCheckable(
true );
504 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
508 QString expString = mExpressionString;
509 if ( expString.length() > 35 )
511 expString.truncate( 35 );
512 expString.append( QChar( 0x2026 ) );
515 expString.prepend( tr(
"Current: " ) );
517 if ( !mActionExpression )
519 mActionExpression =
new QAction( expString,
this );
520 mActionExpression->setCheckable(
true );
524 mActionExpression->setText( expString );
526 mDefineMenu->addAction( mActionExpression );
529 mDefineMenu->addAction( mActionExpDialog );
530 mDefineMenu->addAction( mActionCopyExpr );
531 mDefineMenu->addAction( mActionPasteExpr );
535 mDefineMenu->addAction( mActionExpDialog );
536 mDefineMenu->addAction( mActionPasteExpr );
539 if ( hasExp || !mFieldName.isEmpty() )
541 mDefineMenu->addSeparator();
542 mDefineMenu->addAction( mActionClearExpr );
547 mDefineMenu->addSeparator();
548 mActionAssistant->setCheckable( mProperty.
transformer() );
549 mActionAssistant->setChecked( mProperty.
transformer() );
550 mDefineMenu->addAction( mActionAssistant );
554 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
556 if ( action == mActionActive )
558 setActivePrivate( mActionActive->data().toBool() );
562 else if ( action == mActionDescription )
564 showDescriptionDialog();
566 else if ( action == mActionExpDialog )
568 showExpressionDialog();
570 else if ( action == mActionExpression )
574 setActivePrivate(
true );
579 else if ( action == mActionCopyExpr )
581 QApplication::clipboard()->setText( mExpressionString );
583 else if ( action == mActionPasteExpr )
585 QString exprString = QApplication::clipboard()->text();
586 if ( !exprString.isEmpty() )
588 mExpressionString = exprString;
591 setActivePrivate(
true );
597 else if ( action == mActionClearExpr )
599 setActivePrivate(
false );
602 mExpressionString.clear();
607 else if ( action == mActionAssistant )
611 else if ( action == mActionCreateAuxiliaryField )
615 else if ( mFieldsMenu->actions().contains( action ) )
617 if ( action->isEnabled() )
619 if ( mFieldName != action->text() )
621 mFieldName = action->data().toString();
625 setActivePrivate(
true );
631 else if ( mVariablesMenu->actions().contains( action ) )
633 if ( mExpressionString != action->text().prepend(
"@" ) )
635 mExpressionString = action->data().toString().prepend(
"@" );
639 setActivePrivate(
true );
644 else if ( mColorsMenu->actions().contains( action ) )
646 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
648 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
652 setActivePrivate(
true );
660 void QgsPropertyOverrideButton::showDescriptionDialog()
662 QgsMessageViewer *mv =
new QgsMessageViewer(
this );
663 mv->setWindowTitle( tr(
"Data Definition Description" ) );
664 mv->setMessageAsHtml( mFullDescription );
669 void QgsPropertyOverrideButton::showExpressionDialog()
678 d.setExpectedOutputFormat( mInputDescription );
679 if ( d.exec() == QDialog::Accepted )
681 mExpressionString = d.expressionText().trimmed();
684 setActivePrivate( !mExpressionString.isEmpty() );
692 void QgsPropertyOverrideButton::showAssistant()
712 mFieldName = this->mProperty.
field();
728 QDialog *dlg =
new QDialog(
this );
729 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
731 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
733 dlg->setLayout(
new QVBoxLayout() );
734 dlg->layout()->addWidget( widget );
735 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
736 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
737 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
738 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
739 dlg->layout()->addWidget( buttonBox );
741 if ( dlg->exec() == QDialog::Accepted )
745 mFieldName = mProperty.
field();
752 settings.
setValue( key, dlg->saveGeometry() );
756 void QgsPropertyOverrideButton::updateGui()
758 bool hasExp = !mExpressionString.isEmpty();
759 bool hasField = !mFieldName.isEmpty();
762 QString deftip = tr(
"undefined" );
768 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
769 QRegularExpressionMatch match = rx.match( mExpressionString );
770 if ( match.hasMatch() )
773 deftip = match.captured( 1 );
774 deftype = tr(
"project color" );
779 if ( exp.hasParserError() )
782 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
786 deftip = mExpressionString;
794 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
797 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
808 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
810 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
812 if ( !mUsageInfo.isEmpty() )
814 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
817 if ( !mInputDescription.isEmpty() )
819 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
822 if ( !mDataTypesString.isEmpty() )
824 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
827 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
833 if ( deftip.length() > 75 )
835 deftip.truncate( 75 );
836 deftip.append( QChar( 0x2026 ) );
839 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
841 setToolTip( mFullDescription );
845 void QgsPropertyOverrideButton::setActivePrivate(
bool active )
854 void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
856 const auto constMSiblingWidgets = mSiblingWidgets;
857 for (
const SiblingWidget &sw : constMSiblingWidgets )
859 switch ( sw.mSiblingType )
862 case SiblingCheckState:
867 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
868 if ( btn && btn->isCheckable() )
870 btn->setChecked( sw.mNatural ? state : !state );
874 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
875 if ( grpbx && grpbx->isCheckable() )
877 grpbx->setChecked( sw.mNatural ? state : !state );
884 case SiblingEnableState:
886 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
888 le->setReadOnly( sw.mNatural ? !state : state );
890 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
894 case SiblingVisibility:
896 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
900 case SiblingExpressionText:
902 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
909 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
918 case SiblingLinkedWidget:
920 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
924 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
925 QRegularExpressionMatch match = rx.match( mExpressionString );
926 if ( match.hasMatch() )
928 cb->linkToProjectColor( match.captured( 1 ) );
933 cb->linkToProjectColor( QString() );
956 mExpressionContextGenerator = generator;
961 for (
const SiblingWidget &sw : qgis::as_const( mSiblingWidgets ) )
963 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
966 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
968 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
980 void QgsPropertyOverrideButton::showHelp()
982 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );