23 #include <QVBoxLayout> 
   24 #include <QTreeWidget> 
   27 #include <QMouseEvent> 
   29 #include <QPushButton> 
   30 #include <QHeaderView> 
   31 #include <QMessageBox> 
   41   QVBoxLayout *verticalLayout = 
new QVBoxLayout( 
this );
 
   42   verticalLayout->setSpacing( 3 );
 
   43   verticalLayout->setContentsMargins( 3, 3, 3, 3 );
 
   44   mTreeWidget = 
new QgsVariableEditorTree( 
this );
 
   45   mTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection );
 
   46   verticalLayout->addWidget( mTreeWidget );
 
   47   QHBoxLayout *horizontalLayout = 
new QHBoxLayout();
 
   48   horizontalLayout->setSpacing( 6 );
 
   49   QSpacerItem *horizontalSpacer = 
new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
 
   50   horizontalLayout->addItem( horizontalSpacer );
 
   51   mAddButton = 
new QPushButton();
 
   53   mAddButton->setEnabled( 
false );
 
   54   mAddButton->setToolTip( tr( 
"Add variable" ) );
 
   55   horizontalLayout->addWidget( mAddButton );
 
   56   mRemoveButton = 
new QPushButton();
 
   58   mRemoveButton->setEnabled( 
false );
 
   59   mRemoveButton->setToolTip( tr( 
"Remove variable" ) );
 
   60   horizontalLayout->addWidget( mRemoveButton );
 
   61   verticalLayout->addLayout( horizontalLayout );
 
   62   connect( mRemoveButton, &QAbstractButton::clicked, 
this, &QgsVariableEditorWidget::mRemoveButton_clicked );
 
   63   connect( mAddButton, &QAbstractButton::clicked, 
this, &QgsVariableEditorWidget::mAddButton_clicked );
 
   64   connect( mTreeWidget, &QTreeWidget::itemSelectionChanged, 
this, &QgsVariableEditorWidget::selectionChanged );
 
   76   settings.
setValue( saveKey() + 
"column0width", mTreeWidget->header()->sectionSize( 0 ) );
 
   91   val = settings.
value( saveKey() + 
"column0width" );
 
   93   const int sectionSize = val.toInt( &ok );
 
   96     mTreeWidget->header()->resizeSection( 0, sectionSize );
 
  100   QWidget::showEvent( event );
 
  111   mTreeWidget->resetTree();
 
  112   mTreeWidget->setContext( mContext.get() );
 
  113   mTreeWidget->refreshTree();
 
  118   mEditableScopeIndex = scopeIndex;
 
  119   if ( mEditableScopeIndex >= 0 )
 
  121     mAddButton->setEnabled( 
true );
 
  123   mTreeWidget->setEditableScopeIndex( scopeIndex );
 
  124   mTreeWidget->refreshTree();
 
  129   if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  133   return mContext->scope( mEditableScopeIndex );
 
  138   QVariantMap variables;
 
  139   if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  146   for ( 
const QString &variable : constVariableNames )
 
  151     variables.insert( variable, scope->
variable( variable ) );
 
  157 QString QgsVariableEditorWidget::saveKey()
 const 
  161   const QString setGroup = mSettingGroup.isEmpty() ? objectName() : mSettingGroup;
 
  162   QString saveKey = 
"/QgsVariableEditorTree/" + setGroup + 
'/';
 
  166 void QgsVariableEditorWidget::mAddButton_clicked()
 
  168   if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  172   scope->
