QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgscheckablecombobox.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscheckablecombobox.cpp
3 ------------------------
4 begin : March 21, 2017
5 copyright : (C) 2017 by Alexander Bruy
6 email : alexander dot bruy at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "moc_qgscheckablecombobox.cpp"
20#include "qgsapplication.h"
21
22#include <QEvent>
23#include <QMouseEvent>
24#include <QLineEdit>
25#include <QPoint>
26#include <QAbstractItemView>
27
28
30 : QStandardItemModel( 0, 1, parent )
31{
32}
33
34Qt::ItemFlags QgsCheckableItemModel::flags( const QModelIndex &index ) const
35{
36 return QStandardItemModel::flags( index ) | Qt::ItemIsUserCheckable;
37}
38
39QVariant QgsCheckableItemModel::data( const QModelIndex &index, int role ) const
40{
41 QVariant value = QStandardItemModel::data( index, role );
42
43 if ( index.isValid() && role == Qt::CheckStateRole && !value.isValid() )
44 {
45 value = Qt::Unchecked;
46 }
47
48 return value;
49}
50
51bool QgsCheckableItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
52{
53 const bool ok = QStandardItemModel::setData( index, value, role );
54
55 if ( ok && role == Qt::CheckStateRole )
56 {
57 emit itemCheckStateChanged( index );
58 }
59
60 emit dataChanged( index, index );
61 return ok;
62}
63
64
66 : QStyledItemDelegate( parent )
67{
68}
69
70void QgsCheckBoxDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
71{
72 QStyleOptionViewItem &nonConstOpt = const_cast<QStyleOptionViewItem &>( option );
73 nonConstOpt.showDecorationSelected = false;
74 QStyledItemDelegate::paint( painter, nonConstOpt, index );
75}
76
77
79 : QComboBox( parent )
80 , mModel( new QgsCheckableItemModel( this ) )
81 , mSeparator( QStringLiteral( ", " ) )
82{
83 setModel( mModel );
84 setItemDelegate( new QgsCheckBoxDelegate( this ) );
85
86 QLineEdit *lineEdit = new QLineEdit( this );
87 lineEdit->setReadOnly( true );
88 QPalette pal = qApp->palette();
89 pal.setBrush( QPalette::Base, pal.button() );
90 lineEdit->setPalette( pal );
91 setLineEdit( lineEdit );
92 lineEdit->installEventFilter( this );
93 lineEdit->setContextMenuPolicy( Qt::CustomContextMenu );
94 connect( lineEdit, &QAbstractItemView::customContextMenuRequested, this, &QgsCheckableComboBox::showContextMenu );
95
96 mContextMenu = new QMenu( this );
97 mSelectAllAction = mContextMenu->addAction( tr( "Select All" ) );
98 mDeselectAllAction = mContextMenu->addAction( tr( "Deselect All" ) );
99 connect( mSelectAllAction, &QAction::triggered, this, &QgsCheckableComboBox::selectAllOptions );
100 connect( mDeselectAllAction, &QAction::triggered, this, &QgsCheckableComboBox::deselectAllOptions );
101
102 view()->viewport()->installEventFilter( this );
103 view()->setContextMenuPolicy( Qt::CustomContextMenu );
104 connect( view(), &QAbstractItemView::customContextMenuRequested, this, &QgsCheckableComboBox::showContextMenu );
105
106 connect( model(), &QStandardItemModel::rowsInserted, this, [ = ]( const QModelIndex &, int, int ) { updateDisplayText(); } );
107 connect( model(), &QStandardItemModel::rowsRemoved, this, [ = ]( const QModelIndex &, int, int ) { updateDisplayText(); } );
108 connect( model(), &QStandardItemModel::dataChanged, this, [ = ]( const QModelIndex &, const QModelIndex &, const QVector< int > & ) { updateDisplayText(); } );
109}
110
112{
113 return mSeparator;
114}
115
116void QgsCheckableComboBox::setSeparator( const QString &separator )
117{
118 if ( mSeparator != separator )
119 {
120 mSeparator = separator;
121 updateDisplayText();
122 }
123}
124
126{
127 return mDefaultText;
128}
129
130void QgsCheckableComboBox::setDefaultText( const QString &text )
131{
132 if ( mDefaultText != text )
133 {
134 mDefaultText = text;
135 updateDisplayText();
136 }
137}
138
139void QgsCheckableComboBox::addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData )
140{
141 QComboBox::addItem( text, userData );
142 setItemCheckState( count() - 1, state );
143}
144
146{
147 QStringList items;
148
149 if ( auto *lModel = model() )
150 {
151 const QModelIndex index = lModel->index( 0, modelColumn(), rootModelIndex() );
152 const QModelIndexList indexes = lModel->match( index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly );
153 const auto constIndexes = indexes;
154 for ( const QModelIndex &index : constIndexes )
155 {
156 items += index.data().toString();
157 }
158 }
159
160 return items;
161}
162
164{
165 QVariantList data;
166
167 if ( auto *lModel = model() )
168 {
169 const QModelIndex index = lModel->index( 0, modelColumn(), rootModelIndex() );
170 const QModelIndexList indexes = lModel->match( index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly );
171 const auto constIndexes = indexes;
172 for ( const QModelIndex &index : constIndexes )
173 {
174 data += index.data( Qt::UserRole ).toString();
175 }
176 }
177
178 return data;
179}
180
181Qt::CheckState QgsCheckableComboBox::itemCheckState( int index ) const
182{
183 return static_cast<Qt::CheckState>( itemData( index, Qt::CheckStateRole ).toInt() );
184}
185
186void QgsCheckableComboBox::setItemCheckState( int index, Qt::CheckState state )
187{
188 setItemData( index, state, Qt::CheckStateRole );
189}
190
192{
193 const QVariant value = itemData( index, Qt::CheckStateRole );
194 if ( value.isValid() )
195 {
196 const Qt::CheckState state = static_cast<Qt::CheckState>( value.toInt() );
197 setItemData( index, ( state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked ), Qt::CheckStateRole );
198 }
199 updateCheckedItems();
200}
201
203{
204 if ( !mSkipHide )
205 {
206 QComboBox::hidePopup();
207 }
208 mSkipHide = false;
209}
210
212{
213 Q_UNUSED( pos )
214
215 mContextMenu->exec( QCursor::pos() );
216}
217
219{
220 blockSignals( true );
221 for ( int i = 0; i < count(); i++ )
222 {
223 setItemData( i, Qt::Checked, Qt::CheckStateRole );
224 }
225 blockSignals( false );
226 updateCheckedItems();
227}
228
230{
231 blockSignals( true );
232 for ( int i = 0; i < count(); i++ )
233 {
234 setItemData( i, Qt::Unchecked, Qt::CheckStateRole );
235 }
236 blockSignals( false );
237 updateCheckedItems();
238}
239
240bool QgsCheckableComboBox::eventFilter( QObject *object, QEvent *event )
241{
242 if ( object == lineEdit() )
243 {
244 if ( event->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent *>( event )->button() == Qt::LeftButton && object == lineEdit() )
245 {
246 mSkipHide = true;
247 showPopup();
248 }
249 }
250 else if ( ( event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease )
251 && object == view()->viewport() )
252 {
253 mSkipHide = true;
254
255 if ( event->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent *>( event )->button() == Qt::RightButton )
256 {
257 return true;
258 }
259
260 if ( event->type() == QEvent::MouseButtonRelease )
261 {
262 const QModelIndex index = view()->indexAt( static_cast<QMouseEvent *>( event )->pos() );
263 if ( index.isValid() )
264 {
265 QgsCheckableItemModel *myModel = qobject_cast<QgsCheckableItemModel *>( model() );
266 QStandardItem *item = myModel->itemFromIndex( index );
267 item->setCheckState( item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked );
268 updateCheckedItems();
269 }
270 return true;
271 }
272 }
273
274 return QComboBox::eventFilter( object, event );
275}
276
277void QgsCheckableComboBox::setCheckedItems( const QStringList &items )
278{
279 const auto constItems = items;
280 for ( const QString &text : constItems )
281 {
282 const int index = findText( text );
283 setItemCheckState( index, index != -1 ? Qt::Checked : Qt::Unchecked );
284 }
285 updateCheckedItems();
286}
287
288void QgsCheckableComboBox::resizeEvent( QResizeEvent *event )
289{
290 QComboBox::resizeEvent( event );
291 updateDisplayText();
292}
293
294void QgsCheckableComboBox::updateCheckedItems()
295{
296 const QStringList items = checkedItems();
297 updateDisplayText();
298 emit checkedItemsChanged( items );
299}
300
301void QgsCheckableComboBox::updateDisplayText()
302{
303 // There is only a line edit if the combobox is in editable state
304 if ( !lineEdit() )
305 return;
306
307 QString text;
308 const QStringList items = checkedItems();
309 if ( items.isEmpty() )
310 {
311 text = mDefaultText;
312 }
313 else
314 {
315 text = items.join( mSeparator );
316 }
317
318 const QRect rect = lineEdit()->rect();
319 const QFontMetrics fontMetrics( font() );
320 text = fontMetrics.elidedText( text, Qt::ElideRight, rect.width() );
321 setEditText( text );
322}
323
QStyledItemDelegate subclass for QgsCheckableComboBox.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Renders the delegate using the given painter and style option for the item specified by index.
QgsCheckBoxDelegate(QObject *parent=nullptr)
Constructor for QgsCheckBoxDelegate.
void setSeparator(const QString &separator)
Set separator used to separate items in the display text.
void setItemCheckState(int index, Qt::CheckState state)
Sets the item check state to state.
void deselectAllOptions()
Removes selection from all items.
void selectAllOptions()
Selects all items.
void checkedItemsChanged(const QStringList &items)
Emitted whenever the checked items list changed.
void setCheckedItems(const QStringList &items)
Set items which should be checked/selected.
void toggleItemCheckState(int index)
Toggles the item check state.
bool eventFilter(QObject *object, QEvent *event) override
Filters events to enable context menu.
QgsCheckableComboBox(QWidget *parent=nullptr)
Constructor for QgsCheckableComboBox.
QVariantList checkedItemsData() const
Returns userData (stored in the Qt::UserRole) associated with currently checked items.
QgsCheckableItemModel * mModel
QgsCheckableItemModel * model() const
Returns the custom item model which handles checking the items.
Qt::CheckState itemCheckState(int index) const
Returns the checked state of the item identified by index.
void addItemWithCheckState(const QString &text, Qt::CheckState state, const QVariant &userData=QVariant())
Adds an item to the combobox with the given text, check state (stored in the Qt::CheckStateRole) and ...
void showContextMenu(QPoint pos)
Display context menu which allows selecting/deselecting all items at once.
void hidePopup() override
Hides the list of items in the combobox if it is currently visible and resets the internal state.
void resizeEvent(QResizeEvent *event) override
Handler for widget resizing.
void setDefaultText(const QString &text)
Set default text which will be displayed in the widget when no items selected.
QStandardItemModel subclass which makes all items checkable by default.
void itemCheckStateChanged(const QModelIndex &index)
Emitted whenever the item's checkstate has changed.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Sets the role data for the item at index to value.
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns a combination of the item flags: items are enabled (ItemIsEnabled), selectable (ItemIsSelecta...
QgsCheckableItemModel(QObject *parent=nullptr)
Constructor for QgsCheckableItemModel.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Returns the data stored under the given role for the item referred to by the index.