LeechCraft  0.6.70-18450-gabe19ee3b0
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 <QStringListModel>
17 #include <QAction>
18 #include <QtDebug>
19 #include "ui_categoryselector.h"
20 #include "util.h"
21 
22 namespace LC::Util
23 {
24  class CategorySelector::SelectorTagsModel : public QStringListModel
25  {
26  CategorySelector& Selector_;
27  QSet<int> SelectedRows_;
28 
29  QString Header_;
30  public:
31  explicit SelectorTagsModel (CategorySelector& selector)
32  : QStringListModel { &selector }
33  , Selector_ { selector }
34  {
35  }
36 
37  QVariant headerData (int section, Qt::Orientation orientation, int role) const override
38  {
39  if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section)
40  return {};
41 
42  return Header_;
43  }
44 
45  Qt::ItemFlags flags (const QModelIndex& index) const override
46  {
47  return (QStringListModel::flags (index) & ~Qt::ItemIsEditable) | Qt::ItemIsUserCheckable;
48  }
49 
50  QVariant data (const QModelIndex& index, int role) const override
51  {
52  if (role == Qt::CheckStateRole)
53  return SelectedRows_.contains (index.row ()) ? Qt::Checked : Qt::Unchecked;
54 
55  return QStringListModel::data (index, role);
56  }
57 
58  bool setData (const QModelIndex& index, const QVariant& value, int role) override
59  {
60  if (role != Qt::CheckStateRole)
61  return false;
62 
63  if (value.value<Qt::CheckState> () == Qt::Checked)
64  SelectedRows_ << index.row ();
65  else
66  SelectedRows_.remove (index.row ());
67  emit dataChanged (index, index, { Qt::CheckStateRole });
68  Selector_.NotifyTagsSelection ();
69  return true;
70  }
71 
72  void SelectAll ()
73  {
74  const int size = stringList ().size ();
75  if (!size)
76  return;
77 
78  SelectedRows_.reserve (size);
79  for (int i = 0; i < size; ++i)
80  SelectedRows_ << i;
81 
82  emit dataChanged (index (0), index (size - 1), { Qt::CheckStateRole });
83 
84  Selector_.NotifyTagsSelection ();
85  }
86 
87  void SelectNone ()
88  {
89  const int size = stringList ().size ();
90  if (!size)
91  return;
92 
93  SelectedRows_.clear ();
94  emit dataChanged (index (0), index (size - 1), { Qt::CheckStateRole });
95 
96  Selector_.NotifyTagsSelection ();
97  }
98 
99  void SetHeader (QString header)
100  {
101  if (header == Header_)
102  return;
103 
104  Header_ = std::move (header);
105  emit headerDataChanged (Qt::Horizontal, 0, 0);
106  }
107  };
108 
110  : QDialog (parent)
111  , Ui_ { new Ui::CategorySelector }
112  , Model_ { *new SelectorTagsModel { *this } }
113  , Separator_ { GetDefaultTagsSeparator () }
114  {
115  setWindowTitle (tr ("Tags selector"));
116  setWindowFlags (Qt::Dialog | Qt::WindowStaysOnTopHint);
117 
118  Ui_->setupUi (this);
119  Ui_->Tree_->setModel (&Model_);
120 
121  const auto& avail = screen ()->availableGeometry ();
122  setMinimumHeight (avail.height () / 3 * 2);
123 
124  const auto all = new QAction (tr ("Select all"), this);
125  all->setProperty ("ActionIcon", "edit-select-all");
126  connect (all,
127  &QAction::triggered,
128  this,
130 
131  const auto none = new QAction (tr ("Select none"), this);
132  none->setProperty ("ActionIcon", "edit-select-none");
133  connect (none,
134  &QAction::triggered,
135  this,
137 
138  Ui_->Tree_->addAction (all);
139  Ui_->Tree_->addAction (none);
140 
141  Ui_->Tree_->setContextMenuPolicy (Qt::ActionsContextMenu);
142 
143  SetButtonsMode (parent ? ButtonsMode::NoButtons : ButtonsMode::Close);
144  }
145 
146  void CategorySelector::SetCaption (const QString& caption)
147  {
148  Model_.SetHeader (caption);
149  }
150 
151  void CategorySelector::SetPossibleSelections (QStringList tags, bool sort)
152  {
153  auto guard = DisableNotifications ();
154 
155  if (sort)
156  tags.sort ();
157  Model_.setStringList (tags);
158  Model_.SelectNone ();
159  }
160 
162  {
163  return Model_.stringList ();
164  }
165 
166  QStringList CategorySelector::GetSelections () const
167  {
168  const auto& allTags = Model_.stringList ();
169  const auto& selectedIdxes = GetSelectedIndexes ();
170  QStringList selected;
171  selected.reserve (selectedIdxes.size ());
172  for (const auto idx : selectedIdxes)
173  selected << allTags [idx];
174  return selected;
175  }
176 
178  {
179  QList<int> result;
180 
181  const auto& rowCount = Model_.stringList ().size ();
182  for (int i = 0; i < rowCount; ++i)
183  {
184  const auto state = Model_.index (i).data (Qt::CheckStateRole).value<Qt::CheckState> ();
185  if (state == Qt::Checked)
186  result << i;
187  }
188 
189  return result;
190  }
191 
192  void CategorySelector::SetSelections (const QStringList& tags)
193  {
194  auto guard = DisableNotifications (false);
195 
196  const auto& allTags = Model_.stringList ();
197  const auto rowCount = allTags.size ();
198  for (int i = 0; i < rowCount; ++i)
199  {
200  const auto state = tags.contains (allTags [i]) ?
201  Qt::Checked :
202  Qt::Unchecked;
203  Model_.setData (Model_.index (i), state, Qt::CheckStateRole);
204  }
205  }
206 
208  {
209  return Separator_;
210  }
211 
212  void CategorySelector::SetSeparator (const QString& sep)
213  {
214  Separator_ = sep;
215  }
216 
218  {
219  switch (mode)
220  {
222  Ui_->ButtonsBox_->setVisible (false);
223  break;
224  case ButtonsMode::Close:
225  Ui_->ButtonsBox_->setStandardButtons (QDialogButtonBox::Close);
226  Ui_->ButtonsBox_->setVisible (true);
227  break;
229  Ui_->ButtonsBox_->setStandardButtons (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
230  Ui_->ButtonsBox_->setVisible (true);
231  break;
232  }
233  }
234 
235  void CategorySelector::moveEvent (QMoveEvent *e)
236  {
237  QWidget::moveEvent (e);
238  QPoint pos = e->pos ();
239  QRect avail = screen ()->availableGeometry ();
240  int dx = 0, dy = 0;
241  if (pos.x () + width () > avail.width ())
242  dx = width () + pos.x () - avail.width ();
243  if (pos.y () + height () > avail.height () &&
244  height () < avail.height ())
245  dy = height () + pos.y () - avail.height ();
246 
247  if (dx || dy)
248  move (pos - QPoint (dx, dy));
249  }
250 
252  {
253  Model_.SelectAll ();
254  }
255 
257  {
258  Model_.SelectNone ();
259  }
260 
261  void CategorySelector::SetSelectionsFromString (const QString& text)
262  {
263  auto guard = DisableNotifications (false);
264  SetSelections (text.split (Separator_, Qt::SkipEmptyParts));
265  }
266 
267  void CategorySelector::NotifyTagsSelection ()
268  {
269  if (NotificationsEnabled_)
271  }
272 
273  DefaultScopeGuard CategorySelector::DisableNotifications (bool reemit)
274  {
275  auto prevValue = NotificationsEnabled_;
276  NotificationsEnabled_ = false;
277  return MakeScopeGuard ([this, prevValue, reemit]
278  {
279  NotificationsEnabled_ = prevValue;
280  if (reemit)
281  NotifyTagsSelection ();
282  });
283  }
284 }
QStringList GetSelections() const
Gets selected items.
CategorySelector(QWidget *parent=nullptr)
Constructor.
auto && sep
Definition: ctstringutils.h:67
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:1091
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