17#include "moc_qgsvariableeditorwidget.cpp" 
   42  QVBoxLayout *verticalLayout = 
new QVBoxLayout( 
this );
 
   43  verticalLayout->setSpacing( 3 );
 
   44  verticalLayout->setContentsMargins( 3, 3, 3, 3 );
 
   45  mTreeWidget = 
new QgsVariableEditorTree( 
this );
 
   46  mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
 
   47  verticalLayout->addWidget( mTreeWidget );
 
   48  QHBoxLayout *horizontalLayout = 
new QHBoxLayout();
 
   49  horizontalLayout->setSpacing( 6 );
 
   50  QSpacerItem *horizontalSpacer = 
new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
 
   51  horizontalLayout->addItem( horizontalSpacer );
 
   52  mAddButton = 
new QPushButton();
 
   54  mAddButton->setEnabled( 
false );
 
   55  mAddButton->setToolTip( tr( 
"Add variable" ) );
 
   56  horizontalLayout->addWidget( mAddButton );
 
   57  mRemoveButton = 
new QPushButton();
 
   59  mRemoveButton->setEnabled( 
false );
 
   60  mRemoveButton->setToolTip( tr( 
"Remove variable" ) );
 
   61  horizontalLayout->addWidget( mRemoveButton );
 
   62  verticalLayout->addLayout( horizontalLayout );
 
   63  connect( mRemoveButton, &QAbstractButton::clicked, 
this, &QgsVariableEditorWidget::mRemoveButton_clicked );
 
   64  connect( mAddButton, &QAbstractButton::clicked, 
this, &QgsVariableEditorWidget::mAddButton_clicked );
 
   65  connect( mTreeWidget, &QTreeWidget::itemSelectionChanged, 
this, &QgsVariableEditorWidget::selectionChanged );
 
 
   77  settings.
setValue( saveKey() + 
"column0width", mTreeWidget->header()->sectionSize( 0 ) );
 
 
   92  val = settings.
value( saveKey() + 
"column0width" );
 
   94  const int sectionSize = val.toInt( &ok );
 
   97    mTreeWidget->header()->resizeSection( 0, sectionSize );
 
  101  QWidget::showEvent( event );
 
 
  112  mTreeWidget->resetTree();
 
  113  mTreeWidget->setContext( mContext.get() );
 
  114  mTreeWidget->refreshTree();
 
 
  119  mEditableScopeIndex = scopeIndex;
 
  120  if ( mEditableScopeIndex >= 0 )
 
  122    mAddButton->setEnabled( 
true );
 
  124  mTreeWidget->setEditableScopeIndex( scopeIndex );
 
  125  mTreeWidget->refreshTree();
 
 
  130  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  134  return mContext->scope( mEditableScopeIndex );
 
 
  139  QVariantMap variables;
 
  140  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  147  for ( 
const QString &variable : constVariableNames )
 
  152    variables.insert( variable, scope->
variable( variable ) );
 
 
  158QString QgsVariableEditorWidget::saveKey()
 const 
  162  const QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
 
  163  QString saveKey = 
"/QgsVariableEditorTree/" + setGroup + 
'/';
 
  167void QgsVariableEditorWidget::mAddButton_clicked()
 
  169  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  173  scope->
setVariable( QStringLiteral( 
"new_variable" ), QVariant() );
 
  174  mTreeWidget->refreshTree();
 
  175  QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral( 
"new_variable" ) );
 
  176  const QModelIndex index = mTreeWidget->itemToIndex( item );
 
  177  mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
 
  178  mTreeWidget->editItem( item, 0 );
 
  183void QgsVariableEditorWidget::mRemoveButton_clicked()
 
  185  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  189  const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
 
  191  const auto constSelectedItems = selectedItems;
 
  192  for ( QTreeWidgetItem *item : constSelectedItems )
 
  194    if ( !( item->flags() & Qt::ItemIsEditable ) )
 
  197    const QString name = item->text( 0 );
 
  206    mTreeWidget->removeItem( item );
 
  208  mTreeWidget->refreshTree();
 
  211void QgsVariableEditorWidget::selectionChanged()
 
  213  if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  215    mRemoveButton->setEnabled( 
false );
 
  220  const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
 
  222  bool removeEnabled = 
true;
 
  223  const auto constSelectedItems = selectedItems;
 
  224  for ( QTreeWidgetItem *item : constSelectedItems )
 
  226    if ( !( item->flags() & Qt::ItemIsEditable ) )
 
  228      removeEnabled = 
