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();