Chatterino
SignalVectorModel.hpp
Go to the documentation of this file.
1 #pragma once
2 
4 
5 #include <QAbstractTableModel>
6 #include <QMimeData>
7 #include <QStandardItem>
8 #include <boost/optional.hpp>
9 
10 #include <pajlada/signals/signalholder.hpp>
11 
12 namespace chatterino {
13 
14 template <typename TVectorItem>
15 class SignalVectorModel : public QAbstractTableModel,
16  pajlada::Signals::SignalHolder
17 {
18 public:
19  SignalVectorModel(int columnCount, QObject *parent = nullptr)
20  : QAbstractTableModel(parent)
21  , columnCount_(columnCount)
22  {
23  for (int i = 0; i < columnCount; i++)
24  {
25  this->headerData_.emplace_back();
26  }
27  }
28 
30  {
31  this->vector_ = vec;
32 
33  auto insert = [this](const SignalVectorItemEvent<TVectorItem> &args) {
34  if (args.caller == this)
35  {
36  return;
37  }
38  // get row index
39  int index = this->getModelIndexFromVectorIndex(args.index);
40  assert(index >= 0 && index <= this->rows_.size());
41 
42  // get row items
43  std::vector<QStandardItem *> row = this->createRow();
44  this->getRowFromItem(args.item, row);
45 
46  // insert row
47  index = this->beforeInsert(args.item, row, index);
48 
49  this->beginInsertRows(QModelIndex(), index, index);
50  this->rows_.insert(this->rows_.begin() + index,
51  Row(row, args.item));
52  this->endInsertRows();
53  };
54 
55  int i = 0;
56  for (const TVectorItem &item : vec->raw())
57  {
58  SignalVectorItemEvent<TVectorItem> args{item, i++, 0};
59 
60  insert(args);
61  }
62 
63  this->managedConnect(vec->itemInserted, insert);
64 
65  this->managedConnect(vec->itemRemoved, [this](auto args) {
66  if (args.caller == this)
67  {
68  return;
69  }
70 
71  int row = this->getModelIndexFromVectorIndex(args.index);
72  assert(row >= 0 && row <= this->rows_.size());
73 
74  // remove row
75  std::vector<QStandardItem *> items = this->rows_[row].items;
76 
77  this->beginRemoveRows(QModelIndex(), row, row);
78  this->rows_.erase(this->rows_.begin() + row);
79  this->endRemoveRows();
80 
81  this->afterRemoved(args.item, items, row);
82 
83  for (QStandardItem *item : items)
84  {
85  delete item;
86  }
87  });
88 
89  this->afterInit();
90  }
91 
93  {
94  this->initialize(vec);
95  return this;
96  }
97 
99  {
100  for (Row &row : this->rows_)
101  {
102  for (QStandardItem *item : row.items)
103  {
104  delete item;
105  }
106  }
107  }
108 
109  int rowCount(const QModelIndex &parent) const override
110  {
111  (void)parent;
112 
113  return this->rows_.size();
114  }
115 
116  int columnCount(const QModelIndex &parent) const override
117  {
118  (void)parent;
119 
120  return this->columnCount_;
121  }
122 
123  QVariant data(const QModelIndex &index, int role) const override
124  {
125  int row = index.row(), column = index.column();
126  if (row < 0 || column < 0 || row >= this->rows_.size() ||
127  column >= this->columnCount_)
128  {
129  return QVariant();
130  }
131 
132  return rows_[row].items[column]->data(role);
133  }
134 
135  bool setData(const QModelIndex &index, const QVariant &value,
136  int role) override
137  {
138  int row = index.row(), column = index.column();
139  if (row < 0 || column < 0 || row >= this->rows_.size() ||
140  column >= this->columnCount_)
141  {
142  return false;
143  }
144 
145  Row &rowItem = this->rows_[row];
146 
147  assert(this->columnCount_ == rowItem.items.size());
148 
149  auto &cell = rowItem.items[column];
150 
151  cell->setData(value, role);
152 
153  if (rowItem.isCustomRow)
154  {
155  this->customRowSetData(rowItem.items, column, value, role, row);
156  }
157  else
158  {
159  int vecRow = this->getVectorIndexFromModelIndex(row);
160  this->vector_->removeAt(vecRow, this);
161 
162  assert(this->rows_[row].original);
163  TVectorItem item = this->getItemFromRow(
164  this->rows_[row].items, this->rows_[row].original.get());
165  this->vector_->insert(item, vecRow, this);
166  }
167 
168  return true;
169  }
170 
171  QVariant headerData(int section, Qt::Orientation orientation,
172  int role) const override
173  {
174  if (orientation != Qt::Horizontal)
175  {
176  return QVariant();
177  }
178 
179  auto it = this->headerData_[section].find(role);
180  if (it == this->headerData_[section].end())
181  {
182  return QVariant();
183  }
184  else
185  {
186  return it.value();
187  }
188  }
189 
190  bool setHeaderData(int section, Qt::Orientation orientation,
191  const QVariant &value,
192  int role = Qt::DisplayRole) override
193  {
194  if (orientation != Qt::Horizontal)
195  {
196  return false;
197  }
198 
199  this->headerData_[section][role] = value;
200 
201  emit this->headerDataChanged(Qt::Horizontal, section, section);
202  return true;
203  }
204 
205  Qt::ItemFlags flags(const QModelIndex &index) const override
206  {
207  int row = index.row(), column = index.column();
208 
209  if (row < 0 || column < 0 || row >= this->rows_.size() ||
210  column >= this->columnCount_)
211  {
212  return Qt::NoItemFlags;
213  }
214 
215  assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
216  column < this->columnCount_);
217 
218  const auto &rowItem = this->rows_[row];
219 
220  assert(this->columnCount_ == rowItem.items.size());
221 
222  return rowItem.items[column]->flags();
223  }
224 
225  QStandardItem *getItem(int row, int column)
226  {
227  assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
228  column < this->columnCount_);
229 
230  const auto &rowItem = this->rows_[row];
231 
232  assert(this->columnCount_ == rowItem.items.size());
233 
234  return rowItem.items[column];
235  }
236 
237  void deleteRow(int row)
238  {
239  int signalVectorRow = this->getVectorIndexFromModelIndex(row);
240  this->vector_->removeAt(signalVectorRow);
241  }
242 
243  bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
244  const QModelIndex &destinationParent,
245  int destinationChild) override
246  {
247  if (count != 1)
248  {
249  return false;
250  }
251 
252  assert(sourceRow >= 0 && sourceRow < this->rows_.size());
253 
254  int signalVectorRow = this->getVectorIndexFromModelIndex(sourceRow);
255  this->beginMoveRows(sourceParent, sourceRow, sourceRow,
256  destinationParent, destinationChild);
257 
258  TVectorItem item =
259  this->getItemFromRow(this->rows_[sourceRow].items,
260  this->rows_[sourceRow].original.get());
261  this->vector_->removeAt(signalVectorRow);
262  this->vector_->insert(
263  item, this->getVectorIndexFromModelIndex(destinationChild));
264 
265  this->endMoveRows();
266 
267  return true;
268  }
269 
270  bool removeRows(int row, int count, const QModelIndex &parent) override
271  {
272  (void)parent;
273 
274  if (count != 1)
275  {
276  return false;
277  }
278 
279  assert(row >= 0 && row < this->rows_.size());
280 
281  int signalVectorRow = this->getVectorIndexFromModelIndex(row);
282  this->vector_->removeAt(signalVectorRow);
283 
284  return true;
285  }
286 
287  QStringList mimeTypes() const override
288  {
289  return {"chatterino_row_id"};
290  }
291 
292  QMimeData *mimeData(const QModelIndexList &list) const override
293  {
294  if (list.length() == 1)
295  {
296  return nullptr;
297  }
298 
299  // Check if all indices are in the same row -> single row selected
300  for (auto &&x : list)
301  {
302  if (x.row() != list.first().row())
303  return nullptr;
304  }
305 
306  auto data = new QMimeData;
307  data->setData("chatterino_row_id", QByteArray::number(list[0].row()));
308  return data;
309  }
310 
311  bool dropMimeData(const QMimeData *data, Qt::DropAction action, int /*row*/,
312  int /*column*/, const QModelIndex &parent) override
313  {
314  if (data->hasFormat("chatterino_row_id") &&
315  action & (Qt::DropAction::MoveAction | Qt::DropAction::CopyAction))
316  {
317  int from = data->data("chatterino_row_id").toInt();
318  int to = parent.row();
319 
320  int vectorFrom = this->getVectorIndexFromModelIndex(from);
321  int vectorTo = this->getVectorIndexFromModelIndex(to);
322 
323  if (vectorFrom < 0 || vectorFrom > this->vector_->raw().size() ||
324  vectorTo < 0 || vectorTo > this->vector_->raw().size())
325  {
326  return false;
327  }
328 
329  if (from != to)
330  {
331  this->moveRow(this->index(from, to), from, parent, to);
332  }
333 
334  // We return false since we remove items ourselves.
335  return false;
336  }
337 
338  return false;
339  }
340 
341  Qt::DropActions supportedDropActions() const override
342  {
343  return this->vector_->isSorted()
344  ? Qt::DropActions()
345  : Qt::DropAction::CopyAction | Qt::DropAction::MoveAction;
346  }
347 
348 protected:
349  virtual void afterInit()
350  {
351  }
352 
353  // turn a vector item into a model row
354  virtual TVectorItem getItemFromRow(std::vector<QStandardItem *> &row,
355  const TVectorItem &original) = 0;
356 
357  // turns a row in the model into a vector item
358  virtual void getRowFromItem(const TVectorItem &item,
359  std::vector<QStandardItem *> &row) = 0;
360 
361  virtual int beforeInsert(const TVectorItem &item,
362  std::vector<QStandardItem *> &row,
363  int proposedIndex)
364  {
365  (void)item, (void)row;
366 
367  return proposedIndex;
368  }
369 
370  virtual void afterRemoved(const TVectorItem &item,
371  std::vector<QStandardItem *> &row, int index)
372  {
373  (void)item, (void)row, (void)index;
374  }
375 
376  virtual void customRowSetData(const std::vector<QStandardItem *> &row,
377  int column, const QVariant &value, int role,
378  int rowIndex)
379  {
380  (void)row, (void)column, (void)value, (void)role, (void)rowIndex;
381  }
382 
383  void insertCustomRow(std::vector<QStandardItem *> row, int index)
384  {
385  assert(index >= 0 && index <= this->rows_.size());
386 
387  this->beginInsertRows(QModelIndex(), index, index);
388  this->rows_.insert(this->rows_.begin() + index,
389  Row(std::move(row), true));
390  this->endInsertRows();
391  }
392 
393  void removeCustomRow(int index)
394  {
395  assert(index >= 0 && index <= this->rows_.size());
396  assert(this->rows_[index].isCustomRow);
397 
398  this->beginRemoveRows(QModelIndex(), index, index);
399  this->rows_.erase(this->rows_.begin() + index);
400  this->endRemoveRows();
401  }
402 
403  std::vector<QStandardItem *> createRow()
404  {
405  std::vector<QStandardItem *> row;
406  for (int i = 0; i < this->columnCount_; i++)
407  {
408  row.push_back(new QStandardItem());
409  }
410  return row;
411  }
412 
413  struct Row {
414  std::vector<QStandardItem *> items;
415  boost::optional<TVectorItem> original;
417 
418  Row(std::vector<QStandardItem *> _items, bool _isCustomRow = false)
419  : items(std::move(_items))
420  , isCustomRow(_isCustomRow)
421  {
422  }
423 
424  Row(std::vector<QStandardItem *> _items, const TVectorItem &_original,
425  bool _isCustomRow = false)
426  : items(std::move(_items))
427  , original(_original)
428  , isCustomRow(_isCustomRow)
429  {
430  }
431  };
432 
433  const std::vector<Row> &rows() const
434  {
435  return this->rows_;
436  }
437 
438 private:
439  std::vector<QMap<int, QVariant>> headerData_;
440  SignalVector<TVectorItem> *vector_;
441  std::vector<Row> rows_;
442 
443  const int columnCount_;
444 
445  // returns the related index of the SignalVector
446  int getVectorIndexFromModelIndex(int index)
447  {
448  int i = 0;
449 
450  for (auto &row : this->rows_)
451  {
452  if (row.isCustomRow)
453  {
454  index--;
455  continue;
456  }
457 
458  if (i == index)
459  {
460  return i;
461  }
462  i++;
463  }
464 
465  return i;
466  }
467 
468 public:
469  // returns the related index of the model
470  int getModelIndexFromVectorIndex(int vectorIndex) const
471  {
472  int modelIndex = 0;
473 
474  for (auto &row : this->rows())
475  {
476  if (row.isCustomRow)
477  {
478  vectorIndex++;
479  }
480 
481  if (modelIndex == vectorIndex)
482  {
483  return modelIndex;
484  }
485 
486  modelIndex++;
487  }
488 
489  return modelIndex;
490  }
491 };
492 
493 } // namespace chatterino
bool removeRows(int row, int count, const QModelIndex &parent) override
Definition: SignalVectorModel.hpp:270
boost::optional< TVectorItem > original
Definition: SignalVectorModel.hpp:415
QStandardItem * getItem(int row, int column)
Definition: SignalVectorModel.hpp:225
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role=Qt::DisplayRole) override
Definition: SignalVectorModel.hpp:190
void initialize(SignalVector< TVectorItem > *vec)
Definition: SignalVectorModel.hpp:29
pajlada::Signals::Signal< SignalVectorItemEvent< T > > itemInserted
Definition: SignalVector.hpp:24
Definition: SeventvEventAPISubscription.hpp:67
virtual int beforeInsert(const TVectorItem &item, std::vector< QStandardItem *> &row, int proposedIndex)
Definition: SignalVectorModel.hpp:361
pajlada::Signals::Signal< SignalVectorItemEvent< T > > itemRemoved
Definition: SignalVector.hpp:25
bool setData(const QModelIndex &index, const QVariant &value, int role) override
Definition: SignalVectorModel.hpp:135
Definition: Application.cpp:48
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex &parent) override
Definition: SignalVectorModel.hpp:311
virtual TVectorItem getItemFromRow(std::vector< QStandardItem *> &row, const TVectorItem &original)=0
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: SignalVectorModel.hpp:205
void removeAt(int index, void *caller=nullptr)
Definition: SignalVector.hpp:110
const std::vector< Row > & rows() const
Definition: SignalVectorModel.hpp:433
SignalVectorModel(int columnCount, QObject *parent=nullptr)
Definition: SignalVectorModel.hpp:19
void deleteRow(int row)
Definition: SignalVectorModel.hpp:237
QStringList mimeTypes() const override
Definition: SignalVectorModel.hpp:287
void removeCustomRow(int index)
Definition: SignalVectorModel.hpp:393
Definition: SignalVectorModel.hpp:15
Qt::DropActions supportedDropActions() const override
Definition: SignalVectorModel.hpp:341
int getModelIndexFromVectorIndex(int vectorIndex) const
Definition: SignalVectorModel.hpp:470
const std::vector< T > & raw() const
Definition: SignalVector.hpp:124
virtual void getRowFromItem(const TVectorItem &item, std::vector< QStandardItem *> &row)=0
virtual void afterRemoved(const TVectorItem &item, std::vector< QStandardItem *> &row, int index)
Definition: SignalVectorModel.hpp:370
virtual ~SignalVectorModel()
Definition: SignalVectorModel.hpp:98
std::vector< QStandardItem * > items
Definition: SignalVectorModel.hpp:414
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override
Definition: SignalVectorModel.hpp:243
virtual void customRowSetData(const std::vector< QStandardItem *> &row, int column, const QVariant &value, int role, int rowIndex)
Definition: SignalVectorModel.hpp:376
Row(std::vector< QStandardItem *> _items, bool _isCustomRow=false)
Definition: SignalVectorModel.hpp:418
QVariant data(const QModelIndex &index, int role) const override
Definition: SignalVectorModel.hpp:123
void insertCustomRow(std::vector< QStandardItem *> row, int index)
Definition: SignalVectorModel.hpp:383
Definition: SignalVectorModel.hpp:413
SignalVectorModel< TVectorItem > * initialized(SignalVector< TVectorItem > *vec)
Definition: SignalVectorModel.hpp:92
QMimeData * mimeData(const QModelIndexList &list) const override
Definition: SignalVectorModel.hpp:292
int rowCount(const QModelIndex &parent) const override
Definition: SignalVectorModel.hpp:109
std::vector< QStandardItem * > createRow()
Definition: SignalVectorModel.hpp:403
virtual void afterInit()
Definition: SignalVectorModel.hpp:349
Definition: SignalVector.hpp:14
int columnCount(const QModelIndex &parent) const override
Definition: SignalVectorModel.hpp:116
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
Definition: SignalVectorModel.hpp:171
Row(std::vector< QStandardItem *> _items, const TVectorItem &_original, bool _isCustomRow=false)
Definition: SignalVectorModel.hpp:424
bool isSorted() const
Definition: SignalVector.hpp:44
bool isCustomRow
Definition: SignalVectorModel.hpp:416
int insert(const T &item, int index=-1, void *caller=nullptr)
Definition: SignalVector.hpp:65