false;
 
  232    const QString name = item->text( 0 );
 
  236      removeEnabled = 
false;
 
  242      removeEnabled = 
false;
 
  246  mRemoveButton->setEnabled( removeEnabled );
 
  255QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
 
  256  : QTreeWidget( parent )
 
  259  if ( mExpandIcon.isNull() )
 
  261    QPixmap pix( 14, 14 );
 
  262    pix.fill( Qt::transparent );
 
  263    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
 
  264    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
 
  265    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
 
  266    mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
 
  269  setIconSize( QSize( 18, 18 ) );
 
  271  setHeaderLabels( QStringList() << tr( 
"Variable" ) << tr( 
"Value" ) );
 
  272  setEditTriggers( QAbstractItemView::AllEditTriggers );
 
  273  setRootIsDecorated( 
false );
 
  274  header()->setSectionsMovable( 
false );
 
  275  header()->setSectionResizeMode( QHeaderView::Interactive );
 
  277  mEditorDelegate = 
new VariableEditorDelegate( 
this, 
this );
 
  278  setItemDelegate( mEditorDelegate );
 
  287  const int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
 
  295  else if ( mContext->scopeCount() > contextIndex )
 
  297    return mContext->scope( contextIndex );
 
  307  const int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
 
  308  if ( contextIndex < 0 )
 
  310  return mVariableToItem.value( qMakePair( contextIndex, name ) );
 
  315  if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  320  return mContext->scope( mEditableScopeIndex );
 
  323void QgsVariableEditorTree::refreshTree()
 
  325  if ( !mContext || mEditableScopeIndex < 0 )
 
  333  const auto constScopes = mContext->scopes();
 
  336    refreshScopeItems( scope, scopeIndex );
 
  343  const QColor baseColor = rowColor( scopeIndex );
 
  344  const bool isCurrent = scopeIndex == mEditableScopeIndex;
 
  345  QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
 
  348  for ( 
const QString &name : names )
 
  350    QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
 
  353      item = 
new QTreeWidgetItem( scopeItem );
 
  354      mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
 
  357    const bool readOnly = scope->
isReadOnly( name );
 
  358    bool isActive = 
true;
 
  362      activeScope = mContext->activeScopeForVariable( name );
 
  363      isActive = activeScope == scope;
 
  366    item->setFlags( item->flags() | Qt::ItemIsEnabled );
 
  367    item->setText( 0, name );
 
  368    const QVariant value = scope->
variable( name );
 
  370    item->setText( 1, previewString );
 
  371    QFont font = item->font( 0 );
 
  372    if ( readOnly || !isCurrent )
 
  374      font.setItalic( 
true );
 
  375      item->setFlags( item->flags() ^ Qt::ItemIsEditable );
 
  379      font.setItalic( 
false );
 
  380      item->setFlags( item->flags() | Qt::ItemIsEditable );
 
  385      font.setStrikeOut( 
true );
 
  386      const QString toolTip = tr( 
"Overridden by value from %1" ).arg( activeScope->
name() );
 
  387      item->setToolTip( 0, toolTip );
 
  388      item->setToolTip( 1, toolTip );
 
  392      font.setStrikeOut( 
false );
 
  393      item->setToolTip( 0, name );
 
  394      item->setToolTip( 1, previewString );
 
  396    item->setFont( 0, font );
 
  397    item->setFont( 1, font );
 
  398    item->setData( 0, RowBaseColor, baseColor );
 
  399    item->setData( 0, ContextIndex, scopeIndex );
 
  400    item->setFirstColumnSpanned( 
false );
 
  409  const bool isCurrent = scopeIndex == mEditableScopeIndex;
 
  411  QTreeWidgetItem *scopeItem = 
nullptr;
 
  412  if ( mScopeToItem.contains( scopeIndex ) )
 
  415    scopeItem = mScopeToItem.
value( scopeIndex );
 
  420    scopeItem = 