setVariable( QStringLiteral( 
"new_variable" ), QVariant() );
 
  173   mTreeWidget->refreshTree();
 
  174   QTreeWidgetItem *item = mTreeWidget->itemFromVariable( scope, QStringLiteral( 
"new_variable" ) );
 
  175   const QModelIndex index = mTreeWidget->itemToIndex( item );
 
  176   mTreeWidget->selectionModel()->select( index, QItemSelectionModel::ClearAndSelect );
 
  177   mTreeWidget->editItem( item, 0 );
 
  182 void QgsVariableEditorWidget::mRemoveButton_clicked()
 
  184   if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  188   const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
 
  190   const auto constSelectedItems = selectedItems;
 
  191   for ( QTreeWidgetItem *item : constSelectedItems )
 
  193     if ( !( item->flags() & Qt::ItemIsEditable ) )
 
  196     const QString name = item->text( 0 );
 
  205     mTreeWidget->removeItem( item );
 
  207   mTreeWidget->refreshTree();
 
  210 void QgsVariableEditorWidget::selectionChanged()
 
  212   if ( mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  214     mRemoveButton->setEnabled( 
false );
 
  219   const QList<QTreeWidgetItem *> selectedItems = mTreeWidget->selectedItems();
 
  221   bool removeEnabled = 
true;
 
  222   const auto constSelectedItems = selectedItems;
 
  223   for ( QTreeWidgetItem *item : constSelectedItems )
 
  225     if ( !( item->flags() & Qt::ItemIsEditable ) )
 
  227       removeEnabled = 
false;
 
  231     const QString name = item->text( 0 );
 
  235       removeEnabled = 
false;
 
  241       removeEnabled = 
false;
 
  245   mRemoveButton->setEnabled( removeEnabled );
 
  254 QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent )
 
  255   : QTreeWidget( parent )
 
  258   if ( mExpandIcon.isNull() )
 
  260     QPixmap pix( 14, 14 );
 
  261     pix.fill( Qt::transparent );
 
  262     mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::Off );
 
  263     mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconExpandSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::Off );
 
  264     mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Normal, QIcon::On );
 
  265     mExpandIcon.addPixmap( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconCollapseSmall.svg" ) ).pixmap( 14, 14 ), QIcon::Selected, QIcon::On );
 
  268   setIconSize( QSize( 18, 18 ) );
 
  270   setHeaderLabels( QStringList() << tr( 
"Variable" ) << tr( 
"Value" ) );
 
  271   setEditTriggers( QAbstractItemView::AllEditTriggers );
 
  272   setRootIsDecorated( 
false );
 
  273   header()->setSectionsMovable( 
false );
 
  274   header()->setSectionResizeMode( QHeaderView::Interactive );
 
  276   mEditorDelegate = 
new VariableEditorDelegate( 
this, 
this );
 
  277   setItemDelegate( mEditorDelegate );
 
  286   const int contextIndex = item->data( 0, ContextIndex ).toInt( &ok );
 
  294   else if ( mContext->scopeCount() > contextIndex )
 
  296     return mContext->scope( contextIndex );
 
  306   const int contextIndex = mContext ? mContext->indexOfScope( scope ) : 0;
 
  307   if ( contextIndex < 0 )
 
  309   return mVariableToItem.value( qMakePair( contextIndex, name ) );
 
  314   if ( !mContext || mEditableScopeIndex < 0 || mEditableScopeIndex >= mContext->scopeCount() )
 
  319   return mContext->scope( mEditableScopeIndex );
 
  322 void QgsVariableEditorTree::refreshTree()
 
  324   if ( !mContext || mEditableScopeIndex < 0 )
 
  332   const auto constScopes = mContext->scopes();
 
  335     refreshScopeItems( scope, scopeIndex );
 
  342   const QColor baseColor = rowColor( scopeIndex );
 
  343   const bool isCurrent = scopeIndex == mEditableScopeIndex;
 
  344   QTreeWidgetItem *scopeItem = mScopeToItem.value( scopeIndex );
 
  347   for ( 
const QString &name : names )
 
  349     QTreeWidgetItem *item = mVariableToItem.value( qMakePair( scopeIndex, name ) );
 
  352       item = 
new QTreeWidgetItem( scopeItem );
 
  353       mVariableToItem.insert( qMakePair( scopeIndex, name ), item );
 
  356     const bool readOnly = scope->
isReadOnly( name );
 
  357     bool isActive = 
true;
 
  361       activeScope = mContext->activeScopeForVariable( name );
 
  362       isActive = activeScope == scope;
 
  365     item->setFlags( item->flags() | Qt::ItemIsEnabled );
 
  366     item->setText( 0, name );
 
  367     const QVariant value = scope->
variable( name );
 
  369     item->setText( 1, previewString );
 
  370     QFont font = item->font( 0 );
 
  371     if ( readOnly || !isCurrent )
 
  373       font.setItalic( 
true );
 
  374       item->setFlags( item->flags() ^ Qt::ItemIsEditable );
 
  378       font.setItalic( 
false );
 
  379       item->setFlags( item->flags() | Qt::ItemIsEditable );
 
  384       font.setStrikeOut( 
true );
 
  385       const QString toolTip = tr( 
"Overridden by value from %1" ).arg( activeScope->
name() );
 
  386       item->setToolTip( 0, toolTip );
 
  387       item->setToolTip( 1, toolTip );
 
  391       font.setStrikeOut( 
false );
 
  392       item->setToolTip( 0, name );
 
  393       item->setToolTip( 1, previewString );
 
  395     item->setFont( 0, font );
 
  396     item->setFont( 1, font );
 
  397     item->setData( 0, RowBaseColor, baseColor );
 
  398     item->setData( 0, ContextIndex, scopeIndex );
 
  399     item->setFirstColumnSpanned( 
false );
 
  408   const bool isCurrent = scopeIndex == mEditableScopeIndex;
 
  410   QTreeWidgetItem *scopeItem = 
nullptr;
 
  411   if ( mScopeToItem.contains( scopeIndex ) )
 
  414     scopeItem = mScopeToItem.
value( scopeIndex );
 
  419     scopeItem = 
new QTreeWidgetItem();
 
  420     mScopeToItem.insert( scopeIndex, scopeItem );
 
  421     scopeItem->setFlags( scopeItem->flags() | Qt::ItemIsEnabled );
 
  422     scopeItem->setText( 0, scope->
name() );
 
  423     scopeItem->setFlags( scopeItem->flags() ^ Qt::ItemIsEditable );
 
  424     scopeItem->setFirstColumnSpanned( 
true );
 
  425     QFont scopeFont = scopeItem->font( 0 );
 
  426     scopeFont .setBold( 
true );
 
  427     scopeItem->setFont( 0, scopeFont );
 
  428     scopeItem->setFirstColumnSpanned( 
true );
 
  430     addTopLevelItem( scopeItem );
 
  433     if ( isCurrent || settings.
value( 
"QgsVariableEditor/" + scopeItem->text( 0 ) + 
"/expanded" ).toBool() )
 
  434       scopeItem->setExpanded( 
true );
 
  436     scopeItem->setIcon( 0, mExpandIcon );
 
  439   refreshScopeVariables( scope, scopeIndex );
 
  442 void QgsVariableEditorTree::removeItem( QTreeWidgetItem *item )
 
  447   mVariableToItem.remove( mVariableToItem.key( item ) );
 
  448   item->parent()->takeChild( item->parent()->indexOfChild( item ) );
 
  453 void QgsVariableEditorTree::renameItem( QTreeWidgetItem *item, 
const QString &name )
 
  458   const int contextIndex = mVariableToItem.key( item ).first;
 
  459   mVariableToItem.remove( mVariableToItem.key( item ) );
 
  460   mVariableToItem.insert( qMakePair( contextIndex, name ), item );
 
  461   item->setText( 0, name );
 
  466 void QgsVariableEditorTree::resetTree()
 
  468   mVariableToItem.clear();
 
  469   mScopeToItem.clear();
 
  473 void QgsVariableEditorTree::emitChanged()
 
  478 void QgsVariableEditorTree::drawRow( QPainter *painter, 
const QStyleOptionViewItem &option,
 
  479                                      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() );
 
  500 QColor 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 );
 
  522 void QgsVariableEditorTree::toggleContextExpanded( QTreeWidgetItem *item )
 
  527   item->setExpanded( !item->isExpanded() );
 
  531   settings.
