swift
viewbase.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "gui/views/viewbase.h"
5 
6 #include <QApplication>
7 #include <QClipboard>
8 #include <QFileDialog>
9 #include <QStringBuilder>
10 #include <QTextEdit>
11 
12 #include "config/buildconfig.h"
14 #include "gui/guiutility.h"
15 #include "gui/models/allmodels.h"
18 #include "misc/worker.h"
19 
20 using namespace swift::misc;
21 using namespace swift::gui;
22 using namespace swift::gui::menus;
23 using namespace swift::gui::models;
24 using namespace swift::gui::filters;
25 using namespace swift::gui::settings;
26 using namespace swift::gui::components;
27 
28 namespace swift::gui::views
29 {
30  template <class T>
31  CViewBase<T>::CViewBase(QWidget *parent, ModelClass *model) : CViewBaseNonTemplate(parent), m_model(model)
32  {
33  this->setSortingEnabled(true);
34  if (model) { this->standardInit(model); }
35  }
36 
37  template <class T>
38  int CViewBase<T>::updateContainer(const ContainerType &container, bool sort, bool resize)
39  {
40  Q_ASSERT_X(m_model, Q_FUNC_INFO, "Missing model");
41  if (container.isEmpty())
42  {
43  // shortcut
44  this->clear();
45  return 0;
46  }
47 
48  // we have data
49  this->showLoadIndicator(container.size());
50  const bool reallyResize = resize && isResizeConditionMet(container.size()); // do we really perform resizing
51  bool presize = (m_resizeMode == PresizeSubset) && this->isEmpty() && // only when no data yet
52  !reallyResize; // not when we resize later
53  presize =
54  presize || (this->isEmpty() && resize &&
55  !reallyResize); // we presize if we wanted to resize but actually do not because of condition
56  const bool presizeThresholdReached =
57  presize && container.size() > ResizeSubsetThreshold; // only when size making sense
58 
59  // when we will not resize, we might presize
60  if (presizeThresholdReached)
61  {
62  const int presizeRandomElements = this->getPresizeRandomElementsSize(container.size());
63  if (presizeRandomElements > 0)
64  {
65  m_model->update(container.sampleElements(presizeRandomElements), false);
66  this->fullResizeToContents();
67  }
68  }
69 
70  const int c = m_model->update(container, sort);
71 
72  // resize after real update according to mode
73  if (presizeThresholdReached) // cppcheck-suppress knownConditionTrueFalse
74  {
75  // currently no furhter actions
76  }
77  else if (reallyResize)
78  {
79  this->resizeToContents(); // mode based resize
80  }
81  else if (presize && !presizeThresholdReached) // cppcheck-suppress knownConditionTrueFalse
82  {
83  // small amount of data not covered before
84  this->fullResizeToContents();
85  }
86  this->updateSortIndicator(); // make sure sort indicator represents sort order
87  this->hideLoadIndicator();
88  return c;
89  }
90 
91  template <class T>
92  CWorker *CViewBase<T>::updateContainerAsync(const ContainerType &container, bool sort, bool resize)
93  {
94  // avoid unnecessary effort when empty
95  if (container.isEmpty())
96  {
97  this->clear();
98  return nullptr;
99  }
100 
101  Q_UNUSED(sort)
102  ModelClass *model = this->derivedModel();
103  const auto sortColumn = model->getSortColumn();
104  const auto sortOrder = model->getSortOrder();
105  this->showLoadIndicator(container.size());
106  CWorker *worker = CWorker::fromTask(this, "ViewSort", [model, container, sortColumn, sortOrder]() {
107  return model->sortContainerByColumn(container, sortColumn, sortOrder);
108  });
109  worker->thenWithResult<ContainerType>(this, [this, resize](const ContainerType &sortedContainer) {
110  this->updateContainer(sortedContainer, false, resize);
111  });
112  worker->then(this, &CViewBase::asyncUpdateFinished);
113  return worker;
114  }
115 
116  template <class T>
117  void CViewBase<T>::updateContainerMaybeAsync(const ContainerType &container, bool sort, bool resize)
118  {
119  if (container.isEmpty()) { this->clear(); }
120  else if (container.size() > ASyncRowsCountThreshold && sort)
121  {
122  // larger container with sorting
123  this->updateContainerAsync(container, sort, resize);
124  }
125  else { this->updateContainer(container, sort, resize); }
126  }
127 
128  template <class T>
129  void CViewBase<T>::insert(const ObjectType &value, bool resize)
130  {
131  Q_ASSERT(m_model);
132  if (this->rowCount() < 1)
133  {
134  // this allows presizing
135  this->updateContainerMaybeAsync(ContainerType({ value }), true, resize);
136  }
137  else
138  {
139  m_model->insert(value);
140  if (resize) { this->performModeBasedResizeToContent(); }
141  }
142  }
143 
144  template <class T>
145  void CViewBase<T>::insert(const ContainerType &container, bool resize)
146  {
147  Q_ASSERT(m_model);
148  if (this->rowCount() < 1)
149  {
150  // this allows presizing
151  this->updateContainerMaybeAsync(container, true, resize);
152  }
153  else
154  {
155  m_model->insert(container);
156  if (resize) { this->performModeBasedResizeToContent(); }
157  }
158  }
159 
160  template <class T>
161  void CViewBase<T>::push_back(const ObjectType &value, bool resize)
162  {
163  Q_ASSERT(m_model);
164  if (this->rowCount() < 1)
165  {
166  // this allows presizing
167  this->updateContainerMaybeAsync(ContainerType({ value }), true, resize);
168  }
169  else
170  {
171  m_model->push_back(value);
172  if (resize) { this->performModeBasedResizeToContent(); }
173  }
174  }
175 
176  template <class T>
177  void CViewBase<T>::push_back(const ContainerType &container, bool resize)
178  {
179  Q_ASSERT(m_model);
180  if (this->rowCount() < 1)
181  {
182  // this allows presizing
183  this->updateContainerMaybeAsync(container, true, resize);
184  }
185  else
186  {
187  m_model->push_back(container);
188  if (resize) { this->performModeBasedResizeToContent(); }
189  }
190  }
191 
192  template <class T>
193  const typename CViewBase<T>::ObjectType &CViewBase<T>::at(const QModelIndex &index) const
194  {
195  Q_ASSERT(m_model);
196  return m_model->at(index);
197  }
198 
199  template <class T>
201  {
202  Q_ASSERT(m_model);
203  return m_model->container();
204  }
205 
206  template <class T>
207  QList<int> CViewBase<T>::rowsOf(const ContainerType &container) const
208  {
209  QList<int> rows;
210  for (const ObjectType &o : container)
211  {
212  const int i = this->rowOf(o);
213  if (i >= 0) { rows.push_back(i); }
214  }
215  return rows;
216  }
217 
218  template <class T>
219  int CViewBase<T>::rowOf(const ObjectType &obj) const
220  {
222  const ContainerType objects = m_model->containerOrFilteredContainer();
223  int i = 0;
224  for (const ObjectType &o : objects)
225  {
226  if (o == obj) { return i; }
227  ++i;
228  }
229  return -1;
230  }
231 
232  template <class T>
234  {
235  Q_ASSERT(m_model);
236  return m_model->containerOrFilteredContainer(filtered);
237  }
238 
239  template <class T>
241  {
242  if (!this->hasSelection()) { return ContainerType(); }
243  ContainerType c;
244  const QModelIndexList indexes = this->selectedRows();
245  for (const QModelIndex &i : indexes) { c.push_back(this->at(i)); }
246  return c;
247  }
248 
249  template <class T>
251  {
252  if (!this->hasSelection()) { return this->containerOrFilteredContainer(); }
253  ContainerType c;
254  const QModelIndexList indexes = this->unselectedRows();
255  for (const QModelIndex &i : indexes) { c.push_back(this->at(i)); }
256  return c;
257  }
258 
259  template <class T>
261  {
262  if (this->hasSelection()) { return this->selectedObjects().front(); }
263  if (this->rowCount() < 2) { return this->containerOrFilteredContainer().frontOrDefault(); }
264 
265  // too many, not selected
266  return ObjectType();
267  }
268 
269  template <class T>
271  {
272  if (vm.isEmpty()) { return 0; }
273  if (!hasSelection()) { return 0; }
274  int c = 0;
275 
276  int lastUpdatedRow = -1;
277  int firstUpdatedRow = -1;
278  const CPropertyIndexList propertyIndexes(vm.indexes());
279  const QModelIndexList indexes = this->selectedRows();
280 
281  for (const QModelIndex &i : indexes)
282  {
283  if (i.row() == lastUpdatedRow) { continue; }
284  lastUpdatedRow = i.row();
285  if (firstUpdatedRow < 0 || lastUpdatedRow < firstUpdatedRow) { firstUpdatedRow = lastUpdatedRow; }
286  ObjectType obj(this->at(i));
287 
288  // update all properties in map
289  for (const CPropertyIndex &pi : propertyIndexes) { obj.setPropertyByIndex(pi, vm.value(pi)); }
290 
291  // and update container
292  if (this->derivedModel()->setInContainer(i, obj)) { c++; }
293  }
294 
295  if (c > 0) { this->derivedModel()->emitDataChanged(firstUpdatedRow, lastUpdatedRow); }
296  return c;
297  }
298 
299  template <class T>
300  int CViewBase<T>::updateSelected(const CVariant &variant, const CPropertyIndex &index)
301  {
302  const CPropertyIndexVariantMap vm(index, variant);
303  return this->updateSelected(vm);
304  }
305 
306  template <class T>
308  {
309  const ContainerType c = this->selectedObjects();
310  return c.frontOrDefault();
311  }
312 
313  template <class T>
315  {
316  if (!this->hasSelection()) { return 0; }
317  if (this->isEmpty()) { return 0; }
318 
319  const int currentRows = this->rowCount();
320  const ContainerType selected(this->selectedObjects());
321  const CVariant deletedObjsVariant = CVariant::from(selected);
322  int delta = 0;
323 
324  if (!this->hasFilter() && currentRows == this->selectedRowCount())
325  {
326  // shortcut if all are selected
327  this->clear(); // clear all
328  delta = currentRows;
329  }
330  else
331  {
332  ContainerType unselectedObjects(this->container());
333  unselectedObjects.removeIfInSubset(selected);
334  this->updateContainerMaybeAsync(unselectedObjects);
335  delta = currentRows - unselectedObjects.size();
336  }
337  emit this->objectsDeleted(deletedObjsVariant);
338  return delta;
339  }
340 
341  template <class T>
343  {
344  const int rc = this->rowCount();
345  if (rc > ResizeSubsetThreshold)
346  {
347  const int presizeRandomElements = this->getPresizeRandomElementsSize(rc);
348  if (presizeRandomElements > 0)
349  {
350  const ContainerType containerBackup(this->container());
351  m_model->update(containerBackup.sampleElements(presizeRandomElements), false);
352  this->fullResizeToContents();
353  m_model->update(containerBackup, false);
354  }
355  }
356  else { this->fullResizeToContents(); }
357  }
358 
359  template <class T>
361  {
362  Q_ASSERT(m_model);
363  return m_model->clearHighlighting();
364  }
365 
366  template <class T>
368  {
369  Q_ASSERT(m_model);
370  if (!m_model->hasFilter()) { return; }
371  if (this->isEmpty()) { return; }
372  ContainerType filtered(m_model->containerFiltered());
373  this->removeFilter();
374  this->updateContainerMaybeAsync(filtered);
375  }
376 
377  template <class T>
379  {
380  Q_ASSERT(m_model);
381  m_model->clear();
382  this->hideLoadIndicator();
383  }
384 
385  template <class T>
387  {
388  Q_ASSERT(m_model);
389  return m_model->rowCount();
390  }
391 
392  template <class T>
394  {
395  Q_ASSERT(m_model);
396  return m_model->columnCount(QModelIndex());
397  }
398 
399  template <class T>
401  {
402  Q_ASSERT(m_model);
403  return m_model->rowCount() < 1;
404  }
405 
406  template <class T>
408  {
409  Q_ASSERT(m_model);
410  return m_model->isOrderable();
411  }
412 
413  template <class T>
414  void CViewBase<T>::allowDragDrop(bool allowDrag, bool allowDrop, bool allowDropJsonFile)
415  {
416  Q_ASSERT(m_model);
417 
418  // see model for implementing logic of drag
419  this->viewport()->setAcceptDrops(allowDrop);
420  this->setDragEnabled(allowDrag);
421  this->setDropIndicatorShown(allowDrag || allowDrop);
422  m_model->allowDrop(allowDrop);
423  m_model->allowJsonFileDrop(allowDropJsonFile);
424  }
425 
426  template <class T>
428  {
429  Q_ASSERT(m_model);
430  return m_model->isDropAllowed();
431  }
432 
433  template <class T>
434  void CViewBase<T>::dropEvent(QDropEvent *event)
435  {
436  if (m_model && m_model->isJsonFileDropAllowed() &&
438  {
439  const QFileInfo fi = CGuiUtility::representedMimeFile(event->mimeData());
440  const CStatusMessage msgs = this->loadJsonFile(fi.absoluteFilePath());
441  Q_UNUSED(msgs)
442  return;
443  }
445  }
446 
447  template <class T>
448  bool CViewBase<T>::acceptDrop(const QMimeData *mimeData) const
449  {
450  Q_ASSERT(m_model);
451  const bool a = m_model->acceptDrop(mimeData);
452  return a;
453  }
454 
455  template <class T>
456  bool CViewBase<T>::setSorting(const CPropertyIndex &propertyIndex, Qt::SortOrder order)
457  {
458  Q_ASSERT(m_model);
459  return m_model->setSorting(propertyIndex, order);
460  }
461 
462  template <class T>
463  void CViewBase<T>::sortByPropertyIndex(const CPropertyIndex &propertyIndex, Qt::SortOrder order)
464  {
465  m_model->sortByPropertyIndex(propertyIndex, order);
466  }
467 
468  template <class T>
470  {
471  m_model->sort();
472  }
473 
474  template <class T>
476  {
477  m_model->resort();
478  }
479 
480  template <class T>
481  QJsonObject CViewBase<T>::toJson(bool selectedOnly) const
482  {
483  Q_ASSERT(m_model);
484  return m_model->toJson(selectedOnly);
485  }
486 
487  template <class T>
488  QString CViewBase<T>::toJsonString(QJsonDocument::JsonFormat format, bool selectedOnly) const
489  {
490  Q_ASSERT(m_model);
491  return m_model->toJsonString(format, selectedOnly);
492  }
493 
494  template <class T>
495  void CViewBase<T>::setObjectName(const QString &name)
496  {
497  // then name here is mainly set for debugging purposes so each model can be identified
498  Q_ASSERT(m_model);
499  QTableView::setObjectName(name);
500  m_model->setObjectName(name);
501  }
502 
503  template <class T>
505  {
506  this->derivedModel()->takeFilterOwnership(filter);
507  }
508 
509  template <class T>
511  {
512  this->derivedModel()->removeFilter();
513  }
514 
515  template <class T>
517  {
518  return derivedModel()->hasFilter();
519  }
520 
521  template <class T>
522  void CViewBase<T>::addContainerTypesAsDropTypes(bool objectType, bool containerType)
523  {
524  if (objectType) { m_model->addAcceptedMetaTypeId(qMetaTypeId<ObjectType>()); }
525  if (containerType) { m_model->addAcceptedMetaTypeId(qMetaTypeId<ContainerType>()); }
526  }
527 
528  template <class T>
530  {
531  Q_ASSERT_X(isOrderable(), Q_FUNC_INFO, "Model not orderable");
532  this->allowDragDrop(true, true);
533  this->setDragDropMode(InternalMove);
534  this->setDropActions(Qt::MoveAction);
535  this->addContainerTypesAsDropTypes(true, true);
536  }
537 
538  template <class T>
539  void CViewBase<T>::setTabWidgetViewText(QTabWidget *tw, int index)
540  {
541  if (!tw) { return; }
542  QString o = tw->tabText(index);
543  const QString f = this->hasFilter() ? "F" : "";
544  o = CGuiUtility::replaceTabCountValue(o, this->rowCount()) + f;
545  tw->setTabText(index, o);
546  }
547 
548  template <class T>
550  {
551  const int width = this->width() - 25; // avoid scrollbars etc, reserve a little space
552  QList<int> widths = this->getColumns().calculateWidths(width);
553  if (widths.isEmpty()) { return; }
554  for (int c = 0; c < this->getColumns().size(); c++)
555  {
556  const int w = widths.at(c);
557  this->setColumnWidth(c, w);
558  }
559  }
560 
561  template <class T>
563  {
564  if (m_model->hasValidSortColumn())
565  {
566  Q_ASSERT(this->horizontalHeader());
567  this->horizontalHeader()->setSortIndicator(m_model->getSortColumn(), m_model->getSortOrder());
568  }
569  }
570 
571  template <class T>
573  {
574  Q_ASSERT_X(model || m_model, Q_FUNC_INFO, "Missing model");
575  if (model)
576  {
577  if (model == m_model) { return; }
578  if (m_model) { m_model->disconnect(); }
579 
580  m_model = model;
581  m_model->setSelectionModel(this); // set myself as selection model
582  bool c = connect(m_model, &ModelClass::modelDataChanged, this, &CViewBase::modelDataChanged);
583  Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed");
584  c = connect(m_model, &ModelClass::modelDataChangedDigest, this, &CViewBase::modelDataChangedDigest);
585  Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed");
586  c = connect(m_model, &ModelClass::objectChanged, this, &CViewBase::objectChanged);
587  Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed");
588  c = connect(m_model, &ModelClass::changed, this, &CViewBase::modelChanged);
589  Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed");
590  c = connect(m_model, &ModelClass::changed, this, &CViewBase::onModelChanged);
591  Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed");
592  Q_UNUSED(c)
593  }
594 
595  this->setModel(m_model); // via QTableView
597  this->setSortIndicator();
598  }
599 
600  template <class T>
601  bool CViewBase<T>::reachedResizeThreshold(int containerSize) const
602  {
603  if (containerSize < 0) { return this->rowCount() > m_skipResizeThreshold; }
604  return containerSize > m_skipResizeThreshold;
605  }
606 
607  template <class T>
609  {
610  // small set or large set? This only performs real resizing, no presizing
611  // remark, see also presizeOrFullResizeToContents
612  if (this->isResizeConditionMet()) { this->fullResizeToContents(); }
613  else
614  {
615  m_resizeCount++; // skipped resize
616  }
617  }
618 
619  template <class T>
620  int CViewBase<T>::performUpdateContainer(const swift::misc::CVariant &variant, bool sort, bool resize)
621  {
622  ContainerType c(variant.to<ContainerType>());
623  return this->updateContainer(c, sort, resize);
624  }
625 
626  template <class T>
628  {
629  if (this->derivedModel()->hasValidSortColumn())
630  {
631  const int index = this->derivedModel()->getSortColumn();
632  Qt::SortOrder order = this->derivedModel()->getSortOrder();
633  this->horizontalHeader()->setSortIndicator(index, order);
634  }
635  }
636 
637  template <class T>
638  void CViewBase<T>::mouseOverCallback(const QModelIndex &index, bool mouseOver)
639  {
640  // void
641  Q_UNUSED(index)
642  Q_UNUSED(mouseOver)
643  }
644 
645  template <class T>
646  void CViewBase<T>::drawDropIndicator(bool indicator)
647  {
648  m_dropIndicator = indicator;
649  }
650 
651  template <class T>
652  void CViewBase<T>::selectObjects(const ContainerType &selectedObjects)
653  {
654  Q_UNUSED(selectedObjects)
655  }
656 
657  template <class T>
659  {
660  Q_UNUSED(data)
661  static const CStatusMessage e(this, CStatusMessage::SeverityInfo, u"no modification", true);
662  return e;
663  }
664 
665  template <class T>
667  {
668  Q_UNUSED(data)
669  static const CStatusMessage e(this, CStatusMessage::SeverityInfo, u"validation passed", true);
670  return e;
671  }
672 
673  template <class T>
675  {
676  Q_UNUSED(data)
677  }
678 
679  template <class T>
681  {
683 
684  // Clear highlighting
685  if (this->derivedModel()->hasHighlightedRows())
686  {
687  menuActions.addAction(CIcons::refresh16(), "Clear highlighting", CMenuAction::pathViewClearHighlighting(),
688  nullptr, { this, &CViewBaseNonTemplate::clearHighlighting });
689  }
690  }
691 
692  template <class T>
694  {
696  do {
697  if (fileName.isEmpty())
698  {
699  m = CStatusMessage(this).error(u"Load canceled, no file name");
700  break;
701  }
702 
703  const QString json(CFileUtils::readFileToString(fileName));
704  if (json.isEmpty())
705  {
706  m = CStatusMessage(this).warning(u"Reading '%1' yields no data") << fileName;
707  break;
708  }
709  if (!json::looksLikeSwiftJson(json))
710  {
711  m = CStatusMessage(this).warning(u"No swift JSON '%1'") << fileName;
712  break;
713  }
714 
715  try
716  {
717  const bool allowCacheFormat = this->allowCacheFileFormatJson();
718  const QJsonObject jsonObject = json::jsonObjectFromString(json, allowCacheFormat);
719  if (jsonObject.isEmpty())
720  {
721  m = CStatusMessage(this).warning(u"No valid swift JSON '%1'") << fileName;
722  break;
723  }
724 
725  ContainerType container;
726 
727  if (jsonObject.contains("type") && jsonObject.contains("value"))
728  {
729  // read from variant format
730  CVariant containerVariant;
731  containerVariant.convertFromJson(jsonObject);
732  if (!containerVariant.canConvert<ContainerType>())
733  {
734  m = CStatusMessage(this).warning(u"No valid swift JSON '%1'") << fileName;
735  break;
736  }
737  container = containerVariant.value<ContainerType>();
738  }
739  else
740  {
741  // container format directly
742  container.convertFromJson(jsonObject);
743  }
744 
745  const int countBefore = container.size();
746  m = this->modifyLoadedJsonData(container);
747  if (m.isFailure()) { break; } // modification error
748  if (countBefore > 0 && container.isEmpty()) { break; }
749  m = this->validateLoadedJsonData(container);
750  if (m.isFailure()) { break; } // validaton error
751  this->updateContainerMaybeAsync(container);
752  m = CStatusMessage(this, CStatusMessage::SeverityInfo, "Reading " + fileName + " completed", true);
753  this->jsonLoadedAndModelUpdated(container);
754  this->rememberLastJsonDirectory(fileName);
755  }
756  catch (const CJsonException &ex)
757  {
758  m = CStatusMessage::fromJsonException(ex, this, QString("Reading JSON from '%1'").arg(fileName));
759  break;
760  }
761  }
762  while (false);
763 
764  emit this->jsonLoadCompleted(m);
765  return m;
766  }
767 
768  template <class T>
770  {
771  const ContainerType container = selectedOnly ? this->selectedObjects() : this->container();
772  const QString json = container.toJsonString();
773  QTextEdit *te = this->textEditDialog()->textEdit();
774  te->setReadOnly(true);
775  te->setText(json);
776  this->textEditDialog()->show();
777  }
778 
779  template <class T>
780  CStatusMessage CViewBase<T>::loadJson(const QString &directory)
781  {
782  const QString fileName = QFileDialog::getOpenFileName(
783  nullptr, tr("Load data file"), directory.isEmpty() ? this->getFileDialogFileName(true) : directory,
784  tr("swift (*.json *.txt)"));
785  return this->loadJsonFile(fileName);
786  }
787 
788  template <class T>
789  CStatusMessage CViewBase<T>::saveJson(bool selectedOnly, const QString &directory)
790  {
791  const QString fileName = QFileDialog::getSaveFileName(
792  nullptr, tr("Save data file"), directory.isEmpty() ? this->getFileDialogFileName(false) : directory,
793  tr("swift (*.json *.txt)"));
794  if (fileName.isEmpty()) { return CStatusMessage(this, CStatusMessage::SeverityDebug, u"Save canceled", true); }
795  const QString json(this->toJsonString(QJsonDocument::Indented, selectedOnly)); // save as CVariant JSON
796 
797  // save file
798  CWorker::fromTask(qApp, Q_FUNC_INFO, [=] { CFileUtils::writeStringToFile(json, fileName); });
799  this->rememberLastJsonDirectory(fileName);
800  return CStatusMessage(this, CStatusMessage::SeverityInfo, u"Writing " % fileName % u" in progress", true);
801  }
802 
803  template <class T>
805  {
806  QClipboard *clipboard = QApplication::clipboard();
807  if (!clipboard) { return; }
808  if (!this->hasSelection()) { return; }
809  const ContainerType selection = this->selectedObjects();
810  if (selection.isEmpty()) { return; }
811  const CVariant copyJson = CVariant::from(selection);
812  const QString json = copyJson.toJsonString();
813  clipboard->setText(json);
814  }
815 
816  template <class T>
818  {
819  if (!QApplication::clipboard()) { return; }
820  this->copy();
821  this->removeSelectedRows();
822  }
823 
824  template <class T>
826  {
827  const QClipboard *clipboard = QApplication::clipboard();
828  if (!clipboard) { return; }
829  const QString json = clipboard->text();
830  if (json.isEmpty()) { return; }
831  if (!json::looksLikeSwiftJson(json)) { return; } // no JSON
832  try
833  {
834  ContainerType objects;
835  objects.convertFromJson(json);
836  if (!objects.isEmpty()) { this->insert(objects); }
837  }
838  catch (const CJsonException &ex)
839  {
840  Q_UNUSED(ex)
841  }
842  }
843 
844  template <class T>
846  {
847  const QDialog::DialogCode statusCode = static_cast<QDialog::DialogCode>(status);
848  return filterWidgetChangedFilter(statusCode == QDialog::Accepted);
849  }
850 
851  template <class T>
853  {
854  if (enabled)
855  {
856  if (!m_filterWidget) { this->removeFilter(); }
857  else
858  {
859  // takes the filter and triggers the filtering
861  dynamic_cast<IModelFilterProvider<ContainerType> *>(m_filterWidget);
862  Q_ASSERT_X(provider, Q_FUNC_INFO, "Filter widget does not provide interface");
863  if (!provider) { return false; }
864  std::unique_ptr<IModelFilter<ContainerType>> f(provider->createModelFilter());
865  if (f->isValid()) { this->takeFilterOwnership(f); }
866  else { this->removeFilter(); }
867  }
868  }
869  else
870  {
871  // no filter
872  this->removeFilter();
873  }
874  return true; // handled
875  }
876 
877  template <class T>
878  void CViewBase<T>::onClicked(const QModelIndex &index)
879  {
880  if (!m_acceptClickSelection) { return; }
881  if (!index.isValid()) { return; }
882  emit this->objectClicked(CVariant::fromValue(at(index)));
883  }
884 
885  template <class T>
886  void CViewBase<T>::onDoubleClicked(const QModelIndex &index)
887  {
888  if (!m_acceptDoubleClickSelection) { return; }
889  if (!index.isValid()) { return; }
890  emit this->objectDoubleClicked(CVariant::fromValue(at(index)));
891  }
892 
893  template <class T>
894  void CViewBase<T>::onRowSelected(const QModelIndex &index)
895  {
896  if (!m_acceptRowSelection) { return; }
897  if (!index.isValid()) { return; }
898  emit this->objectSelected(CVariant::fromValue(at(index)));
899  }
900 
901 } // namespace swift::gui::views
static QString replaceTabCountValue(const QString &oldName, int count)
Replace count in name such as "stations (4)".
Definition: guiutility.cpp:522
static bool isMimeRepresentingReadableJsonFile(const QMimeData *mime)
Is representing existing JSON file.
Definition: guiutility.cpp:427
static QFileInfo representedMimeFile(const QMimeData *mime)
Represented file if any.
Definition: guiutility.cpp:410
Bunch of CMenuAction objects.
Definition: menuaction.h:384
CMenuAction addAction(const CMenuAction &menuAction)
Add menu action.
Definition: menuaction.cpp:210
Model filter interface.
Definition: modelfilter.h:21
Model filter interface for those who can generate such a filter (e.g. a widget or dialog)
Definition: modelfilter.h:61
virtual std::unique_ptr< IModelFilter< ContainerType > > createModelFilter() const =0
Get the filter, this is the filter itself, not its widget or dialog.
Base class for views.
Definition: viewbase.h:648
void sort()
Sort if columns or order changed.
Definition: viewbase.cpp:469
virtual ContainerType unselectedObjects() const
Unselected objects.
Definition: viewbase.cpp:250
virtual bool filterDialogFinished(int status)
Filter dialog finished.
Definition: viewbase.cpp:845
int rowOf(const ObjectType &obj) const
The row of the given object.
Definition: viewbase.cpp:219
virtual void cut()
Clipboard cut/copy/paste.
Definition: viewbase.cpp:817
virtual int performUpdateContainer(const swift::misc::CVariant &variant, bool sort, bool resize)
Helper method with template free signature.
Definition: viewbase.cpp:620
void standardInit(ModelClass *model=nullptr)
Standard initialization.
Definition: viewbase.cpp:572
virtual swift::misc::CStatusMessage loadJsonFile(const QString &fileName)
Method creating the menu.
Definition: viewbase.cpp:693
void initAsOrderable()
Init so items can be ordered, includes enabling drag and drop.
Definition: viewbase.cpp:529
virtual bool acceptDrop(const QMimeData *mimeData) const
Accept drop data?
Definition: viewbase.cpp:448
QList< int > rowsOf(const ContainerType &container) const
The rows of the given objects.
Definition: viewbase.cpp:207
virtual void removeFilter()
Remove filter.
Definition: viewbase.cpp:510
virtual void onRowSelected(const QModelIndex &index)
Row selected.
Definition: viewbase.cpp:894
void setTabWidgetViewText(QTabWidget *tw, int index)
Set a tab widget text based on row count, filter etc.
Definition: viewbase.cpp:539
virtual swift::misc::CStatusMessage loadJson(const QString &directory={})
Load JSON.
Definition: viewbase.cpp:780
virtual void allowDragDrop(bool allowDrag, bool allowDrop, bool allowDropJsonFile=false)
Allow to drag and/or drop value objects.
Definition: viewbase.cpp:414
virtual void clearHighlighting()
Clear any highlighted objects.
Definition: viewbase.cpp:360
virtual bool isEmpty() const
Empty?
Definition: viewbase.cpp:400
void updateContainerMaybeAsync(const ContainerType &container, bool sort=true, bool resize=true)
Based on size call sync / async update.
Definition: viewbase.cpp:117
void insert(const ObjectType &value, bool resize=true)
Insert.
Definition: viewbase.cpp:129
virtual ContainerType selectedObjects() const
Selected objects.
Definition: viewbase.cpp:240
virtual swift::misc::CStatusMessage saveJson(bool selectedOnly=false, const QString &directory={})
Save JSON.
Definition: viewbase.cpp:789
void setSortIndicator()
Set the search indicator based on model.
Definition: viewbase.cpp:562
virtual int rowCount() const
Elements in container.
Definition: viewbase.cpp:386
const ObjectType & at(const QModelIndex &index) const
Value object at.
Definition: viewbase.cpp:193
virtual void clear()
Clear data.
Definition: viewbase.cpp:378
typename T::ObjectType ObjectType
Model container element type.
Definition: viewbase.h:660
virtual void sortByPropertyIndex(const swift::misc::CPropertyIndex &propertyIndex, Qt::SortOrder order=Qt::AscendingOrder)
Sort by index.
Definition: viewbase.cpp:463
void takeFilterOwnership(std::unique_ptr< models::IModelFilter< ContainerType >> &filter)
Set filter and take ownership, any previously set filter will be destroyed.
Definition: viewbase.cpp:504
virtual bool filterWidgetChangedFilter(bool enabled)
Filter changed in filter widget.
Definition: viewbase.cpp:852
virtual void setObjectName(const QString &name)
Set own name and the model's name.
Definition: viewbase.cpp:495
void push_back(const ObjectType &value, bool resize=true)
Push back.
Definition: viewbase.cpp:161
int columnCount() const
Column count.
Definition: viewbase.cpp:393
virtual swift::misc::CStatusMessage validateLoadedJsonData(const ContainerType &data) const
Verify JSON data loaded in swift::gui::views::CViewBaseNonTemplate::loadJson.
Definition: viewbase.cpp:666
virtual bool reachedResizeThreshold(int containrerSize=-1) const
Skip resizing because of size?
Definition: viewbase.cpp:601
QString toJsonString(QJsonDocument::JsonFormat format=QJsonDocument::Indented, bool selectedOnly=false) const
Convert to JSON string.
Definition: viewbase.cpp:488
virtual swift::misc::CStatusMessage modifyLoadedJsonData(ContainerType &data) const
Modify JSON data loaded in swift::gui::views::CViewBaseNonTemplate::loadJson.
Definition: viewbase.cpp:658
virtual void selectObjects(const ContainerType &selectedObjects)
Select.
Definition: viewbase.cpp:652
virtual void onDoubleClicked(const QModelIndex &index)
Index double clicked.
Definition: viewbase.cpp:886
void setPercentageColumnWidths()
Set the widths based on the column percentages.
Definition: viewbase.cpp:549
virtual void displayContainerAsJsonPopup(bool selectedOnly)
Display the container as JSON popup.
Definition: viewbase.cpp:769
virtual void performModeBasedResizeToContent()
Perform resizing (no presizing) / non slot method for template.
Definition: viewbase.cpp:608
bool hasFilter() const
Has filter set?
Definition: viewbase.cpp:516
virtual int removeSelectedRows()
Remove selected rows.
Definition: viewbase.cpp:314
virtual bool isDropAllowed() const
Drop allowed?
Definition: viewbase.cpp:427
swift::misc::CWorker * updateContainerAsync(const ContainerType &container, bool sort=true, bool resize=true)
Update whole container in background.
Definition: viewbase.cpp:92
void resort()
Resort ("forced sorting")
Definition: viewbase.cpp:475
virtual void onClicked(const QModelIndex &index)
Remove filter.
Definition: viewbase.cpp:878
virtual void jsonLoadedAndModelUpdated(const ContainerType &data)
In swift::gui::views::CViewBaseNonTemplate::loadJson the view has been updated because of loaded JSON...
Definition: viewbase.cpp:674
ObjectType selectedObject() const
Selected object (or default)
Definition: viewbase.cpp:307
const ContainerType & container() const
Access to container.
Definition: viewbase.cpp:200
typename T::ContainerType ContainerType
Model container type.
Definition: viewbase.h:657
QJsonObject toJson(bool selectedOnly=false) const
Convert to JSON.
Definition: viewbase.cpp:481
virtual void materializeFilter()
Materialize filter.
Definition: viewbase.cpp:367
virtual void presizeOrFullResizeToContents()
Depending on CViewBaseNonTemplate::ResizeSubsetThreshold presize or fully resize.
Definition: viewbase.cpp:342
const ContainerType & containerOrFilteredContainer(bool *filtered=nullptr) const
Full container or cached filtered container as approproiate.
Definition: viewbase.cpp:233
virtual bool setSorting(const swift::misc::CPropertyIndex &propertyIndex, Qt::SortOrder order=Qt::AscendingOrder)
Sorting.
Definition: viewbase.cpp:456
virtual void mouseOverCallback(const QModelIndex &index, bool mouseOver)
From delegate indicating we are in mouse over state.
Definition: viewbase.cpp:638
virtual void drawDropIndicator(bool indicator)
Draw drop indicator.
Definition: viewbase.cpp:646
virtual void copy()
Clipboard cut/copy/paste.
Definition: viewbase.cpp:804
int updateSelected(const swift::misc::CVariant &variant, const swift::misc::CPropertyIndex &index)
Update selected objects.
Definition: viewbase.cpp:300
virtual void updateSortIndicator()
Set the sort indicator to the current sort column.
Definition: viewbase.cpp:627
void addContainerTypesAsDropTypes(bool objectType=true, bool containerType=true)
Add the object and container type as accepted drop types CDropBase::addAcceptedMetaTypeId.
Definition: viewbase.cpp:522
virtual void customMenu(menus::CMenuActions &menuActions)
Method creating the menu.
Definition: viewbase.cpp:680
virtual void dropEvent(QDropEvent *event)
Definition: viewbase.cpp:434
ObjectType firstSelectedOrDefaultObject() const
First selected, the only one, or default.
Definition: viewbase.cpp:260
int updateContainer(const ContainerType &container, bool sort=true, bool resize=true)
Update whole container.
Definition: viewbase.cpp:38
virtual bool isOrderable() const
Is the corresponding model orderable, swift::misc::models::CListModelBaseNonTemplate::isOrderable.
Definition: viewbase.cpp:407
virtual void paste()
Clipboard cut/copy/paste.
Definition: viewbase.cpp:825
Non templated base class, allows Q_OBJECT and signals / slots to be used.
Definition: viewbase.h:87
virtual void clearHighlighting()=0
Clear any highlighted objects.
void modelChanged()
Model bas been changed (means data in view have been changed)
void objectChanged(const swift::misc::CVariant &object, const swift::misc::CPropertyIndex &changedIndex)
Single object was changed in model.
void onModelChanged()
Underlying model changed.
virtual void dropEvent(QDropEvent *event)
void modelDataChangedDigest(int count, bool withFilter)
Model data changed,.
virtual void customMenu(menus::CMenuActions &menuActions)
Method creating the menu.
void asyncUpdateFinished()
Asynchronous update finished.
void modelDataChanged(int count, bool withFilter)
Model data changed.
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Definition: jsonexception.h:24
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Value object encapsulating a list of property indexes.
Specialized value object compliant map for variants, based on indexes.
CVariant value(const CPropertyIndex &index) const
Value.
CPropertyIndexList indexes() const
Indexes.
Streamable status message, e.g.
bool isFailure() const
Operation considered unsuccessful.
Wrapper around QVariant which provides transparent access to CValueObject methods of the contained ob...
Definition: variant.h:66
T to() const
Synonym for value().
Definition: variant.h:176
T value() const
Return the value converted to the type T.
Definition: variant.h:169
void convertFromJson(const QJsonObject &json)
Assign from JSON object.
bool canConvert(int typeId) const
True if this variant can be converted to the type with the given metatype ID.
QString toJsonString(QJsonDocument::JsonFormat format=QJsonDocument::Indented) const
Convenience function JSON as string.
void then(T *context, F functor)
Connects to a functor or method which will be called when the task is finished.
Definition: worker.h:70
Class for doing some arbitrary parcel of work in its own thread.
Definition: worker.h:188
void thenWithResult(F functor)
Connects to a functor to which will be passed the result when the task is finished.
Definition: worker.h:218
QJsonObject jsonObjectFromString(const QString &json, bool acceptCacheFormat)
JSON Object from string.
Definition: json.cpp:413
High level reusable GUI components.
Definition: aboutdialog.cpp:13
Filter to search data sets.
Models to be used with views, mainly QTableView.
Views, mainly QTableView.
GUI related classes.
Free functions in swift::misc.