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