swift
viewbasenontemplate.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include <algorithm>
5 
6 #include <QAction>
7 #include <QApplication>
8 #include <QMetaMethod>
9 #include <QShortcut>
10 
11 #include "config/buildconfig.h"
13 #include "gui/dockwidgetinfoarea.h"
16 #include "gui/guiapplication.h"
17 #include "gui/guiutility.h"
18 #include "gui/loadindicator.h"
19 #include "gui/menus/fontmenus.h"
20 #include "gui/menus/menudelegate.h"
21 #include "gui/shortcut.h"
22 #include "gui/views/viewbase.h"
23 #include "misc/logmessage.h"
24 
25 using namespace swift::config;
26 using namespace swift::misc;
27 using namespace swift::gui;
28 using namespace swift::gui::menus;
29 using namespace swift::gui::models;
30 using namespace swift::gui::filters;
31 using namespace swift::gui::settings;
32 using namespace swift::gui::components;
33 
34 namespace swift::gui::views
35 {
36  CViewBaseNonTemplate::CViewBaseNonTemplate(QWidget *parent) : COverlayMessagesTableView(parent)
37  {
39  connect(this, &QWidget::customContextMenuRequested, this, &CViewBaseNonTemplate::customMenuRequested);
43 
44  // setting resize mode rowsResizeModeToContent() causes extremly slow views
45  // default, see: m_rowResizeMode
46 
47  // scroll modes
50  this->setWordWrap(false);
52 
53  // shortcuts
54  auto *filter = new QShortcut(CShortcut::keyDisplayFilter(), this);
56  Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut");
57  filter->setObjectName("Filter shortcut for " + this->objectName());
58  filter->setContext(Qt::WidgetShortcut);
59 
62  Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut");
63  clearSelection->setObjectName("Clear selection shortcut for " + this->objectName());
65 
66  auto *saveJson = new QShortcut(CShortcut::keySaveViews(), this);
68  Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut");
69  saveJson->setObjectName("Save JSON for " + this->objectName());
70  saveJson->setContext(Qt::WidgetShortcut);
71 
72  auto *deleteRow = new QShortcut(CShortcut::keyDelete(), this);
73  s = connect(deleteRow, &QShortcut::activated, this, &CViewBaseNonTemplate::removeSelectedRowsChecked);
74  Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut");
75  deleteRow->setObjectName("Remove selected rows for " + this->objectName());
76  deleteRow->setContext(Qt::WidgetShortcut);
77 
78  auto *copy = new QShortcut(CShortcut::keyCopy(), this);
80  Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut");
81  copy->setObjectName("Copy selection shortcut for " + this->objectName());
82  copy->setContext(Qt::WidgetShortcut);
83 
84  auto *resize = new QShortcut(CShortcut::keyResizeView(), this);
86  Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut");
87  resize->setObjectName("Resize view shortcut for " + this->objectName());
88  resize->setContext(Qt::WidgetShortcut);
89  }
90 
92 
94  {
95  // further init could go here
96  const bool c = CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget);
97  return c;
98  }
99 
101 
102  void CViewBaseNonTemplate::setFilterWidgetImpl(QWidget *filterWidget)
103  {
104  if (filterWidget == m_filterWidget) { return; }
105 
106  // dialog or filter widget
107  if (m_filterWidget)
108  {
111  if (m_filterWidget->parent() == this) { m_filterWidget->deleteLater(); }
112  m_filterWidget = nullptr;
113  }
114 
115  if (filterWidget)
116  {
117  this->menuAddItems(MenuFilter);
118  m_filterWidget = filterWidget;
119  }
120  }
121 
123  {
124  if (filterDialog == m_filterWidget) { return; }
125  this->setFilterWidgetImpl(filterDialog);
126  if (filterDialog)
127  {
128  const bool s =
129  connect(filterDialog, &CFilterDialog::finished, this, &CViewBaseNonTemplate::filterDialogFinished);
130  Q_ASSERT_X(s, Q_FUNC_INFO, "filter dialog connect");
131  Q_UNUSED(s);
132  }
133  }
134 
136  {
137  if (filterWidget == m_filterWidget) { return; }
138  this->setFilterWidgetImpl(filterWidget);
139  if (filterWidget)
140  {
141  bool s = connect(filterWidget, &CFilterWidget::changeFilter, this,
143  Q_ASSERT_X(s, Q_FUNC_INFO, "filter connect changeFilter");
144  s = connect(this, &CViewBaseNonTemplate::modelDataChanged, filterWidget, &CFilterWidget::onRowCountChanged,
146  Q_ASSERT_X(s, Q_FUNC_INFO, "filter connect modelDataChanged");
147  Q_UNUSED(s);
148  }
149  }
150 
152 
154  {
156  }
157 
159  {
160  if (this->selectionModel()) { disconnect(this->selectionModel()); }
162  if (this->selectionModel())
163  {
166  }
167  }
168 
170 
172  {
173  return this->loadJson(directory);
174  }
175 
177  {
178  return this->saveJson(selectedOnly, directory);
179  }
180 
182  {
183  this->horizontalHeader()->setSectionResizeMode(mode);
184  }
185 
187  {
188  if (menu && nestPreviousMenu)
189  {
190  // new menu with nesting
191  menu->setNestedDelegate(m_menu);
192  m_menu = menu;
193  }
194  else if (!menu && nestPreviousMenu)
195  {
196  // nested new menu
198  }
199  else
200  {
201  // no nesting
202  m_menu = menu;
203  }
204  return menu;
205  }
206 
208  {
209  if (m_menuFlagActions.contains(menu)) { return m_menuFlagActions.value(menu); }
210 
211  CMenuActions ma;
212  switch (menu)
213  {
214  case MenuRefresh:
215  {
217  if (!this->isSignalConnected(requestSignal)) break;
218  ma.addAction(CIcons::refresh16(), "Update", CMenuAction::pathViewUpdates(),
220  break;
221  }
222  case MenuBackend:
223  {
224  static const QMetaMethod requestSignal =
226  if (!this->isSignalConnected(requestSignal)) break;
227  ma.addAction(CIcons::refresh16(), "Reload from backend", CMenuAction::pathViewUpdates(),
229  break;
230  }
232  {
233  QAction *a =
234  ma.addAction(CIcons::appMappings16(), "Automatically display (when loaded)",
235  CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::toggleAutoDisplay });
236  a->setCheckable(true);
237  a->setChecked(this->displayAutomatically());
238  break;
239  }
241  {
242  ma.addAction(CIcons::delete16(), "Remove selected rows", CMenuAction::pathViewAddRemove(),
243  { this, &CViewBaseNonTemplate::removeSelectedRowsChecked }, CShortcut::keyDelete());
244  break;
245  }
246  case MenuClear:
247  {
248  ma.addAction(CIcons::delete16(), "Clear", CMenuAction::pathViewAddRemove(),
249  { this, &CViewBaseNonTemplate::clear });
250  break;
251  }
252  case MenuFilter:
253  {
254  if (m_filterWidget)
255  {
256  const bool dialog = qobject_cast<QDialog *>(m_filterWidget);
257  if (dialog)
258  ma.addAction(CIcons::filter16(),
260  CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::displayFilterDialog },
262  ma.addAction(CIcons::filter16(), "Remove Filter", CMenuAction::pathViewFilter(),
264  }
265  break;
266  }
268  {
269  ma.addAction(CIcons::tableRelationship16(), "Materialize filtered data", CMenuAction::pathViewFilter(),
271  break;
272  }
273  case MenuLoad:
274  {
275  ma.addAction(CIcons::disk16(), "Load from file ", CMenuAction::pathViewLoadSave(),
277  break;
278  }
279  case MenuSave:
280  {
281  ma.addAction(CIcons::disk16(),
283  CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::saveJsonAction },
285  if (this->hasSelection())
286  {
287  ma.addAction(CIcons::disk16(), "Save selected data in file", CMenuAction::pathViewLoadSave(),
289  break;
290  }
291  break;
292  }
293  case MenuCut:
294  {
295  if (!QApplication::clipboard()) break;
296  ma.addAction(CIcons::cut16(), "Cut", CMenuAction::pathViewCutPaste(), { this, &CViewBaseNonTemplate::cut },
298  break;
299  }
300  case MenuPaste:
301  {
302  if (!QApplication::clipboard()) break;
303  ma.addAction(CIcons::paste16(), "Paste", CMenuAction::pathViewCutPaste(),
305  break;
306  }
307  case MenuCopy:
308  {
309  if (!QApplication::clipboard()) break;
310  ma.addAction(CIcons::copy16(), "Copy", CMenuAction::pathViewCutPaste(),
312  break;
313  }
314  default: break;
315  }
316  m_menuFlagActions.insert(menu, ma);
317  return ma;
318  }
319 
321  {
322  if (!this->allowsMultipleSelectedRows()) { return; }
323  const CGeneralGuiSettings settings = m_guiSettings.getThreadLocal();
326  {
327  this->setSelectionMode(settings.getPreferredSelection());
328  }
329  }
330 
332  {
333  if (selectedFileOrDir.isEmpty()) { return; }
334  const QString dir = CDirectories::fileNameToDirectory(selectedFileOrDir);
335  QDir d(dir);
336  if (!d.exists()) { return; }
337 
338  // existing dir
339  CDirectories directories = m_dirSettings.get();
340  directories.setPropertyByIndex(m_dirSettingsIndex, CVariant::fromValue(dir));
341  const CStatusMessage msg = m_dirSettings.setAndSave(directories);
342  CLogMessage::preformatted(msg);
343  }
344 
346  {
347  const CDirectories directories = m_dirSettings.get();
348  return directories.propertyByIndex(m_dirSettingsIndex).toString();
349  }
350 
352  {
353  if (!m_textEditDialog) { m_textEditDialog = new CTextEditDialog(this); }
354  return m_textEditDialog;
355  }
356 
358  {
359  // delegate?
360  if (m_menu) { m_menu->customMenu(menuActions); }
361 
362  // standard view menus
363  if (m_menus.testFlag(MenuRefresh)) { menuActions.addActions(this->initMenuActions(MenuRefresh)); }
364  if (m_menus.testFlag(MenuBackend)) { menuActions.addActions(this->initMenuActions(MenuBackend)); }
365 
366  if (m_menus.testFlag(MenuClear)) { menuActions.addActions(this->initMenuActions(MenuClear)); }
367  if (m_menus.testFlag(MenuDisplayAutomatically))
368  {
369  // here I expect only one action
371  a->setChecked(this->displayAutomatically());
372  }
373  if (m_menus.testFlag(MenuRemoveSelectedRows))
374  {
375  if (this->hasSelection()) { menuActions.addActions(this->initMenuActions(MenuRemoveSelectedRows)); }
376  }
377 
378  if (m_menus.testFlag(MenuCopy)) { menuActions.addActions(this->initMenuActions(MenuCopy)); }
379  if (m_menus.testFlag(MenuCut)) { menuActions.addActions(this->initMenuActions(MenuCut)); }
380  if (m_menus.testFlag(MenuPaste)) { menuActions.addActions(this->initMenuActions(MenuPaste)); }
381  if (m_menus.testFlag(MenuFont) && m_fontMenu)
382  {
383  menuActions.addActions(m_fontMenu->getActions(), CMenuAction::pathFont());
384  }
385 
386  if (m_menus.testFlag(MenuFilter) && m_filterWidget)
387  {
388  menuActions.addActions(this->initMenuActions(MenuFilter));
389  if (m_menus.testFlag(MenuMaterializeFilter))
390  {
392  }
393  }
394 
395  // selection menus, not in menu action list because it depends on current selection
396  const SelectionMode sm = this->selectionMode();
397  if (sm == MultiSelection || sm == ExtendedSelection)
398  {
399  menuActions.addAction("Select all", CMenuAction::pathViewSelection(), nullptr,
401  }
402  if (sm != NoSelection)
403  {
404  menuActions.addAction("Clear selection " + CShortcut::toParenthesisString(CShortcut::keyClearSelection()),
405  CMenuAction::pathViewSelection(), nullptr,
407  }
410  {
411  if (sm != MultiSelection)
412  {
413  menuActions.addAction("Switch to multi selection", CMenuAction::pathViewSelection(), nullptr,
415  }
416 
417  if (sm != ExtendedSelection)
418  {
419  menuActions.addAction("Switch to extended selection", CMenuAction::pathViewSelection(), nullptr,
421  }
422 
423  if (sm != SingleSelection)
424  {
425  menuActions.addAction("Switch to single selection", CMenuAction::pathViewSelection(), nullptr,
427  }
428  }
429 
430  // load/save
431  if (m_menus.testFlag(MenuLoad)) { menuActions.addActions(this->initMenuActions(MenuLoad)); }
432  if (m_menus.testFlag(MenuSave) && !isEmpty()) { menuActions.addActions(this->initMenuActions(MenuSave)); }
433 
434  // resizing
435  menuActions.addAction(
436  CIcons::resize16(), "&Resize " + CShortcut::toParenthesisString(CShortcut::keyResizeView()),
437  CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::presizeOrFullResizeToContents });
438 
439  // resize to content might decrease performance,
440  // so I only allow changing to "content resizing" if size matches
441  // const bool enabled = !this->reachedResizeThreshold();
442  const bool enabled = true;
443  const bool autoResize = (m_resizeMode == ResizingAuto);
444 
445  // when not set to auto, then lets set how we want to resize rows
446  // for auto this is too slow
447  // const bool ww = this->wordWrap();
448  QAction *resizeRowsAction =
449  menuActions.addAction(CIcons::resizeVertical16(), "Resize rows to content", CMenuAction::pathViewResize(),
450  nullptr, { this, &CViewBaseNonTemplate::resizeRowsToContents });
451  resizeRowsAction->setEnabled(
452  true); // as changing from word wraap to none word wrap can leave to high columns, we always enable this
453 
463  // export actions, display in text edit
464  if (CBuildConfig::isLocalDeveloperDebugBuild())
465  {
466  menuActions.addAction(CIcons::tableSheet16(), "Display as JSON", CMenuAction::pathViewLoadSave(),
468  if (this->hasSelection())
469  {
470  menuActions.addAction(CIcons::tableSheet16(), "Display selected as JSON",
471  CMenuAction::pathViewLoadSave(),
473  ;
474  }
475  }
476 
477  QAction *actionInteractiveResize =
478  menuActions.addAction(CIcons::viewTile(), "Resize (auto)", CMenuAction::pathViewResize(), nullptr);
479  actionInteractiveResize->setObjectName(this->objectName().append("ActionResizing"));
480  actionInteractiveResize->setCheckable(true);
481  actionInteractiveResize->setChecked(autoResize);
482  actionInteractiveResize->setEnabled(enabled);
483  connect(actionInteractiveResize, &QAction::toggled, this, &CViewBaseNonTemplate::toggleResizeMode);
484 
485  QAction *actionWordWrap = menuActions.addAction(CIcons::viewMultiColumn(), "Word wrap (multiline)",
486  CMenuAction::pathViewResize(), nullptr);
487  actionWordWrap->setObjectName(this->objectName().append("ActionResizing"));
488  actionWordWrap->setCheckable(true);
489  actionWordWrap->setChecked(this->wordWrap());
490  actionWordWrap->setEnabled(true);
491  connect(actionWordWrap, &QAction::toggled, this, &CViewBaseNonTemplate::toggleWordWrap);
492  }
493 
495  {
496  if (this->isShowingLoadIndicator())
497  {
498  // re-center
499  this->centerLoadIndicator();
500  }
502  }
503 
505  {
506  const QFontMetrics m(this->getHorizontalHeaderFont());
507  const int h = m.height();
508  return h;
509  }
510 
512 
513  QModelIndexList CViewBaseNonTemplate::selectedRows() const
514  {
515  // make sure this is ordered by row and wee keep the same order as in unselectedRows
516  // if we'd know for sure the indexes are always sorted we can remove the sorting here
517  // Qt docu selectedIndexes: Returns a list of all selected model item indexes. The list contains no duplicates,
518  // and is not sorted.
519  QModelIndexList indexes = this->selectionModel()->selectedRows();
520  std::sort(indexes.begin(), indexes.end());
521  return indexes;
522  }
523 
524  QModelIndexList CViewBaseNonTemplate::unselectedRows() const
525  {
526  const QModelIndexList selected = this->selectedRows();
527  QModelIndexList unselected;
528  const int rows = this->rowCount();
529  for (int r = 0; r < rows; r++)
530  {
531  const QModelIndex mi = this->model()->index(r, 0);
532  if (selected.contains(mi)) { continue; }
533  unselected.push_back(mi);
534  }
535  return unselected;
536  }
537 
539  {
540  if (!this->selectionModel()) { return 0; }
541 
542  // multiple times faster than multiple this->selectRow()
543  this->clearSelection();
544  QItemSelection selectedItems;
545  const int columns = this->model()->columnCount() - 1;
546  for (int r : rows) { selectedItems.select(this->model()->index(r, 0), this->model()->index(r, columns)); }
547  this->selectionModel()->select(selectedItems, QItemSelectionModel::Select);
548  return selectedItems.size();
549  }
550 
552  {
553  if (!this->hasSelection()) { return 0; }
554  return this->selectedRows().count();
555  }
556 
557  int CViewBaseNonTemplate::unselectedRowCount() const { return this->rowCount() - this->selectedRowCount(); }
558 
559  bool CViewBaseNonTemplate::hasSingleSelectedRow() const { return this->selectedRowCount() == 1; }
560 
562 
564  {
566  }
567 
569  {
572  }
573 
575  {
578  const int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight());
579  this->verticalHeader()->setDefaultSectionSize(fh); // for height
580  this->verticalHeader()->setMinimumSectionSize(fh); // for height
581 
582  switch (m_rowResizeMode)
583  {
584  case Interactive: this->rowsResizeModeToInteractive(); break;
585  case Content: this->rowsResizeModeToContent(); break;
586  default: Q_ASSERT_X(false, Q_FUNC_INFO, "wrong resize mode"); break;
587  }
588 
589  // call this deferred, otherwise the values are overridden with any values
590  // from the UI builder
591  const QPointer<CViewBaseNonTemplate> guard(this);
592  QTimer::singleShot(500, this, [=]() {
593  if (!guard) { return; }
595  });
596  }
597 
599  {
600  // some logic to find a useful default name
601  if (load)
602  {
603  return CFileUtils::appendFilePaths(this->getRememberedLastJsonDirectory(),
604  CFileUtils::jsonWildcardAppendix());
605  }
606 
607  // Save file path
608  const QString dir = m_dirSettings.get().propertyByIndex(m_dirSettingsIndex).toString();
609  QString name(m_saveFileName);
610  if (name.isEmpty())
611  {
612  // create a name
613  if (this->getDockWidgetInfoArea()) { name = this->getDockWidgetInfoArea()->windowTitle(); }
614  else { name = this->metaObject()->className(); }
615  }
616  if (!name.endsWith(CFileUtils::jsonAppendix(), Qt::CaseInsensitive)) { name += CFileUtils::jsonAppendix(); }
617  return CFileUtils::appendFilePaths(dir, name);
618  }
619 
620  void CViewBaseNonTemplate::menuRemoveItems(Menu menusToRemove) { m_menus &= (~menusToRemove); }
621 
623  {
624  m_menus |= menusToAdd;
625  if (menusToAdd.testFlag(MenuRemoveSelectedRows)) { m_enableDeleteSelectedRows = true; }
626  }
627 
629  {
630  if (!m_menus.testFlag(MenuFilter)) { return; }
631  if (!m_filterWidget) { return; }
632  m_filterWidget->show();
633  }
634 
636  {
637  if (!m_menus.testFlag(MenuLoad)) { return; }
638  const CStatusMessage m = this->loadJson();
639  if (!m.isEmpty()) { CLogMessage::preformatted(m); }
640  }
641 
643  {
644  if (this->isEmpty()) { return; }
645  if (!m_menus.testFlag(MenuSave)) { return; }
646  const CStatusMessage m = this->saveJson(false);
647  if (!m.isEmpty()) { CLogMessage::preformatted(m); }
648  }
649 
651  {
652  if (this->isEmpty()) { return; }
653  if (!m_menus.testFlag(MenuSave)) { return; }
654  const CStatusMessage m = this->saveJson(true);
655  if (!m.isEmpty()) { CLogMessage::preformatted(m); }
656  }
657 
659  {
661  emit this->requestUpdate();
662  }
663 
665  {
667  emit this->requestNewBackendData();
668  }
669 
671 
673  {
674  const int height = this->verticalHeader()->minimumSectionSize();
676  Q_ASSERT_X(verticalHeader, Q_FUNC_INFO, "Missing vertical header");
679  m_rowResizeMode = Interactive;
680  this->showVerticalHeader();
681  }
682 
684  {
686  verticalHeader->setVisible(this->wordWrap() && m_resizeMode != ResizingAuto && m_rowResizeMode == Interactive);
688  }
689 
691  {
693  Q_ASSERT(verticalHeader);
695  m_rowResizeMode = Content;
696  this->showVerticalHeader();
697  }
698 
700  {
701  if (elements > ResizeRowsToContentThreshold) { this->rowsResizeModeToInteractive(); }
702  else { this->rowsResizeModeToContent(); }
703  }
704 
705  int CViewBaseNonTemplate::showLoadIndicator(int containerSizeDependent, std::chrono::milliseconds timeout,
706  bool processEvents)
707  {
708  using namespace std::chrono_literals;
709  if (!m_enabledLoadIndicator) { return -1; }
710  if (m_showingLoadIndicator) { return -1; }
711 
712  if (this->hasDockWidgetArea())
713  {
714  if (!this->isVisibleWidget()) { return -1; }
715  }
716 
717  if (containerSizeDependent >= 0)
718  {
719  // really with indicator?
720  if (containerSizeDependent < ResizeSubsetThreshold) { return -1; }
721  }
722  m_showingLoadIndicator = true;
724 
725  if (!m_loadIndicator)
726  {
727  m_loadIndicator = new CLoadIndicator(64, 64, this);
728  connect(m_loadIndicator, &CLoadIndicator::timedOut, this, &CViewBaseNonTemplate::onLoadIndicatorTimedOut);
729  }
730  this->centerLoadIndicator();
731  return m_loadIndicator->startAnimation(timeout > 0ms ? timeout : m_loadIndicatorTimeoutDefault, processEvents);
732  }
733 
734  int CViewBaseNonTemplate::showLoadIndicatorWithTimeout(std::chrono::milliseconds timeout, bool processEvents)
735  {
736  return this->showLoadIndicator(-1, timeout, processEvents);
737  }
738 
740  {
741  if (!m_loadIndicator) { return; }
742  const QPoint middle = this->viewport()->geometry().center();
744  }
745 
747  {
748  if (!m_showingLoadIndicator) { return; }
749  m_showingLoadIndicator = false;
751  if (!m_loadIndicator) { return; }
752  m_loadIndicator->stopAnimation(loadingId);
753  }
754 
755  bool CViewBaseNonTemplate::isResizeConditionMet(int containerSize) const
756  {
757  if (m_resizeMode == ResizingAlways) { return true; }
758  if (m_resizeMode == PresizeSubset) { return false; }
759  if (m_resizeMode == ResizingOff) { return false; }
760  if (m_resizeMode == ResizingOnce) { return m_resizeCount < 1; }
761  if (m_resizeMode == ResizingAuto)
762  {
763  if (reachedResizeThreshold(containerSize)) { return false; }
764  if (m_resizeAutoNthTime < 2) { return true; }
765  return (m_resizeCount % m_resizeAutoNthTime) == 0;
766  }
767  return false;
768  }
769 
771  {
774 
789  // useless if mode is Interactive
790  if (m_rowResizeMode == Content)
791  {
792  this->resizeRowsToContents(); // rows
793  }
794  m_resizeCount++;
795 
796  // re-stretch
798  {
799  // toggling forces the stretch, otherwise not working
800  this->horizontalHeader()->setStretchLastSection(false);
802  }
803 
804  // const int cols = this->colorCount();
805  // if (this->endsWithEmptyColumn()) { this->setColumnWidth(cols - 1, 10); }
806  // gives a weird NO METRICS warning
807 
808  this->resizeColumnsToContents(); // columns
809 
816  }
817 
818  void CViewBaseNonTemplate::customMenuRequested(const QPoint &pos)
819  {
820  QMenu menu;
821  CMenuActions menuActions;
822  this->customMenu(menuActions);
823  if (menuActions.isEmpty()) { return; }
824  menuActions.toQMenu(menu, true);
825 
826  // Nested dock widget menu
827  const CDockWidgetInfoArea *dockWidget = this->getDockWidgetInfoArea();
828  if (dockWidget)
829  {
830  if (!menu.isEmpty()) { menu.addSeparator(); }
831  const QString mm = QStringLiteral("Dock widget '%1'").arg(dockWidget->windowTitleOrBackup());
832  QMenu *dockWidgetSubMenu = menu.addMenu(CIcons::text16(), mm);
833  dockWidget->addToContextMenu(dockWidgetSubMenu);
834  }
835 
836  const QPoint globalPos = this->mapToGlobal(pos);
837  menu.exec(globalPos);
838  }
839 
840  void CViewBaseNonTemplate::onLoadIndicatorTimedOut() { m_showingLoadIndicator = false; }
841 
842  void CViewBaseNonTemplate::toggleResizeMode(bool checked)
843  {
844  m_resizeMode = checked ? ResizingAuto : ResizingOff;
845  if (m_resizeMode == ResizingAuto)
846  {
847  // make sure not use this one here
849  }
850  else { this->showVerticalHeader(); }
851  }
852 
853  void CViewBaseNonTemplate::toggleWordWrap(bool checked)
854  {
855  if (this->wordWrap() == checked) { return; }
856  if (checked)
857  {
858  // menuAddItems()
859  }
860  this->setWordWrap(checked);
861  this->showVerticalHeader(); // can be slow
862  }
863 
864  void CViewBaseNonTemplate::toggleAutoDisplay()
865  {
866  const auto *a = qobject_cast<const QAction *>(QObject::sender());
867  if (!a) { return; }
868  Q_ASSERT_X(a->isCheckable(), Q_FUNC_INFO, "object not checkable");
869  m_displayAutomatically = a->isChecked();
870  }
871 
873  {
874  // FIXME: Workaround to implement the logic on our own because the default selectAll() implementation does not
875  // seem to work
876  this->clearSelection();
877  QItemSelection selectedItems;
878  const int columns = this->model()->columnCount() - 1;
879  const int rows = this->model()->rowCount() - 1;
880  selectedItems.select(this->model()->index(0, 0), this->model()->index(rows, columns));
881  this->selectionModel()->select(selectedItems, QItemSelectionModel::Select);
882  }
883 
885 
887  {
889  }
890 
892  {
894  }
895 
896  void CViewBaseNonTemplate::removeSelectedRowsChecked()
897  {
898  if (!m_enableDeleteSelectedRows) { return; }
899  this->removeSelectedRows();
900  }
901 
903  {
904  if (!event || !this->acceptDrop(event->mimeData())) { return; }
906  event->acceptProposedAction();
907  }
908 
910  {
911  if (!event || !this->acceptDrop(event->mimeData())) { return; }
912  event->acceptProposedAction();
913  }
914 
916  {
917  if (!event) { return; }
918  event->accept();
919  }
920 
922  {
923  if (!event) { return; }
925  }
926 
928  {
929  containerSize = containerSize >= 0 ? containerSize : this->rowCount();
930  const int presizeRandomElements = containerSize > 1000 ? containerSize / 100 : containerSize / 40;
931  return presizeRandomElements;
932  }
933 } // namespace swift::gui::views
QString windowTitleOrBackup() const
If current window title is empty, use backup.
Definition: dockwidget.cpp:433
Specialized class for dock widgets serving as info area.
void addToContextMenu(QMenu *contextMenu) const
Contribute to menu.
CDockWidgetInfoArea * getDockWidgetInfoArea() const
Corresponding dockable widget in info area.
virtual bool setParentDockWidgetInfoArea(CDockWidgetInfoArea *parentDockableWidget)
Corresponding dockable widget in info area.
static QWidget * mainApplicationWidget()
Main application window widget.
Definition: guiutility.cpp:92
The QProgressIndicator class lets an application display a progress indicator to show that a lengthy ...
Definition: loadindicator.h:31
void centerLoadIndicator(const QPoint &middle)
Center this load indicator.
int startAnimation(std::chrono::milliseconds timeout=std::chrono::milliseconds(0), bool processEvents=false)
Starts the spin animation.
void timedOut()
Timed out.
void stopAnimation(int indicatorId=-1)
Stops the spin animation.
Using this class provides a QTableView with the overlay functionality already integrated.
static const QKeySequence & keyDelete()
Delete, e.g. selected rows.
Definition: shortcut.cpp:61
static const QKeySequence & keySelectAll()
For selecting all.
Definition: shortcut.cpp:30
static const QKeySequence & keyResizeView()
Resize view.
Definition: shortcut.cpp:42
static const QKeySequence & keyDisplayFilter()
Display filter.
Definition: shortcut.cpp:36
static const QKeySequence & keyClearSelection()
For deselecting all.
Definition: shortcut.cpp:24
static const QKeySequence & keyCopy()
Copy.
Definition: shortcut.cpp:73
static QString toParenthesisString(const QKeySequence &sequence)
As string for menus etc. Looks like "(CTRL + R)".
Definition: shortcut.cpp:97
static const QKeySequence & keySaveViews()
Save in views.
Definition: shortcut.cpp:54
Base for filter dialog.
Definition: filterdialog.h:20
Base for filter dialog.
Definition: filterwidget.h:21
QList< QAction * > getActions() const
Allow to use the actions directly.
Definition: fontmenus.cpp:46
Bunch of CMenuAction objects.
Definition: menuaction.h:384
CMenuActions addActions(const CMenuActions &actions)
Add menu actions, returns last valid QAction.
Definition: menuaction.cpp:217
void toQMenu(QMenu &menu, bool separateGroups) const
Insert the sorted actions to the menu.
Definition: menuaction.cpp:326
CMenuAction addAction(const CMenuAction &menuAction)
Add menu action.
Definition: menuaction.cpp:210
bool isEmpty() const
Empty?
Definition: menuaction.h:405
CMenuAction first() const
First action.
Definition: menuaction.h:485
Interface to implement a custom menu.
Definition: menudelegate.h:21
IMenuDelegate * getNestedDelegate() const
Nested delegate.
Definition: menudelegate.h:32
virtual void customMenu(CMenuActions &menuActions)=0
Display custom menu.
void setNestedDelegate(IMenuDelegate *nestedDelegate)
Set nested delegate.
Definition: menudelegate.h:29
QAbstractItemView::SelectionMode getPreferredSelection() const
Preferred selection.
Definition: guisettings.cpp:27
virtual bool filterWidgetChangedFilter(bool enabled)=0
Filter changed in filter widget.
QString getFileDialogFileName(bool load) const
Default file for load/save operations.
QWidget * m_filterWidget
filter widget or dialog
Definition: viewbase.h:602
void menuAddItems(Menu menusToAdd)
Add given menu items.
void setSelectionModel(QItemSelectionModel *model)
components::CTextEditDialog * textEditDialog()
Init text edit dialog if required and return pointer to it.
static constexpr int ResizeSubsetThreshold
When to use pre-sizing with random elements.
Definition: viewbase.h:153
void rowsResizeModeToInteractive()
Init as interactive, as this allows manually resizing.
bool m_forceStretchLastColumnWhenResized
a small table might (few columns) fail stretching, force again
Definition: viewbase.h:590
void hideLoadIndicator(int loadingId=-1)
Hide loading indicator.
void setHorizontalHeaderSectionResizeMode(QHeaderView::ResizeMode mode)
Resize mode.
virtual bool reachedResizeThreshold(int containerSize=-1) const =0
Skip resizing because of size?
swift::misc::CDirectories::ColumnIndex m_dirSettingsIndex
allows to set more specialized directories //!< remember last JSON directory, having this member allo...
Definition: viewbase.h:610
menus::IMenuDelegate * setCustomMenu(menus::IMenuDelegate *menu, bool nestPreviousMenu=true)
Set custom menu if applicable.
Menu m_menus
Default menu settings.
Definition: viewbase.h:603
@ PresizeSubset
use a subset of the data to resize
Definition: viewbase.h:104
@ ResizingAuto
resizing when below threshold,
Definition: viewbase.h:101
int selectedRowCount() const
Number of selected rows.
virtual void resizeToContents()
Resize to contents, strategy depends on container size.
SelectionMode m_originalSelectionMode
Selection mode set.
Definition: viewbase.h:585
void triggerReload()
Trigger reload from backend by signal requestUpdate();.
bool isCurrentlyAllowingMultipleRowSelections() const
Is the current selection mode allow multiple selection.
virtual int removeSelectedRows()=0
Remove selected rows.
int getPresizeRandomElementsSize(int containerSize=-1) const
Calculate presize count.
bool allowsMultipleSelectedRows() const
Allows to select multiple rows.
void requestNewBackendData()
Load data from backend (where it makes sense)
virtual void removeFilter()=0
Remove filter.
virtual QString getRememberedLastJsonDirectory() const
JSON directory.
virtual void onDoubleClicked(const QModelIndex &index)=0
Index double clicked.
virtual void displaySelectedJsonPopup()=0
Display JSON data.
virtual void onClicked(const QModelIndex &index)=0
Index clicked.
virtual bool acceptDrop(const QMimeData *mimeData) const =0
Accept drop data?
void loadIndicatorVisibilityChanged(bool visible)
Load indicator's visibility has been changed.
int getHorizontalHeaderFontHeight() const
Horizontal font height.
bool m_displayAutomatically
display directly when loaded
Definition: viewbase.h:597
QModelIndexList selectedRows() const
Selected rows if any.
void loadJsonAction()
Load JSON for action/menu, void return signatur.
void menuRemoveItems(Menu menusToRemove)
Remove given menu items.
virtual void rememberLastJsonDirectory(const QString &selectedFileOrDir)
JSON directory.
virtual swift::misc::CStatusMessage loadJson(const QString &directory={})=0
Load JSON.
CLoadIndicator * m_loadIndicator
load indicator if needed
Definition: viewbase.h:606
virtual QModelIndexList unselectedRows() const
Unselected (not selected) rows if any.
RowsResizeMode m_rowResizeMode
row resize mode for row height
Definition: viewbase.h:584
void requestUpdate()
Ask for new data from currently loaded data.
bool m_enabledLoadIndicator
loading indicator enabled/disabled
Definition: viewbase.h:593
int showLoadIndicatorWithTimeout(std::chrono::milliseconds timeout=std::chrono::milliseconds { 0 }, bool processEvents=true)
Show loading indicator which can time out.
void saveJsonAction()
Save JSON for action/menu, void return signatur.
void saveSelectedJsonAction()
Save JSON for action/menu, void return signatur.
QWidget * mainApplicationWindowWidget() const
Main application window widget if any.
virtual void displayJsonPopup()=0
Display JSON data.
virtual void performModeBasedResizeToContent()=0
Perform resizing (no presizing) / non slot method for template.
virtual void materializeFilter()=0
Materialize filter.
void setFilterWidget(filters::CFilterWidget *filterWidget)
Set filter widget.
void dragEnterEvent(QDragEnterEvent *event)
swift::misc::CStatusMessage showFileLoadDialog(const QString &directory={})
Show file load dialog.
void onModelChanged()
Underlying model changed.
int m_resizeAutoNthTime
with ResizeAuto, resize every n-th time
Definition: viewbase.h:588
virtual void fullResizeToContents()
Full resizing to content, might be slow.
void setMultiSelection()
Change selection modes.
QMap< MenuFlag, menus::CMenuActions > m_menuFlagActions
initialized actions for menu flag (enum)
Definition: viewbase.h:608
std::chrono::milliseconds m_loadIndicatorTimeoutDefault
default time for timeout
Definition: viewbase.h:589
virtual void clear()=0
Clear data.
virtual void onRowSelected(const QModelIndex &index)=0
Row selected.
void rowsResizeModeToContent()
Resize mode to content.
virtual bool filterDialogFinished(int status)=0
Filter dialog finished.
QString m_saveFileName
save file name (JSON)
Definition: viewbase.h:609
menus::CFontMenu * m_fontMenu
font menu if applicable
Definition: viewbase.h:605
void centerLoadIndicator()
Center / re-center load indicator.
virtual bool isResizeConditionMet(int containerSize=-1) const
Resize or skip resize?
bool hasSingleSelectedRow() const
Single selected row.
bool hasMultipleSelectedRows() const
Multiple selected rows.
swift::misc::CSettingReadOnly< settings::TGeneralGui > m_guiSettings
general GUI settings
Definition: viewbase.h:616
virtual bool isEmpty() const =0
Empty?
int showLoadIndicator(int containerSizeDependent=-1, std::chrono::milliseconds timeout=std::chrono::milliseconds { 0 }, bool processEvents=true)
Show loading indicator.
int m_resizeCount
flag / counter, how many resize activities
Definition: viewbase.h:586
virtual void presizeOrFullResizeToContents()=0
Depending on CViewBaseNonTemplate::ResizeSubsetThreshold presize or fully resize.
virtual void customMenu(menus::CMenuActions &menuActions)
Method creating the menu.
swift::misc::CStatusMessage showFileSaveDialog(bool selectedOnly, const QString &directory={})
Show file save dialog.
virtual void updateSortIndicator()=0
Set the sort indicator to the current sort column.
virtual swift::misc::CStatusMessage saveJson(bool selectedOnly=false, const QString &directory={})=0
Save JSON.
void rowsResizeModeBasedOnThreshold(int elements)
Set content/interactive mode based on ResizeRowsToContentThreshold.
void enableLoadIndicator(bool enable)
Enable loading indicator.
static constexpr int ResizeRowsToContentThreshold
When to use rows resizing (which is slow)
Definition: viewbase.h:157
bool isShowingLoadIndicator
Load indicator property allows using in stylesheet.
Definition: viewbase.h:91
void settingsChanged()
Settings have been changed.
void setExtendedSelection()
Change selection modes.
void modelDataChanged(int count, bool withFilter)
Model data changed.
bool m_showingLoadIndicator
showing loading indicator
Definition: viewbase.h:592
int selectRows(const QSet< int > &rows)
Select given rows.
bool setParentDockWidgetInfoArea(swift::gui::CDockWidgetInfoArea *parentDockableWidget)
Corresponding dockable widget in info area.
virtual void paste()=0
Clipboard cut/copy/paste.
void displayFilterDialog()
Display the filter dialog.
menus::IMenuDelegate * m_menu
custom menu if any
Definition: viewbase.h:604
swift::misc::CSetting< swift::misc::settings::TDirectorySettings > m_dirSettings
directory for load/save
Definition: viewbase.h:613
void setSingleSelection()
Change selection modes.
menus::CMenuActions initMenuActions(MenuFlag menu)
Init menu actions.
virtual void copy()=0
Clipboard cut/copy/paste.
void setFilterDialog(filters::CFilterDialog *filterDialog)
Filter dialog.
components::CTextEditDialog * m_textEditDialog
text edit dialog
Definition: viewbase.h:607
void dragLeaveEvent(QDragLeaveEvent *event)
void triggerReloadFromBackend()
Trigger reload from backend by signal requestNewBackendData()
int unselectedRowCount() const
Unselected row count.
@ MenuDisplayAutomatically
allow to switch display automatically
Definition: viewbase.h:124
@ MenuClear
allow clearing the view via menu
Definition: viewbase.h:120
@ MenuFont
font related menu (size)
Definition: viewbase.h:135
@ MenuRefresh
allow refreshing the view via menu
Definition: viewbase.h:122
@ MenuCopy
copy (for copy/paste)
Definition: viewbase.h:132
@ MenuBackend
allow to request data from backend
Definition: viewbase.h:123
@ MenuRemoveSelectedRows
allow to remove selected rows
Definition: viewbase.h:121
@ MenuPaste
paste (for copy/paste)
Definition: viewbase.h:133
@ MenuMaterializeFilter
materialize filter (filtered data become model data)
Definition: viewbase.h:127
@ MenuFilter
filter can be opened
Definition: viewbase.h:126
@ MenuToggleSelectionMode
allow to toggle selection mode
Definition: viewbase.h:130
bool hasSelection() const
Selection (selected rows)
virtual int rowCount() const =0
Elements in container.
bool displayAutomatically() const
Display automatically (when models are loaded)
Definition: viewbase.h:219
bool m_enableDeleteSelectedRows
selected rows can be deleted
Definition: viewbase.h:598
const QFont & getHorizontalHeaderFont() const
Header (horizontal) font.
Definition: viewbase.h:225
virtual void cut()=0
Clipboard cut/copy/paste.
CStatusMessage setAndSave(const T &value, qint64 timestamp=0)
Write and save in the same step. Must be called from the thread in which the owner lives.
Definition: valuecache.h:417
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
Directories (swift data directories)
Definition: directories.h:25
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: directories.cpp:82
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: directories.cpp:62
bool isEmpty() const
Message empty.
Streamable status message, e.g.
High level reusable GUI components.
Definition: aboutdialog.cpp:14
Filter to search data sets.
Models to be used with views, mainly QTableView.
Views, mainly QTableView.
GUI related classes.
Free functions in swift::misc.
virtual int columnCount(const QModelIndex &parent) const const=0
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
void clicked(const QModelIndex &index)
void doubleClicked(const QModelIndex &index)
virtual bool event(QEvent *event) override
void setHorizontalScrollMode(QAbstractItemView::ScrollMode mode)
QAbstractItemModel * model() const const
virtual void resizeEvent(QResizeEvent *event) override
void setSelectionMode(QAbstractItemView::SelectionMode mode)
QItemSelectionModel * selectionModel() const const
void setTextElideMode(Qt::TextElideMode mode)
void setVerticalScrollMode(QAbstractItemView::ScrollMode mode)
QWidget * viewport() const const
void setCheckable(bool)
void setChecked(bool)
void setEnabled(bool)
void toggled(bool checked)
bool exists() const const
const QMimeData * mimeData() const const
int height() const const
QClipboard * clipboard()
void setDefaultSectionSize(int size)
void setMinimumSectionSize(int size)
void setSectionResizeMode(QHeaderView::ResizeMode mode)
virtual void setVisible(bool v) override
void setSortIndicatorShown(bool show)
void setStretchLastSection(bool stretch)
void select(const QModelIndex &topLeft, const QModelIndex &bottomRight)
void currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
bool hasSelection() const const
virtual void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
QModelIndexList selectedRows(int column) const const
qsizetype size() const const
QAction * addMenu(QMenu *menu)
QAction * addSeparator()
QAction * exec()
bool isEmpty() const const
QMetaMethod fromSignal(PointerToMemberFunction signal)
const char * className() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
bool isSignalConnected(const QMetaMethod &signal) const const
virtual const QMetaObject * metaObject() const const
QObject * parent() const const
QObject * sender() const const
void setObjectName(QAnyStringView name)
void activated()
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
CaseInsensitive
QueuedConnection
CustomContextMenu
WidgetShortcut
ElideNone
virtual void dropEvent(QDropEvent *event) override
QHeaderView * horizontalHeader() const const
void resizeColumnsToContents()
void resizeRowsToContents()
virtual void setSelectionModel(QItemSelectionModel *selectionModel) override
QHeaderView * verticalHeader() const const
void setWordWrap(bool on)
QString toString() const const
void setContextMenuPolicy(Qt::ContextMenuPolicy policy)
void customContextMenuRequested(const QPoint &pos)
QPoint mapToGlobal(const QPoint &pos) const const
void setBackgroundRole(QPalette::ColorRole role)
void setFixedWidth(int w)
void show()
void resize(const QSize &)