new QTreeWidgetItem();
 
  421    mScopeToItem.insert( scopeIndex, scopeItem );
 
  422    scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
 
  423    scopeItem->setText( 0, scope->
name() );
 
  424    scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
 
  425    scopeItem->setFirstColumnSpanned( 
true );
 
  426    QFont scopeFont = scopeItem->font( 0 );
 
  427    scopeFont.setBold( 
true );
 
  428    scopeItem->setFont( 0, scopeFont );
 
  429    scopeItem->setFirstColumnSpanned( 
true );
 
  431    addTopLevelItem( scopeItem );
 
  434    if ( isCurrent || settings.
value( 
"QgsVariableEditor/" + scopeItem->text( 0 ) + 
"/expanded" ).toBool() )
 
  435      scopeItem->setExpanded( 
true );
 
  437    scopeItem->setIcon( 0, mExpandIcon );
 
  440  refreshScopeVariables( scope, scopeIndex );
 
  443void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
 
  448  mVariableToItem.remove( mVariableToItem.key( item ) );
 
  449  item->parent()->takeChild( item->parent()->indexOfChild( item ) );
 
  454void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item, 
const QString &name )
 
  459  const int contextIndex = mVariableToItem.key( item ).first;
 
  460  mVariableToItem.remove( mVariableToItem.key( item ) );
 
  461  mVariableToItem.insert( qMakePair( contextIndex, name ), item );
 
  462  item->setText( 0, name );
 
  467void QgsVariableEditorTree::resetTree()
 
  469  mVariableToItem.clear();
 
  470  mScopeToItem.clear();
 
  474void QgsVariableEditorTree::emitChanged()
 
  479void QgsVariableEditorTree::drawRow( QPainter *painter, 
const QStyleOptionViewItem &option, 
const QModelIndex &index )
 const 
  481  QStyleOptionViewItem opt = option;
 
  482  QTreeWidgetItem *item = itemFromIndex( index );
 
  483  if ( index.parent().isValid() )
 
  486    QColor baseColor = item->data( 0, RowBaseColor ).value<QColor>();
 
  487    if ( index.row() % 2 == 1 )
 
  489      baseColor.setAlpha( 59 );
 
  491    painter->fillRect( option.rect, baseColor );
 
  493  QTreeWidget::drawRow( painter, opt, index );
 
  494  const QColor color = 
static_cast<QRgb
>( QApplication::style()->styleHint( QStyle::SH_Table_GridLineColor, &opt ) );
 
  496  painter->setPen( QPen( color ) );
 
  497  painter->drawLine( opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom() );
 
  500QColor QgsVariableEditorTree::rowColor( 
int index )
 const 
  503  const int colorIdx = index % 6;
 
  507      return QColor( 255, 163, 0, 89 );
 
  509      return QColor( 255, 255, 77, 89 );
 
  511      return QColor( 0, 255, 77, 89 );
 
  513      return QColor( 0, 255, 255, 89 );
 
  515      return QColor( 196, 125, 255, 89 );
 
  518      return QColor( 255, 125, 225, 89 );
 
  522void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
 
  527  item->setExpanded( !item->isExpanded() );
 
  531  settings.
setValue( 
"QgsVariableEditor/" + item->text( 0 ) + 
"/expanded", item->isExpanded() );
 
  534void QgsVariableEditorTree::editNext( 
const QModelIndex &index )
 
  536  if ( !index.isValid() )
 
  539  if ( index.column() == 0 )
 
  542    const QModelIndex nextIndex = index.sibling( index.row(), 1 );
 
  543    if ( nextIndex.isValid() )
 
  545      setCurrentIndex( nextIndex );
 
  551    const QModelIndex nextIndex = model()->index( index.row() + 1, 0, index.parent() );
 
  552    if ( nextIndex.isValid() )
 
  555      setCurrentIndex( nextIndex );
 
  565QModelIndex QgsVariableEditorTree::moveCursor( QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers )
 
  567  if ( cursorAction == QAbstractItemView::MoveNext )
 
  569    const QModelIndex index = currentIndex();
 
  570    if ( index.isValid() )
 
  572      if ( index.column() + 1 < model()->columnCount() )
 
  573        return index.sibling( index.row(), index.column() + 1 );
 
  574      else if ( index.row() + 1 < model()->rowCount( index.parent() ) )
 
  575        return index.sibling( index.row() + 1, 0 );
 
  577        return QModelIndex();
 
  580  else if ( cursorAction == QAbstractItemView::MovePrevious )
 
  582    const QModelIndex index = currentIndex();
 
  583    if ( index.isValid() )
 
  585      if ( index.column() >= 1 )
 
  586        return index.sibling( index.row(), index.column() - 1 );
 
  587      else if ( index.row() >= 1 )
 
  588        return index.sibling( index.row() - 1, model()->columnCount() - 1 );
 
  590        return QModelIndex();
 
  594  return QTreeWidget::moveCursor( cursorAction, modifiers );
 
  597void QgsVariableEditorTree::keyPressEvent( QKeyEvent *event )
 
  599  switch ( event->key() )
 
  605      QTreeWidgetItem *item = currentItem();
 
  606      if ( item && !item->parent() )
 
  609        toggleContextExpanded( item );
 
  612      else if ( item && ( item->flags() & Qt::ItemIsEditable ) )
 
  615        editNext( currentIndex() );
 
  624  if ( event == QKeySequence::Copy )
 
  626    const QList<QTreeWidgetItem *> selected = selectedItems();
 
  627    if ( selected.size() > 0 )
 
  629      QString text = selected.at( 0 )->text( 0 );
 
  630      const QString varName = variableNameFromItem( selected.at( 0 ) );
 
  632      if ( !varName.isEmpty() && scope )
 
  633        text = scope->
