LeechCraft  0.6.70-16373-g319c272718
Modular cross-platform feature rich live environment.
categoryselector.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * LeechCraft - modular cross-platform feature rich internet client.
3  * Copyright (C) 2006-2014 Georg Rudoy
4  *
5  * Distributed under the Boost Software License, Version 1.0.
6  * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7  **********************************************************************/
8 
9 #include "categoryselector.h"
10 #include <algorithm>
11 #include <QStringList>
12 #include <QCheckBox>
13 #include <QVariant>
14 #include <QVBoxLayout>
15 #include <QMoveEvent>
16 #include <QApplication>
17 #include <QDesktopWidget>
18 #include <QStringListModel>
19 #include <QAction>
20 #include <QtDebug>
21 #include "ui_categoryselector.h"
22 #include "util.h"
23 
24 namespace LC::Util
25 {
26  class CategorySelector::SelectorTagsModel : public QStringListModel
27  {
28  CategorySelector& Selector_;
29  QSet<int> SelectedRows_;
30 
31  QString Header_;
32  public:
33  explicit SelectorTagsModel (CategorySelector& selector)
34  : QStringListModel { &selector }
35  , Selector_ { selector }
36  {
37  }
38 
39  QVariant headerData (int section, Qt::Orientation orientation, int role) const override
40  {
41  if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section)
42  return {};
43 
44  return Header_;
45  }
46 
47  Qt::ItemFlags flags (const QModelIndex& index) const override
48  {
49  return (QStringListModel::flags (index) & ~Qt::ItemIsEditable) | Qt::ItemIsUserCheckable;
50  }
51 
52  QVariant data (const QModelIndex& index, int role) const override
53  {
54  if (role == Qt::CheckStateRole)
55  return SelectedRows_.contains (index.row ()) ? Qt::Checked : Qt::Unchecked;
56 
57  return QStringListModel::data (index, role);
58  }
59 
60  bool setData (const QModelIndex& index, const QVariant& value, int role) override
61  {
62  if (role != Qt::CheckStateRole)
63  return false;
64 
65  if (value.value<Qt::CheckState> () == Qt::Checked)
66  SelectedRows_ << index.row ();
67  else
68  SelectedRows_.remove (index.row ());
69  emit dataChanged (index, index, { Qt::CheckStateRole });
70  Selector_.NotifyTagsSelection ();
71  return true;
72  }
73 
74  void SelectAll ()
75  {
76  const int size = stringList ().size ();
77  if (!size)
78  return;
79 
80  SelectedRows_.reserve (size);
81  for (int i = 0; i < size; ++i)
82  SelectedRows_ << i;
83 
84  emit dataChanged (index (0), index (size - 1), { Qt::CheckStateRole });
85 
86  Selector_.NotifyTagsSelection ();
87  }
88 
89  void SelectNone ()
90  {
91  const int size = stringList ().size ();
92  if (!size)
93  return;
94 
95  SelectedRows_.clear ();
96  emit dataChanged (index (0), index (size - 1), { Qt::CheckStateRole });
97 
98  Selector_.NotifyTagsSelection ();
99  }
100 
101  void SetHeader (QString header)
102  {
103  if (header == Header_)
104  return;
105 
106  Header_ = std::move (header);
107  emit headerDataChanged (Qt::Horizontal, 0, 0);
108  }
109  };
110 
112  : QDialog (parent)
113  , Ui_ { new Ui::CategorySelector }
114  , Model_ { *new SelectorTagsModel { *this } }
115  , Separator_ { GetDefaultTagsSeparator () }
116  {
117  setWindowTitle (tr ("Tags selector"));
118  setWindowFlags (Qt::Dialog | Qt::WindowStaysOnTopHint);
119 
120  Ui_->setupUi (this);
121  Ui_->Tree_->setModel (&Model_);
122 
123  const auto& avail = QApplication::desktop ()->availableGeometry (this);
124  setMinimumHeight (avail.height () / 3 * 2);
125 
126  const auto all = new QAction (tr ("Select all"), this);
127  all->setProperty ("ActionIcon", "edit-select-all");
128  connect (all,
129  &QAction::triggered,
130  this,
132 
133  const auto none = new QAction (tr ("Select none"), this);
134  none->setProperty ("ActionIcon", "edit-select-none");
135  connect (none,
136  &QAction::triggered,
137  this,
139 
140  Ui_->Tree_->addAction (all);
141  Ui_->Tree_->addAction (none);
142 
143  Ui_->Tree_->setContextMenuPolicy (Qt::ActionsContextMenu);
144 
145  SetButtonsMode (parent ? ButtonsMode::NoButtons : ButtonsMode::Close);
146  }
147 
148  void CategorySelector::SetCaption (const QString& caption)
149  {
150  Model_.SetHeader (caption);
151  }
152 
153  void CategorySelector::SetPossibleSelections (QStringList tags, bool sort)
154  {
155  auto guard = DisableNotifications ();
156 
157  if (sort)
158  tags.sort ();
159  Model_.setStringList (tags);
160  Model_.SelectNone ();
161  }
162 
164  {
165  return Model_.stringList ();
166  }
167 
168  QStringList CategorySelector::GetSelections () const
169  {
170  const auto& allTags = Model_.stringList ();
171  const auto& selectedIdxes = GetSelectedIndexes ();
172  QStringList selected;
173  selected.reserve (selectedIdxes.size ());
174  for (const auto idx : selectedIdxes)
175  selected << allTags [idx];
176  return selected;
177  }
178 
180  {
181  QList<int> result;
182 
183  const auto& rowCount = Model_.stringList ().size ();
184  for (int i = 0; i < rowCount; ++i)
185  {
186  const auto state = Model_.index (i).data (Qt::CheckStateRole).value<Qt::CheckState> ();
187  if (state == Qt::Checked)
188  result << i;
189  }
190 
191  return result;
192  }
193 
194  void CategorySelector::SetSelections (const QStringList& tags)
195  {
196  auto guard = DisableNotifications (false);
197 
198  const auto& allTags = Model_.stringList ();
199  const auto rowCount = allTags.size ();
200  for (int i = 0; i < rowCount; ++i)
201  {
202  const auto state = tags.contains (allTags [i]) ?
203  Qt::Checked :
204  Qt::Unchecked;
205  Model_.setData (Model_.index (i), state, Qt::CheckStateRole);
206  }
207  }
208 
210  {
211  return Separator_;
212  }
213 
214  void CategorySelector::SetSeparator (const QString& sep)
215  {
216  Separator_ = sep;
217  }
218 
220  {
221  switch (mode)
222  {
224  Ui_->ButtonsBox_->setVisible (false);
225  break;
226  case ButtonsMode::Close:
227  Ui_->ButtonsBox_->setStandardButtons (QDialogButtonBox::Close);
228  Ui_->ButtonsBox_->setVisible (true);
229  break;
231  Ui_->ButtonsBox_->setStandardButtons (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
232  Ui_->ButtonsBox_->setVisible (true);
233  break;
234  }
235  }
236 
237  void CategorySelector::moveEvent (QMoveEvent *e)
238  {
239  QWidget::moveEvent (e);
240  QPoint pos = e->pos ();
241  QRect avail = QApplication::desktop ()->availableGeometry (this);
242  int dx = 0, dy = 0;
243  if (pos.x () + width () > avail.width ())
244  dx = width () + pos.x () - avail.width ();
245  if (pos.y () + height () > avail.height () &&
246  height () < avail.height ())
247  dy = height () + pos.y () - avail.height ();
248 
249  if (dx || dy)
250  move (pos - QPoint (dx, dy));
251  }
252 
254  {
255  Model_.SelectAll ();
256  }
257 
259  {
260  Model_.SelectNone ();
261  }
262 
263  void CategorySelector::SetSelectionsFromString (const QString& text)
264  {
265  auto guard = DisableNotifications (false);
266  SetSelections (text.split (Separator_, Qt::SkipEmptyParts));
267  }
268 
269  void CategorySelector::NotifyTagsSelection ()
270  {
271  if (NotificationsEnabled_)
273  }
274 
275  DefaultScopeGuard CategorySelector::DisableNotifications (bool reemit)
276  {
277  auto prevValue = NotificationsEnabled_;
278  NotificationsEnabled_ = false;
279  return MakeScopeGuard ([this, prevValue, reemit]
280  {
281  NotificationsEnabled_ = prevValue;
282  if (reemit)
283  NotifyTagsSelection ();
284  });
285  }
286 }
QStringList GetSelections() const
Gets selected items.
CategorySelector(QWidget *parent=nullptr)
Constructor.
auto && sep
Definition: ctstringutils.h:40
QString GetDefaultTagsSeparator()
Definition: util.cpp:14
void tagsSelectionChanged(const QStringList &newSelections)
Indicates that selections have changed.
detail::ScopeGuard< detail::DefaultScopeGuardDeleter > DefaultScopeGuard
Definition: util.h:132
QList< int > GetSelectedIndexes() const
Gets the indexes of the selected items.
void SetSelectionsFromString(const QString &newText)
Notifies CategorySelector about logical selection changes.
Qt::ItemFlags flags(const QModelIndex &index) const override
void SetSeparator(const QString &)
Sets the separator for the tags.
QString GetSeparator() const
Returns the separator for the tags.
void SetSelections(const QStringList &subset)
Selects some of the items.
void SetButtonsMode(ButtonsMode)
Sets the buttons mode.
void moveEvent(QMoveEvent *) override
Checks whether after the move event the selector won&#39;t be beoynd the screen. if it would...
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Definition: util.h:155
QStringList GetPossibleSelections() const
void SetCaption(const QString &caption)
Sets the caption of this selector.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
constexpr detail::SelectWhole all
Definition: oral.h:956
void SelectAll()
Selects all variants.
The CategorySelector widget provides a way to select amongst a group of items.
void SelectNone()
Deselects all variants.
virtual void SetPossibleSelections(QStringList selections, bool sort=true)
Sets possible selections.
QVariant data(const QModelIndex &index, int role) const override