multicombobox.cpp
1 #include "multicombobox.h"
2 #include <QtGui>
3 
4 // internal private editor
5 class MultiComboBoxEditor : public QCheckBox
6 {
7 public:
8  MultiComboBoxEditor(QWidget* parent)
9  : QCheckBox(parent)
10  {
11  }
12 
13 protected:
14  bool hitButton(const QPoint& pos) const
15  {
16  // Omit QCheckBox::hitButton() check as it returns true only if
17  // user actually clicked on the checkbox or on its title. In this
18  // case, we want to detect hits on entire widget, which spans
19  // as a whitespace area covering entire row in combobox' list.
20  return QAbstractButton::hitButton(pos);
21  }
22 };
23 
24 
25 // internal private delegate
26 class MultiComboBoxDelegate : public QItemDelegate
27 {
28 public:
29 
30  MultiComboBoxDelegate(QObject *parent)
31  : QItemDelegate(parent)
32  {
33  }
34 
35  void paint(QPainter *painter, const QStyleOptionViewItem &option,
36  const QModelIndex &index) const
37  {
38  //Get item data
39  bool value = index.data(Qt::UserRole).toBool();
40  QString text = index.data(Qt::DisplayRole).toString();
41 
42  // fill style options with item data
43  const QStyle *style = QApplication::style();
44  QStyleOptionButton opt;
45  opt.state |= value ? QStyle::State_On : QStyle::State_Off;
46  opt.state |= QStyle::State_Enabled;
47  opt.text = text;
48  opt.rect = option.rect;
49 
50  // draw item data as CheckBox
51  style->drawControl(QStyle::CE_CheckBox,&opt,painter);
52  }
53 
54  QWidget *createEditor(QWidget *parent,
55  const QStyleOptionViewItem & option ,
56  const QModelIndex & index ) const
57  {
58  return new MultiComboBoxEditor(parent);
59  }
60 
61  void setEditorData(QWidget *editor, const QModelIndex &index) const
62  {
63  QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
64  myEditor->setText(index.data(Qt::DisplayRole).toString());
65  myEditor->setChecked(index.data(Qt::UserRole).toBool());
66  }
67 
68  void setModelData(QWidget *editor, QAbstractItemModel *model,
69  const QModelIndex &index) const
70  {
71  //get the value from the editor (CheckBox)
72  QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
73  bool value = myEditor->isChecked();
74 
75 
76  //set model data
77  QMap<int,QVariant> data;
78  data.insert(Qt::DisplayRole,myEditor->text());
79  data.insert(Qt::UserRole,value);
80  model->setItemData(index,data);
81  }
82 
83  void updateEditorGeometry(QWidget *editor,
84  const QStyleOptionViewItem &option, const QModelIndex &index ) const
85  {
86  editor->setGeometry(option.rect);
87  }
88 };
89 
90 
91 //min-width:10em;
92 MultiComboBox::MultiComboBox(QWidget *widget )
93 : QComboBox(widget)
94 {
95  view()->setItemDelegate(new MultiComboBoxDelegate(this));
96  // Enable editing on items view
97  view()->setEditTriggers(QAbstractItemView::CurrentChanged);
98  view()->viewport()->installEventFilter(this);
99  view()->setAlternatingRowColors(true);
100 }
101 
102 
103 MultiComboBox::~MultiComboBox()
104 {
105 }
106 
107 QString MultiComboBox::displayText() const
108 {
109  return selectedItemTexts().join(", ");
110 }
111 
112 bool MultiComboBox::eventFilter(QObject *object, QEvent *event)
113 {
114  if (object == view()->viewport())
115  {
116  if (handleViewViewportEvent(event))
117  {
118  return true;
119  }
120  }
121  return QComboBox::eventFilter(object,event);
122 }
123 
124 bool MultiComboBox::handleViewViewportEvent(QEvent* event)
125 {
126  switch (event->type())
127  {
128  case QEvent::Hide:
129  // TODO: Have this react only if value actually changed.
130  emit valueChanged();
131  break;
132 
133  case QEvent::Show:
134  if (count() >= 1)
135  {
136  // Without this, the first object on the list will be
137  // impossible to edit unless other object is hovered
138  // over first, or in case if there's only one object
139  // on the list, the list won't be editable at all.
140  view()->edit(model()->index(0, 0));
141  }
142  break;
143 
144  case QEvent::MouseButtonRelease:
145  // Don't close items view after we release the mouse button
146  // by simple eating MouseButtonRelease in viewport of items
147  // view.
148  return true;
149  }
150  return false;
151 }
152 
153 void MultiComboBox::paintEvent(QPaintEvent *)
154 {
155  QStylePainter painter(this);
156  painter.setPen(palette().color(QPalette::Text));
157 
158  // draw the combobox frame, focusrect and selected etc.
159  QStyleOptionComboBox opt;
160  initStyleOption(&opt);
161 
162  // draw the icon and text
163  opt.currentText = displayText();
164  painter.drawComplexControl(QStyle::CC_ComboBox, opt);
165  painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
166 }
167 
168 QStringList MultiComboBox::selectedItemTexts() const
169 {
170  QStringList result;
171  for (int i = 0; i < count(); ++i)
172  {
173  bool checked = itemData(i).toBool();
174  if (checked)
175  {
176  result << itemText(i);
177  }
178  }
179  return result;
180 }
181 
182 void MultiComboBox::setSelectedTexts(const QStringList& texts)
183 {
184  for (int i = 0; i < count(); ++i)
185  {
186  setItemData(i, static_cast<bool>(texts.contains(itemText(i))));
187  }
188  // Prompt widget repaint or the display text may not change.
189  update();
190 }