setValue( 
"QgsVariableEditor/" + item->text( 0 ) + 
"/expanded", item->isExpanded() );
 
  534 void 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 );
 
  565 QModelIndex 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 );
 
  597 void 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 );
 
  645 void 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 );
 
  686 QWidget *VariableEditorDelegate::createEditor( QWidget *parent,
 
  687     const QStyleOptionViewItem &,
 
  688     const QModelIndex &index )
 const 
  694   if ( !index.parent().isValid() )
 
  697   QTreeWidgetItem *item = mParentTree->indexToItem( index );
 
  699   if ( !item || !scope )
 
  702   const QString variableName = mParentTree->variableNameFromIndex( index );
 
  705   if ( scope != mParentTree->editableScope() || scope->
isReadOnly( variableName ) )
 
  708   QLineEdit *lineEdit = 
new QLineEdit( parent );
 
  709   lineEdit->setText( index.column() == 0 ? variableName : mParentTree->editableScope()->variable( variableName ).toString() );
 
  710   lineEdit->setAutoFillBackground( 
true );
 
  714 void VariableEditorDelegate::updateEditorGeometry( QWidget *editor,
 
  715     const QStyleOptionViewItem &option,
 
  716     const QModelIndex & )
 const 
  718   editor->setGeometry( option.rect.adjusted( 0, 0, 0, -1 ) );
 
  721 QSize VariableEditorDelegate::sizeHint( 
const QStyleOptionViewItem &option,
 
  722                                         const QModelIndex &index )
 const 
  724   return QItemDelegate::sizeHint( option, index ) + QSize( 3, 4 );
 
  727 void VariableEditorDelegate::setModelData( QWidget *widget, QAbstractItemModel *model,
 
  728     const QModelIndex &index )
 const 
  735   QTreeWidgetItem *item = mParentTree->indexToItem( index );
 
  737   if ( !item || !scope )
 
  740   QLineEdit *lineEdit = qobject_cast< QLineEdit * >( widget );
 
  744   const QString variableName = mParentTree->variableNameFromIndex( index );
 
  745   if ( index.column() == 0 )
 
  748     QString newName = lineEdit->text();
 
  749     newName = newName.trimmed();
 
  750     newName = newName.replace( 
' ', 
'_' );
 
  753     if ( newName == variableName )
 
  760       QMessageBox::warning( mParentTree, tr( 
"Rename Variable" ), tr( 
"A variable with the name \"%1\" already exists in this context." ).arg( newName ) );
 
  761       newName.append( 
"_1" );
 
  764     const QString value = scope->
variable( variableName ).toString();
 
  765     mParentTree->renameItem( item, newName );
 
  768     mParentTree->emitChanged();
 
  770   else if ( index.column() == 1 )
 
  773     const QString value = lineEdit->text();
 
  774     if ( scope->
variable( variableName ).toString() == value )
 
  779     mParentTree->emitChanged();
 
  781   mParentTree->refreshTree();