variable( varName ).toString();
 
  635      QClipboard *clipboard = QApplication::clipboard();
 
  636      clipboard->setText( text );
 
  642  QTreeWidget::keyPressEvent( event );
 
  645void QgsVariableEditorTree::mousePressEvent( QMouseEvent *event )
 
  647  QTreeWidget::mousePressEvent( event );
 
  648  QTreeWidgetItem *item = itemAt( event->pos() );
 
  652  if ( item->parent() )
 
  658  if ( event->pos().x() + header()->offset() > 20 )
 
  664  if ( event->modifiers() & Qt::ShiftModifier )
 
  667    if ( !item->isExpanded() )
 
  678    toggleContextExpanded( item );
 
  686QWidget *VariableEditorDelegate::createEditor( QWidget *parent, 
const QStyleOptionViewItem &, 
const QModelIndex &index )
 const 
  692  if ( !index.parent().isValid() )
 
  695  QTreeWidgetItem *item = mParentTree->indexToItem( index );
 
  697  if ( !item || !scope )
 
  700  const QString variableName = mParentTree->variableNameFromIndex( index );
 
  703  if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
 
  706  QLineEdit *lineEdit = 
new QLineEdit( parent );
 
  707  lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
 
  708  lineEdit->setAutoFillBackground( 
true );
 
  712void VariableEditorDelegate::updateEditorGeometry( QWidget *editor, 
const QStyleOptionViewItem &option, 
const QModelIndex & )
 const 
  714  editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
 
  717QSize VariableEditorDelegate::sizeHint( 
const QStyleOptionViewItem &option, 
const QModelIndex &index )
 const 
  719  return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
 
  722void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model, 
const QModelIndex &index )
 const 
  729  QTreeWidgetItem *item = mParentTree->indexToItem( index );
 
  731  if ( !item || !scope )
 
  734  QLineEdit *lineEdit = qobject_cast<QLineEdit *>( widget );
 
  738  const QString variableName = mParentTree->variableNameFromIndex( index );
 
  739  if ( index.column() == 0 )
 
  742    QString newName = lineEdit->text();
 
  743    newName = newName.trimmed();
 
  744    newName = newName.replace( 
' ', 
'_' );
 
  747    if ( newName == variableName )
 
  754      QMessageBox::warning( mParentTree, tr( 
"Rename Variable" ), tr( 
"A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
 
  755      newName.append( 
"_1" );
 
  758    const QString value = scope->
variable( variableName ).toString();
 
  759    mParentTree->renameItem( item, newName );
 
  762    mParentTree->emitChanged();
 
  764  else if ( index.column() == 1 )
 
  767    const QString value = lineEdit->text();
 
  768    if ( scope->
variable( variableName ).toString() == value )
 
  773    mParentTree->emitChanged();
 
  775  mParentTree->refreshTree();
 
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
 
Single scope for storing variables and functions for use within a QgsExpressionContext.
 
bool hasVariable(const QString &name) const
Tests whether a variable with the specified name exists in the scope.
 
QVariant variable(const QString &name) const
Retrieves a variable's value from the scope.
 
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
 
bool isReadOnly(const QString &name) const
Tests whether the specified variable is read only and should not be editable by users.
 
QString name() const
Returns the friendly display name of the context scope.
 
QStringList filteredVariableNames() const
Returns a filtered and sorted list of variable names contained within the scope.
 
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
 
QStringList variableNames() const
Returns a list of variable names contained within the scope.
 
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
 
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true, int maximumPreviewLength=60)
Formats an expression result for friendly display to the user.
 
Scoped object for saving and restoring a QPainter object's state.
 
Stores settings for use within QGIS.
 
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
 
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.