31#include <QDialogButtonBox>
35#include <QListWidgetItem>
40#include <QStackedWidget>
41#include <QStandardItem>
45#include "moc_qgsoptionsdialogbase.cpp"
48 : QDialog( parent, fl )
58 mSettings->setValue( QStringLiteral(
"/Windows/%1/geometry" ).arg(
mOptsKey ), saveGeometry() );
85 if ( title.isEmpty() )
92 if (
auto *lLayout = layout() )
94 lLayout->setContentsMargins( 0, 0, 0, 0 );
98 mOptListWidget = findChild<QListWidget *>( QStringLiteral(
"mOptionsListWidget" ) );
99 mOptTreeView = findChild<QTreeView *>( QStringLiteral(
"mOptionsTreeView" ) );
109 QFrame *optionsFrame = findChild<QFrame *>( QStringLiteral(
"mOptionsFrame" ) );
110 mOptStackedWidget = findChild<QStackedWidget *>( QStringLiteral(
"mOptionsStackedWidget" ) );
111 mOptSplitter = findChild<QSplitter *>( QStringLiteral(
"mOptionsSplitter" ) );
112 mOptButtonBox = findChild<QDialogButtonBox *>( QStringLiteral(
"buttonBox" ) );
113 QFrame *buttonBoxFrame = findChild<QFrame *>( QStringLiteral(
"mButtonBoxFrame" ) );
114 mSearchLineEdit = findChild<QgsFilterLineEdit *>( QStringLiteral(
"mSearchLineEdit" ) );
129 iconSize = size + iconBuffer;
136 optView->setIconSize( QSize( iconSize, iconSize ) );
137 optView->setFrameStyle( QFrame::NoFrame );
140 optionsFrame->layout()->setContentsMargins( 0, frameMargin, frameMargin, frameMargin );
141 QVBoxLayout *layout =
static_cast<QVBoxLayout *
>( optionsFrame->layout() );
143 if ( buttonBoxFrame )
145 buttonBoxFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
146 layout->insertWidget( layout->count(), buttonBoxFrame );
156 disconnect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
157 connect(
mOptButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
158 disconnect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
159 connect(
mOptButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
168 connect(
mOptTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
mOptStackedWidget, [
this](
const QItemSelection &,
const QItemSelection & ) {
169 const QModelIndexList selected = mOptTreeView->selectionModel()->selectedIndexes();
170 if ( selected.isEmpty() )
173 const QModelIndex index = mTreeProxyModel->mapToSource( selected.at( 0 ) );
175 if ( !mOptTreeModel || !mOptTreeModel->itemFromIndex( index )->isSelectable() )
178 mOptStackedWidget->setCurrentIndex( mTreeProxyModel->sourceIndexToPageNumber( index ) );
216 if ( !title.isEmpty() )
227 restoreGeometry(
mSettings->value( QStringLiteral(
"/Windows/%1/geometry" ).arg(
mOptsKey ) ).toByteArray() );
233 optView->setMaximumWidth(
237 optView->setAttribute( Qt::WA_MacShowFocusRect,
false );
249 l->setContentsMargins( 0, 0, 0, 0 );
256 int curIndx =
mSettings->value( QStringLiteral(
"/Windows/%1/tab" ).arg(
mOptsKey ), 0 ).toInt();
277 setListToItemAtIndex( curIndx );
280void QgsOptionsDialogBase::setListToItemAtIndex(
int index )
299 QSizePolicy::Policy policy = QSizePolicy::Ignored;
302 policy = QSizePolicy::MinimumExpanding;
322 if ( currentPage->objectName() == page )
331void QgsOptionsDialogBase::addPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QStringList &path,
const QString &key )
337 QListWidgetItem *item =
new QListWidgetItem();
338 item->setIcon( icon );
339 item->setText( title );
340 item->setToolTip( tooltip );
345 QStandardItem *item =
new QStandardItem( icon, title );
346 item->setToolTip( tooltip );
347 if ( !key.isEmpty() )
349 item->setData( key );
353 QStandardItem *parentItem =
nullptr;
356 QStringList parents = path;
357 while ( !parents.empty() )
359 const QString parentPath = parents.takeFirst();
361 QModelIndex thisParent;
362 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
364 const QModelIndex index =
mOptTreeModel->index( row, 0, parent );
365 if ( index.data().toString().compare( parentPath, Qt::CaseInsensitive ) == 0
366 || index.data( Qt::UserRole + 1 ).toString().compare( parentPath, Qt::CaseInsensitive ) == 0 )
374 if ( !thisParent.isValid() )
376 QStandardItem *newParentItem =
new QStandardItem( parentPath );
377 newParentItem->setToolTip( parentPath );
378 newParentItem->setSelectable(
false );
380 parentItem->appendRow( newParentItem );
383 parentItem = newParentItem;
395 parentItem->appendRow( item );
396 const QModelIndex newIndex =
mOptTreeModel->indexFromItem( item );
404 scrollArea->setWidgetResizable(
true );
405 scrollArea->setFrameShape( QFrame::NoFrame );
406 scrollArea->setObjectName( widget->objectName() );
407 scrollArea->setWidget( widget );
415void QgsOptionsDialogBase::insertPage(
const QString &title,
const QString &tooltip,
const QIcon &icon, QWidget *widget,
const QString &before,
const QStringList &path,
const QString &key )
421 if ( currentPage->objectName() == before )
427 QListWidgetItem *item =
new QListWidgetItem();
428 item->setIcon( icon );
429 item->setText( title );
430 item->setToolTip( tooltip );
435 QModelIndex sourceIndexBefore =
mTreeProxyModel->pageNumberToSourceIndex( page );
436 QList<QModelIndex> sourceBeforeIndices;
437 while ( sourceIndexBefore.parent().isValid() )
439 sourceBeforeIndices.insert( 0, sourceIndexBefore );
440 sourceIndexBefore = sourceIndexBefore.parent();
442 sourceBeforeIndices.insert( 0, sourceIndexBefore );
444 QStringList parentPaths = path;
446 QModelIndex parentIndex;
447 QStandardItem *parentItem =
nullptr;
448 while ( !parentPaths.empty() )
450 QString thisPath = parentPaths.takeFirst();
451 QModelIndex sourceIndex = !sourceBeforeIndices.isEmpty() ? sourceBeforeIndices.takeFirst() : QModelIndex();
453 if ( sourceIndex.data().toString().compare( thisPath, Qt::CaseInsensitive ) == 0
454 || sourceIndex.data( Qt::UserRole + 1 ).toString().compare( thisPath, Qt::CaseInsensitive ) == 0 )
456 parentIndex = sourceIndex;
461 QStandardItem *newParentItem =
new QStandardItem( thisPath );
462 newParentItem->setToolTip( thisPath );
463 newParentItem->setSelectable(
false );
464 if ( sourceIndex.isValid() )
468 parentItem->insertRow( sourceIndex.row(), newParentItem );
470 mOptTreeModel->insertRow( sourceIndex.row(), newParentItem );
476 parentItem->appendRow( newParentItem );
480 parentItem = newParentItem;
484 QStandardItem *item =
new QStandardItem( icon, title );
485 item->setToolTip( tooltip );
486 if ( !key.isEmpty() )
488 item->setData( key );
492 if ( sourceBeforeIndices.empty() )
493 parentItem->appendRow( item );
496 parentItem->insertRow( sourceBeforeIndices.at( 0 ).row(), item );
506 scrollArea->setWidgetResizable(
true );
507 scrollArea->setFrameShape( QFrame::NoFrame );
508 scrollArea->setWidget( widget );
509 scrollArea->setObjectName( widget->objectName() );
516 addPage( title, tooltip, icon, widget, path );
521 const int minimumTextLength = 3;
538 if (
mOptListWidget->item( r )->text().contains( text, Qt::CaseInsensitive ) )
544 mOptListWidget->setRowHidden( r, text.length() >= minimumTextLength );
550 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
558 QMap<int, bool> hiddenPages;
561 hiddenPages.insert( r, text.length() >= minimumTextLength );
564 std::function<void(
const QModelIndex & )> traverseModel;
566 traverseModel = [&](
const QModelIndex &parent ) {
567 for (
int row = 0; row <
mOptTreeModel->rowCount( parent ); ++row )
569 const QModelIndex currentIndex =
mOptTreeModel->index( row, 0, parent );
570 if ( currentIndex.data().toString().contains( text, Qt::CaseInsensitive ) )
572 hiddenPages.insert(
mTreeProxyModel->sourceIndexToPageNumber( currentIndex ),
false );
574 traverseModel( currentIndex );
577 traverseModel( QModelIndex() );
581 if ( rsw.first->searchHighlight( text.length() >= minimumTextLength ? text : QString() ) )
583 hiddenPages.insert( rsw.second,
false );
586 for (
auto it = hiddenPages.constBegin(); it != hiddenPages.constEnd(); ++it )
591 if (
mOptTreeView && text.length() >= minimumTextLength )
617 if ( !
mTreeProxyModel->filterAcceptsRow( currentSourceIndex.row(), currentSourceIndex.parent() ) )
619 std::function<QModelIndex(
const QModelIndex & )> traverseModel;
620 traverseModel = [&](
const QModelIndex &parent ) -> QModelIndex {
623 const QModelIndex proxyIndex =
mTreeProxyModel->index( row, 0, parent );
624 const QModelIndex sourceIndex =
mTreeProxyModel->mapToSource( proxyIndex );
625 if (
mOptTreeModel->itemFromIndex( sourceIndex )->isSelectable() )
631 QModelIndex res = traverseModel( proxyIndex );
636 return QModelIndex();
639 const QModelIndex firstVisibleSourceIndex = traverseModel( QModelIndex() );
641 if ( firstVisibleSourceIndex.isValid() )
667 const QList<QWidget *> widgets =
mOptStackedWidget->widget( i )->findChildren<QWidget *>();
668 for ( QWidget *widget : widgets )
675 QHash<QWidget *, QgsOptionsDialogHighlightWidget *> customHighlightWidgets;
682 if ( customHighlightWidgets.contains( widget ) )
684 shw = customHighlightWidgets.value( widget );
694 QgsDebugMsgLevel( QStringLiteral(
"Registering: %1" ).arg( widget->objectName() ), 4 );
708 res->setToolTip( tooltip );
736 QDialog::showEvent( e );
744 QDialog::paintEvent( e );
752 if ( !itemText.isEmpty() )
754 setWindowTitle( QStringLiteral(
"%1 %2 %3" )
756 .arg( QChar( 0x2014 ) )
773 if ( optView->maximumWidth() != 16777215 )
774 optView->setMaximumWidth( 16777215 );
778 int iconWidth = optView->iconSize().width();
779 int snapToIconWidth = iconWidth + 32;
782 mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
785 int newWidth = optView->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
786 bool diffWidth = optView->minimumWidth() != newWidth;
789 optView->setMinimumWidth( newWidth );
791 if (
mIconOnly && ( diffWidth || optView->width() != newWidth ) )
793 splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
794 splitSizes[0] = newWidth;
841 if ( ( *it ).second == index )
850 QMessageBox::warning(
nullptr, tr(
"Missing Objects" ), tr(
"Base options dialog could not be initialized.\n\n"
851 "Missing some of the .ui template objects:\n" )
852 +
" mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter,\n mOptionsListFrame",
853 QMessageBox::Ok, QMessageBox::Ok );
858QgsOptionsProxyModel::QgsOptionsProxyModel( QObject *parent )
859 : QSortFilterProxyModel( parent )
861 setDynamicSortFilter(
true );
864void QgsOptionsProxyModel::setPageHidden(
int page,
bool hidden )
866 mHiddenPages[page] = hidden;
870QModelIndex QgsOptionsProxyModel::pageNumberToSourceIndex(
int page )
const
872 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
874 return QModelIndex();
876 int pagesRemaining = page;
877 std::function<QModelIndex(
const QModelIndex & )> traversePages;
880 traversePages = [&](
const QModelIndex &parent ) -> QModelIndex {
881 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
883 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
884 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
886 if ( pagesRemaining == 0 )
893 const QModelIndex res = traversePages( currentIndex );
897 return QModelIndex();
900 return traversePages( QModelIndex() );
903int QgsOptionsProxyModel::sourceIndexToPageNumber(
const QModelIndex &index )
const
905 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
911 std::function<int(
const QModelIndex & )> traverseModel;
914 traverseModel = [&](
const QModelIndex &parent ) ->
int {
915 for (
int row = 0; row < itemModel->rowCount( parent ); ++row )
917 const QModelIndex currentIndex = itemModel->index( row, 0, parent );
918 if ( currentIndex == index )
921 if ( itemModel->itemFromIndex( currentIndex )->isSelectable() )
924 const int res = traverseModel( currentIndex );
931 return traverseModel( QModelIndex() );
934bool QgsOptionsProxyModel::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent )
const
936 QStandardItemModel *itemModel = qobject_cast<QStandardItemModel *>( sourceModel() );
940 const QModelIndex sourceIndex = sourceModel()->index( source_row, 0, source_parent );
942 const int pageNumber = sourceIndexToPageNumber( sourceIndex );
943 if ( !mHiddenPages.value( pageNumber,
false ) )
946 if ( sourceModel()->hasChildren( sourceIndex ) )
949 for (
int row = 0; row < sourceModel()->rowCount( sourceIndex ); ++row )
951 if ( filterAcceptsRow( row, sourceIndex ) )
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
void cleared()
Emitted when the widget is cleared.
virtual void updateWindowTitle()
QPointer< QgsSettings > mSettings
void warnAboutMissingObjects()
void resizeAlltabs(int index)
Resizes all tabs when the dialog is resized.
void paintEvent(QPaintEvent *e) override
void restoreLastPage()
Refocus the active tab from the last time the dialog was shown.
QList< QPair< QgsOptionsDialogHighlightWidget *, int > > mRegisteredSearchWidgets
void searchText(const QString &text)
searchText searches for a text in all the pages of the stacked widget and highlight the results
void registerTextSearchWidgets()
register widgets in the dialog to search for text in it it is automatically called if a line edit has...
virtual void optionsStackedWidget_CurrentChanged(int index)
Select relevant tab on current page change.
~QgsOptionsDialogBase() override
QgsOptionsDialogBase(const QString &settingsKey, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags(), QgsSettings *settings=nullptr)
Constructor.
QgsFilterLineEdit * mSearchLineEdit
void setSettings(QgsSettings *settings)
virtual void optionsStackedWidget_WidgetRemoved(int index)
Remove tab and unregister widgets on page remove.
QDialogButtonBox * mOptButtonBox
QgsOptionsProxyModel * mTreeProxyModel
void addPage(const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QStringList &path=QStringList(), const QString &key=QString())
Adds a new page to the dialog pages.
QStandardItemModel * mOptTreeModel
QStandardItem * createItem(const QString &name, const QString &tooltip, const QString &icon)
Creates a new QStandardItem with the specified name, tooltip and icon.
virtual void updateOptionsListVerticalTabs()
Update tabs on the splitter move.
QListWidget * mOptListWidget
void restoreOptionsBaseUi(const QString &title=QString())
Restore the base ui.
QStackedWidget * mOptStackedWidget
void initOptionsBase(bool restoreUi=true, const QString &title=QString())
Set up the base ui connections for vertical tabs.
void showEvent(QShowEvent *e) override
void insertPage(const QString &title, const QString &tooltip, const QIcon &icon, QWidget *widget, const QString &before, const QStringList &path=QStringList(), const QString &key=QString())
Inserts a new page into the dialog pages.
void setCurrentPage(const QString &page)
Sets the dialog page (by object name) to show.
Base class for widgets for pages included in the options dialog.
QHash< QWidget *, QgsOptionsDialogHighlightWidget * > registeredHighlightWidgets()
Returns the registered highlight widgets used to search and highlight text in options dialogs.
Stores settings for use within QGIS.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define QgsDebugMsgLevel(str, level)