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