From a005ebbbc92e68737e7fd605953392c296246b78 Mon Sep 17 00:00:00 2001 From: Roland Rossgotterer Date: Thu, 22 Nov 2018 11:13:19 +0100 Subject: [PATCH] Split viewbase.cpp viewbase.cpp instantiates many template classes and ended up as a huge object file. Building with MinGW in not-optimized debug build, the file got too big. Therefore split the template instantiations into individual files. --- src/blackgui/views/viewbase.cpp | 920 +-------------------- src/blackgui/views/viewbaseaviation.cpp | 26 + src/blackgui/views/viewbasemisc.cpp | 22 + src/blackgui/views/viewbasenetwork.cpp | 21 + src/blackgui/views/viewbasenontemplate.cpp | 889 ++++++++++++++++++++ src/blackgui/views/viewbasesimulation.cpp | 21 + src/blackgui/views/viewbaseweather.cpp | 20 + 7 files changed, 1002 insertions(+), 917 deletions(-) create mode 100644 src/blackgui/views/viewbaseaviation.cpp create mode 100644 src/blackgui/views/viewbasemisc.cpp create mode 100644 src/blackgui/views/viewbasenetwork.cpp create mode 100644 src/blackgui/views/viewbasenontemplate.cpp create mode 100644 src/blackgui/views/viewbasesimulation.cpp create mode 100644 src/blackgui/views/viewbaseweather.cpp diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index 202593c16..1f840a6df 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -7,59 +7,20 @@ * contained in the LICENSE file. */ -#include "blackgui/components/texteditdialog.h" +#include "blackgui/guiutility.h" +#include "blackgui/models/allmodels.h" #include "blackgui/views/viewbase.h" #include "blackgui/views/viewbaseproxystyle.h" #include "blackgui/views/viewbaseitemdelegate.h" -#include "blackgui/filters/filterdialog.h" -#include "blackgui/filters/filterwidget.h" -#include "blackgui/models/allmodels.h" -#include "blackgui/models/allmodelcontainers.h" -#include "blackgui/menus/menuaction.h" -#include "blackgui/menus/menudelegate.h" -#include "blackgui/menus/fontmenus.h" -#include "blackgui/dockwidgetinfoarea.h" -#include "blackgui/guiapplication.h" -#include "blackgui/guiutility.h" -#include "blackgui/loadindicator.h" -#include "blackgui/shortcut.h" -#include "blackmisc/fileutils.h" -#include "blackmisc/icons.h" -#include "blackmisc/logmessage.h" -#include "blackmisc/namevariantpair.h" -#include "blackmisc/namevariantpairlist.h" -#include "blackmisc/propertyindexvariantmap.h" +#include "blackgui/components/texteditdialog.h" #include "blackmisc/worker.h" #include "blackconfig/buildconfig.h" #include -#include #include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -using namespace BlackConfig; using namespace BlackMisc; using namespace BlackGui; using namespace BlackGui::Menus; @@ -72,845 +33,6 @@ namespace BlackGui { namespace Views { - CViewBaseNonTemplate::CViewBaseNonTemplate(QWidget *parent) : - QTableView(parent) - { - this->setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &QWidget::customContextMenuRequested, this, &CViewBaseNonTemplate::customMenuRequested); - connect(this, &QTableView::clicked, this, &CViewBaseNonTemplate::ps_clicked); - connect(this, &QTableView::doubleClicked, this, &CViewBaseNonTemplate::ps_doubleClicked); - this->horizontalHeader()->setSortIndicatorShown(true); - - // setting resize mode rowsResizeModeToContent() causes extremly slow views - // default, see: m_rowResizeMode - - // scroll modes - this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - this->setWordWrap(true); - this->setTextElideMode(Qt::ElideMiddle); - - // shortcuts - QShortcut *filter = new QShortcut(CShortcut::keyDisplayFilter(), this); - connect(filter, &QShortcut::activated, this, &CViewBaseNonTemplate::displayFilterDialog); - filter->setObjectName("Filter shortcut for " + this->objectName()); - filter->setContext(Qt::WidgetShortcut); - - QShortcut *clearSelection = new QShortcut(CShortcut::keyClearSelection(), this); - connect(clearSelection, &QShortcut::activated, this, &CViewBaseNonTemplate::clearSelection); - clearSelection->setObjectName("Clear selection shortcut for " + this->objectName()); - clearSelection->setContext(Qt::WidgetShortcut); - - QShortcut *saveJson = new QShortcut(CShortcut::keySaveViews(), this); - connect(saveJson, &QShortcut::activated, this, &CViewBaseNonTemplate::saveJsonAction); - saveJson->setObjectName("Save JSON for " + this->objectName()); - saveJson->setContext(Qt::WidgetShortcut); - - QShortcut *deleteRow = new QShortcut(CShortcut::keyDelete(), this); - connect(deleteRow, &QShortcut::activated, this, &CViewBaseNonTemplate::removeSelectedRowsChecked); - deleteRow->setObjectName("Remove selected rows for " + this->objectName()); - deleteRow->setContext(Qt::WidgetShortcut); - - QShortcut *copy = new QShortcut(CShortcut::keyCopy(), this); - connect(copy, &QShortcut::activated, this, &CViewBaseNonTemplate::copy); - copy->setObjectName("Copy selection shortcut for " + this->objectName()); - copy->setContext(Qt::WidgetShortcut); - - QShortcut *resize = new QShortcut(CShortcut::keyResizeView(), this); - connect(resize, &QShortcut::activated, this, &CViewBaseNonTemplate::fullResizeToContents); - resize->setObjectName("Resize view shortcut for " + this->objectName()); - resize->setContext(Qt::WidgetShortcut); - } - - CViewBaseNonTemplate::~CViewBaseNonTemplate() - { - // dtor - } - - bool CViewBaseNonTemplate::setParentDockWidgetInfoArea(CDockWidgetInfoArea *parentDockableWidget) - { - const bool c = CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget); - return c; - } - - void CViewBaseNonTemplate::resizeToContents() - { - this->performModeBasedResizeToContent(); - } - - void CViewBaseNonTemplate::setFilterWidgetImpl(QWidget *filterWidget) - { - if (filterWidget == m_filterWidget) { return; } - - // dialog or filter widget - if (m_filterWidget) - { - disconnect(m_filterWidget); - this->menuRemoveItems(MenuFilter); - if (m_filterWidget->parent() == this) { m_filterWidget->deleteLater(); } - m_filterWidget = nullptr; - } - - if (filterWidget) - { - this->menuAddItems(MenuFilter); - m_filterWidget = filterWidget; - } - } - - void CViewBaseNonTemplate::setFilterDialog(CFilterDialog *filterDialog) - { - if (filterDialog == m_filterWidget) { return; } - this->setFilterWidgetImpl(filterDialog); - if (filterDialog) - { - const bool s = connect(filterDialog, &CFilterDialog::finished, this, &CViewBaseNonTemplate::ps_filterDialogFinished); - Q_ASSERT_X(s, Q_FUNC_INFO, "filter dialog connect"); - Q_UNUSED(s); - } - } - - void CViewBaseNonTemplate::setFilterWidget(CFilterWidget *filterWidget) - { - if (filterWidget == m_filterWidget) { return; } - this->setFilterWidgetImpl(filterWidget); - if (filterWidget) - { - bool s = connect(filterWidget, &CFilterWidget::changeFilter, this, &CViewBaseNonTemplate::ps_filterWidgetChangedFilter); - Q_ASSERT_X(s, Q_FUNC_INFO, "filter connect"); - s = connect(this, &CViewBaseNonTemplate::modelDataChanged, filterWidget, &CFilterWidget::onRowCountChanged); - Q_ASSERT_X(s, Q_FUNC_INFO, "filter connect"); - Q_UNUSED(s); - } - } - - void CViewBaseNonTemplate::enableLoadIndicator(bool enable) - { - m_enabledLoadIndicator = enable; - } - - bool CViewBaseNonTemplate::isShowingLoadIndicator() const - { - return m_loadIndicator && m_enabledLoadIndicator && m_showingLoadIndicator; - } - - void CViewBaseNonTemplate::setSelectionModel(QItemSelectionModel *model) - { - if (this->selectionModel()) { disconnect(this->selectionModel()); } - QTableView::setSelectionModel(model); - if (this->selectionModel()) - { - connect(this->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &CViewBaseNonTemplate::ps_rowSelected); - } - } - - QWidget *CViewBaseNonTemplate::mainApplicationWindowWidget() const - { - return CGuiUtility::mainApplicationWidget(); - } - - CStatusMessage CViewBaseNonTemplate::showFileLoadDialog(const QString &directory) - { - return this->ps_loadJson(directory); - } - - CStatusMessage CViewBaseNonTemplate::showFileSaveDialog(bool selectedOnly, const QString &directory) - { - return this->ps_saveJson(selectedOnly, directory); - } - - IMenuDelegate *CViewBaseNonTemplate::setCustomMenu(IMenuDelegate *menu, bool nestPreviousMenu) - { - if (menu && nestPreviousMenu) - { - // new menu with nesting - menu->setNestedDelegate(m_menu); - m_menu = menu; - } - else if (!menu && nestPreviousMenu) - { - // nested new menu - m_menu = m_menu->getNestedDelegate(); - } - else - { - // no nesting - m_menu = menu; - } - return menu; - } - - CMenuActions CViewBaseNonTemplate::initMenuActions(CViewBaseNonTemplate::MenuFlag menu) - { - if (m_menuFlagActions.contains(menu)) { return m_menuFlagActions.value(menu); } - - CMenuActions ma; - switch (menu) - { - case MenuRefresh: - { - static const QMetaMethod requestSignal = QMetaMethod::fromSignal(&CViewBaseNonTemplate::requestUpdate); - if (!this->isSignalConnected(requestSignal)) break; - ma.addAction(CIcons::refresh16(), "Update", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::ps_triggerReload }); break; - } - case MenuBackend: - { - static const QMetaMethod requestSignal = QMetaMethod::fromSignal(&CViewBaseNonTemplate::requestNewBackendData); - if (!this->isSignalConnected(requestSignal)) break; - ma.addAction(CIcons::refresh16(), "Reload from backend", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::ps_triggerReloadFromBackend }); break; - } - case MenuDisplayAutomatically: - { - QAction *a = ma.addAction(CIcons::appMappings16(), "Automatically display (when loaded)", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::toggleAutoDisplay }); - a->setCheckable(true); - a->setChecked(this->displayAutomatically()); - break; - } - case MenuRemoveSelectedRows: { ma.addAction(CIcons::delete16(), "Remove selected rows", CMenuAction::pathViewAddRemove(), { this, &CViewBaseNonTemplate::removeSelectedRowsChecked }, CShortcut::keyDelete()); break; } - case MenuClear: { ma.addAction(CIcons::delete16(), "Clear", CMenuAction::pathViewAddRemove(), { this, &CViewBaseNonTemplate::clear }); break; } - case MenuFilter: - { - if (m_filterWidget) - { - const bool dialog = qobject_cast(m_filterWidget); - if (dialog) ma.addAction(CIcons::filter16(), "Show filter " + CShortcut::toParenthesisString(CShortcut::keyDisplayFilter()), CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::displayFilterDialog }, CShortcut::keyDisplayFilter()); - ma.addAction(CIcons::filter16(), "Remove Filter", CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::ps_removeFilter }); - } - break; - } - case MenuMaterializeFilter: { ma.addAction(CIcons::tableRelationship16(), "Materialize filtered data", CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::materializeFilter }); break; } - case MenuLoad: { ma.addAction(CIcons::disk16(), "Load from file ", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::loadJsonAction }); break; } - case MenuSave: - { - ma.addAction(CIcons::disk16(), "Save data in file " + CShortcut::toParenthesisString(CShortcut::keySaveViews()), CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::saveJsonAction }, CShortcut::keySaveViews()); - if (this->hasSelection()) - { - ma.addAction(CIcons::disk16(), "Save selected data in file", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::saveSelectedJsonAction }); break; - } - break; - } - case MenuCut: - { - if (!QApplication::clipboard()) break; - ma.addAction(CIcons::cut16(), "Cut", CMenuAction::pathViewCutPaste(), { this, &CViewBaseNonTemplate::cut }, QKeySequence(QKeySequence::Paste)); - break; - } - case MenuPaste: - { - if (!QApplication::clipboard()) break; - ma.addAction(CIcons::paste16(), "Paste", CMenuAction::pathViewCutPaste(), { this, &CViewBaseNonTemplate::paste }, QKeySequence(QKeySequence::Paste)); - break; - } - case MenuCopy: - { - if (!QApplication::clipboard()) break; - ma.addAction(CIcons::copy16(), "Copy", CMenuAction::pathViewCutPaste(), { this, &CViewBaseNonTemplate::copy }, QKeySequence(QKeySequence::Copy)); - break; - } - default: - break; - } - m_menuFlagActions.insert(menu, ma); - return ma; - } - - void CViewBaseNonTemplate::settingsChanged() - { - if (!this->allowsMultipleSelectedRows()) { return; } - const CGeneralGuiSettings settings = m_guiSettings.getThreadLocal(); - m_originalSelectionMode = settings.getPreferredSelection(); - if (this->isCurrentlyAllowingMultipleRowSelections()) - { - this->setSelectionMode(settings.getPreferredSelection()); - } - } - - void CViewBaseNonTemplate::rememberLastJsonDirectory(const QString &selectedFileOrDir) - { - if (selectedFileOrDir.isEmpty()) { return; } - const QString dir = CDirectories::fileNameToDirectory(selectedFileOrDir); - QDir d(dir); - if (!d.exists()) { return; } - - // existing dir - CDirectories directories = m_dirSettings.get(); - directories.setPropertyByIndex(m_dirSettingsIndex, CVariant::fromValue(dir)); - const CStatusMessage msg = m_dirSettings.setAndSave(directories); - CLogMessage::preformatted(msg); - } - - QString CViewBaseNonTemplate::getRememberedLastJsonDirectory() const - { - const CDirectories directories = m_dirSettings.get(); - return directories.propertyByIndex(m_dirSettingsIndex).toQString(); - } - - Components::CTextEditDialog *CViewBaseNonTemplate::textEditDialog() - { - if (!m_textEditDialog) - { - m_textEditDialog = new CTextEditDialog(this); - } - return m_textEditDialog; - } - - void CViewBaseNonTemplate::customMenu(CMenuActions &menuActions) - { - // delegate? - if (m_menu) { m_menu->customMenu(menuActions); } - - // standard view menus - if (m_menus.testFlag(MenuRefresh)) { menuActions.addActions(this->initMenuActions(MenuRefresh)); } - if (m_menus.testFlag(MenuBackend)) { menuActions.addActions(this->initMenuActions(MenuBackend)); } - if (m_showingLoadIndicator) - { - // just in case, if this ever will be dangling - menuActions.addAction(CIcons::preloader16(), "Hide load indicator", CMenuAction::pathViewUpdates(), nullptr, { this, &CViewBaseNonTemplate::hideLoacIndicatorForced }); - } - - if (m_menus.testFlag(MenuClear)) { menuActions.addActions(this->initMenuActions(MenuClear)); } - if (m_menus.testFlag(MenuDisplayAutomatically)) - { - // here I expect only one action - QAction *a = menuActions.addActions(this->initMenuActions(MenuDisplayAutomatically)).first(); - a->setChecked(this->displayAutomatically()); - } - if (m_menus.testFlag(MenuRemoveSelectedRows)) - { - if (this->hasSelection()) - { - menuActions.addActions(this->initMenuActions(MenuRemoveSelectedRows)); - } - } - - if (m_menus.testFlag(MenuCopy)) { menuActions.addActions(this->initMenuActions(MenuCopy)); } - if (m_menus.testFlag(MenuCut)) { menuActions.addActions(this->initMenuActions(MenuCut)); } - if (m_menus.testFlag(MenuPaste)) { menuActions.addActions(this->initMenuActions(MenuPaste)); } - if (m_menus.testFlag(MenuFont) && m_fontMenu) - { - menuActions.addActions(m_fontMenu->getActions(), CMenuAction::pathFont()); - } - - if (m_menus.testFlag(MenuFilter) && m_filterWidget) - { - menuActions.addActions(this->initMenuActions(MenuFilter)); - if (m_menus.testFlag(MenuMaterializeFilter)) - { - menuActions.addActions(this->initMenuActions(MenuMaterializeFilter)); - } - } - - // selection menus, not in menu action list because it depends on current selection - const SelectionMode sm = this->selectionMode(); - if (sm == MultiSelection || sm == ExtendedSelection) - { - menuActions.addAction("Select all", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::selectAll }, Qt::CTRL + Qt::Key_A); - } - if (sm != NoSelection) - { - menuActions.addAction("Clear selection " + CShortcut::toParenthesisString(CShortcut::keyClearSelection()), CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::clearSelection }, CShortcut::keyClearSelection()); - } - if ((m_originalSelectionMode == MultiSelection || m_originalSelectionMode == ExtendedSelection) && m_menus.testFlag(MenuToggleSelectionMode)) - { - if (sm != MultiSelection) - { - menuActions.addAction("Switch to multi selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::setMultiSelection }); - } - - if (sm != ExtendedSelection) - { - menuActions.addAction("Switch to extended selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::setExtendedSelection }); - } - - if (sm != SingleSelection) - { - menuActions.addAction("Switch to single selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::setSingleSelection }); - } - } - - // load/save - if (m_menus.testFlag(MenuLoad)) { menuActions.addActions(this->initMenuActions(MenuLoad)); } - if (m_menus.testFlag(MenuSave) && !isEmpty()) { menuActions.addActions(this->initMenuActions(MenuSave)); } - - // resizing - menuActions.addAction(CIcons::resize16(), "&Resize " + CShortcut::toParenthesisString(CShortcut::keyResizeView()), CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::presizeOrFullResizeToContents }); - - // resize to content might decrease performance, - // so I only allow changing to "content resizing" if size matches - const bool enabled = !this->reachedResizeThreshold(); - const bool autoResize = (m_resizeMode == ResizingAuto); - - // when not set to auto, then lets set how we want to resize rows - if (m_rowResizeMode == Interactive) - { - QAction *a = menuActions.addAction(CIcons::resizeVertical16(), " Resize rows to content (auto), can be slow", CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::rowsResizeModeToContent }); - a->setEnabled(enabled && !autoResize); - } - else - { - QAction *a = menuActions.addAction(CIcons::resizeVertical16(), "Resize rows interactively", CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::rowsResizeModeToInteractive }); - a->setEnabled(!autoResize); - } - - // export actions, display in text edit - if (CBuildConfig::isLocalDeveloperDebugBuild()) - { - menuActions.addAction(CIcons::tableSheet16(), "Display as JSON", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::displayJsonPopup }); - if (this->hasSelection()) - { - menuActions.addAction(CIcons::tableSheet16(), "Display selected as JSON", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::displaySelectedJsonPopup });; - } - } - - QAction *actionInteractiveResize = menuActions.addAction(CIcons::viewMultiColumn(), "Resize (auto)", CMenuAction::pathViewResize(), nullptr); - actionInteractiveResize->setObjectName(this->objectName().append("ActionResizing")); - actionInteractiveResize->setCheckable(true); - actionInteractiveResize->setChecked(autoResize); - actionInteractiveResize->setEnabled(enabled); - connect(actionInteractiveResize, &QAction::toggled, this, &CViewBaseNonTemplate::toggleResizeMode); - } - - void CViewBaseNonTemplate::resizeEvent(QResizeEvent *event) - { - if (this->isShowingLoadIndicator()) - { - // re-center - this->centerLoadIndicator(); - } - QTableView::resizeEvent(event); - } - - int CViewBaseNonTemplate::getHorizontalHeaderFontHeight() const - { - const QFontMetrics m(this->getHorizontalHeaderFont()); - const int h = m.height(); - return h; - } - - bool CViewBaseNonTemplate::hasSelection() const - { - return this->selectionModel()->hasSelection(); - } - - QModelIndexList CViewBaseNonTemplate::selectedRows() const - { - return this->selectionModel()->selectedRows(); - } - - QModelIndexList CViewBaseNonTemplate::unselectedRows() const - { - QModelIndexList selected = this->selectedRows(); - QModelIndexList unselected; - const int rows = this->rowCount(); - for (int r = 0; r < rows; r++) - { - const QModelIndex mi = this->model()->index(r, 0); - if (selected.contains(mi)) { continue; } - unselected.push_back(mi); - } - return unselected; - } - - void CViewBaseNonTemplate::selectRows(const QSet &rows) - { - if (!this->selectionModel()) { return; } - - // multiple times faster than multiple than this->selectRow() - this->clearSelection(); - QItemSelection selectedItems; - const int columns = this->model()->columnCount() - 1; - for (int r : rows) - { - selectedItems.select(this->model()->index(r, 0), this->model()->index(r, columns)); - } - this->selectionModel()->select(selectedItems, QItemSelectionModel::Select); - } - - int CViewBaseNonTemplate::selectedRowCount() const - { - if (!this->hasSelection()) { return 0;} - return this->selectedRows().count(); - } - - int CViewBaseNonTemplate::unselectedRowCount() const - { - return this->rowCount() - this->selectedRowCount(); - } - - bool CViewBaseNonTemplate::hasSingleSelectedRow() const - { - return this->selectedRowCount() == 1; - } - - bool CViewBaseNonTemplate::hasMultipleSelectedRows() const - { - return this->selectedRowCount() > 1; - } - - bool CViewBaseNonTemplate::allowsMultipleSelectedRows() const - { - return m_originalSelectionMode == ExtendedSelection || m_originalSelectionMode == MultiSelection; - } - - bool CViewBaseNonTemplate::isCurrentlyAllowingMultipleRowSelections() const - { - QAbstractItemView::SelectionMode m = this->selectionMode(); - return m == QAbstractItemView::MultiSelection || m == QAbstractItemView::ExtendedSelection; - } - - void CViewBaseNonTemplate::init() - { - this->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // faster mode - this->horizontalHeader()->setStretchLastSection(true); - const int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); - this->verticalHeader()->setDefaultSectionSize(fh); // for height - this->verticalHeader()->setMinimumSectionSize(fh); // for height - - switch (m_rowResizeMode) - { - case Interactive: this->rowsResizeModeToInteractive(); break; - case Content: this->rowsResizeModeToContent(); break; - default: - Q_ASSERT_X(false, Q_FUNC_INFO, "wrong resize mode"); - break; - } - - // call this deferred, otherwise the values are overridden with any values - // from the UI builder - const QPointer guard(this); - QTimer::singleShot(500, this, [ = ]() - { - if (!guard) { return; } - CViewBaseNonTemplate::settingsChanged(); - }); - } - - QString CViewBaseNonTemplate::getFileDialogFileName(bool load) const - { - // some logic to find a useful default name - if (load) - { - return CFileUtils::appendFilePaths(this->getRememberedLastJsonDirectory(), CFileUtils::jsonWildcardAppendix()); - } - - // Save file path - const QString dir = m_dirSettings.get().propertyByIndex(m_dirSettingsIndex).toQString(); - QString name(m_saveFileName); - if (name.isEmpty()) - { - // create a name - if (this->getDockWidgetInfoArea()) - { - name = this->getDockWidgetInfoArea()->windowTitle(); - } - else - { - name = this->metaObject()->className(); - } - } - if (!name.endsWith(CFileUtils::jsonAppendix(), Qt::CaseInsensitive)) - { - name += CFileUtils::jsonAppendix(); - } - return CFileUtils::appendFilePaths(dir, name); - } - - void CViewBaseNonTemplate::menuRemoveItems(Menu menusToRemove) - { - m_menus &= (~menusToRemove); - } - - void CViewBaseNonTemplate::menuAddItems(Menu menusToAdd) - { - m_menus |= menusToAdd; - if (menusToAdd.testFlag(MenuRemoveSelectedRows)) - { - m_enableDeleteSelectedRows = true; - } - } - - void CViewBaseNonTemplate::displayFilterDialog() - { - if (!m_menus.testFlag(MenuFilter)) { return; } - if (!m_filterWidget) { return; } - m_filterWidget->show(); - } - - void CViewBaseNonTemplate::loadJsonAction() - { - if (!m_menus.testFlag(MenuLoad)) { return; } - const CStatusMessage m = this->ps_loadJson(); - if (!m.isEmpty()) - { - CLogMessage::preformatted(m); - } - } - - void CViewBaseNonTemplate::saveJsonAction() - { - if (this->isEmpty()) { return; } - if (!m_menus.testFlag(MenuSave)) { return; } - const CStatusMessage m = this->ps_saveJson(false); - if (!m.isEmpty()) - { - CLogMessage::preformatted(m); - } - } - - void CViewBaseNonTemplate::saveSelectedJsonAction() - { - if (this->isEmpty()) { return; } - if (!m_menus.testFlag(MenuSave)) { return; } - const CStatusMessage m = this->ps_saveJson(true); - if (!m.isEmpty()) - { - CLogMessage::preformatted(m); - } - } - - void CViewBaseNonTemplate::displayJsonPopup() - { - - } - - void CViewBaseNonTemplate::displaySelectedJsonPopup() - { - - } - - void CViewBaseNonTemplate::ps_triggerReload() - { - this->showLoadIndicatorWithTimeout(m_loadIndicatorTimeoutMsDefault); - emit this->requestUpdate(); - } - - void CViewBaseNonTemplate::ps_triggerReloadFromBackend() - { - this->showLoadIndicatorWithTimeout(m_loadIndicatorTimeoutMsDefault); - emit this->requestNewBackendData(); - } - - void CViewBaseNonTemplate::onModelChanged() - { - this->updateSortIndicator(); - } - - void CViewBaseNonTemplate::rowsResizeModeToInteractive() - { - const int height = this->verticalHeader()->minimumSectionSize(); - QHeaderView *verticalHeader = this->verticalHeader(); - Q_ASSERT_X(verticalHeader, Q_FUNC_INFO, "Missing vertical header"); - verticalHeader->setSectionResizeMode(QHeaderView::Interactive); - verticalHeader->setDefaultSectionSize(height); - m_rowResizeMode = Interactive; - } - - void CViewBaseNonTemplate::rowsResizeModeToContent() - { - QHeaderView *verticalHeader = this->verticalHeader(); - Q_ASSERT(verticalHeader); - verticalHeader->setSectionResizeMode(QHeaderView::ResizeToContents); - m_rowResizeMode = Content; - } - - void CViewBaseNonTemplate::rowsResizeModeBasedOnThreshold(int elements) - { - if (elements > ResizeRowsToContentThreshold) - { - this->rowsResizeModeToInteractive(); - } - else - { - this->rowsResizeModeToContent(); - } - } - - int CViewBaseNonTemplate::showLoadIndicator(int containerSizeDependent, int timeoutMs, bool processEvents) - { - if (!m_enabledLoadIndicator) { return -1; } - if (m_showingLoadIndicator) { return -1; } - if (this->hasDockWidgetArea()) - { - if (!this->isVisibleWidget()) { return -1; } - } - - if (containerSizeDependent >= 0) - { - // really with indicator? - if (containerSizeDependent < ResizeSubsetThreshold) { return -1; } - } - m_showingLoadIndicator = true; - emit this->loadIndicatorVisibilityChanged(m_showingLoadIndicator); - - if (!m_loadIndicator) - { - m_loadIndicator = new CLoadIndicator(64, 64, this); - } - this->centerLoadIndicator(); - return m_loadIndicator->startAnimation(timeoutMs > 0 ? timeoutMs : m_loadIndicatorTimeoutMsDefault, processEvents); - } - - int CViewBaseNonTemplate::showLoadIndicatorWithTimeout(int timeoutMs, bool processEvents) - { - return this->showLoadIndicator(-1, timeoutMs, processEvents); - } - - void CViewBaseNonTemplate::centerLoadIndicator() - { - if (!m_loadIndicator) { return; } - const QPoint middle = this->viewport()->geometry().center(); - m_loadIndicator->centerLoadIndicator(middle); - } - - void CViewBaseNonTemplate::hideLoadIndicator(int loadingId) - { - if (!m_showingLoadIndicator) { return; } - m_showingLoadIndicator = false; - emit this->loadIndicatorVisibilityChanged(m_showingLoadIndicator); - if (!m_loadIndicator) { return; } - m_loadIndicator->stopAnimation(loadingId); - } - - bool CViewBaseNonTemplate::isResizeConditionMet(int containerSize) const - { - if (m_resizeMode == ResizingAlways) { return true; } - if (m_resizeMode == PresizeSubset) { return false; } - if (m_resizeMode == ResizingOff) { return false; } - if (m_resizeMode == ResizingOnce) { return m_resizeCount < 1; } - if (m_resizeMode == ResizingAuto) - { - if (reachedResizeThreshold(containerSize)) { return false; } - if (m_resizeAutoNthTime < 2) { return true; } - return (m_resizeCount % m_resizeAutoNthTime) == 0; - } - return false; - } - - void CViewBaseNonTemplate::fullResizeToContents() - { - // resize to maximum magic trick from: - // http://stackoverflow.com/q/3433664/356726 - this->setVisible(false); - const QRect vpOriginal = this->viewport()->geometry(); - if (m_forceColumnsToMaxSize) - { - // vpNew.setWidth(std::numeric_limits::max()); // largest finite value - const QRect screenGeometry = QApplication::desktop()->screenGeometry(); - QRect vpNew = vpOriginal; - vpNew.setWidth(screenGeometry.width()); - this->viewport()->setGeometry(vpNew); - } - - this->resizeColumnsToContents(); // columns - - // useless if mode is Interactive - if (m_rowResizeMode == Content) - { - this->resizeRowsToContents(); // rows - } - m_resizeCount++; - - // re-stretch - if (m_forceStretchLastColumnWhenResized) { this->horizontalHeader()->setStretchLastSection(true); } - if (m_forceColumnsToMaxSize) { this->viewport()->setGeometry(vpOriginal); } - - // if I store the original visibility and then - // set it back here, the whole view disappears - this->setVisible(true); - } - - void CViewBaseNonTemplate::customMenuRequested(QPoint pos) - { - QMenu menu; - CMenuActions menuActions; - this->customMenu(menuActions); - if (menuActions.isEmpty()) { return; } - menuActions.toQMenu(menu, true); - - // Nested dock widget menu - const CDockWidgetInfoArea *dockWidget = this->getDockWidgetInfoArea(); - if (dockWidget) - { - if (!menu.isEmpty()) { menu.addSeparator(); } - const QString mm = QString("Dock widget '%1'").arg(dockWidget->windowTitleOrBackup()); - QMenu *dockWidgetSubMenu = menu.addMenu(CIcons::text16(), mm); - dockWidget->addToContextMenu(dockWidgetSubMenu); - } - - const QPoint globalPos = this->mapToGlobal(pos); - menu.exec(globalPos); - } - - void CViewBaseNonTemplate::toggleResizeMode(bool checked) - { - m_resizeMode = checked ? ResizingAuto : ResizingOff; - } - - void CViewBaseNonTemplate::toggleAutoDisplay() - { - const QAction *a = qobject_cast(QObject::sender()); - if (!a) { return; } - Q_ASSERT_X(a->isCheckable(), Q_FUNC_INFO, "object not checkable"); - m_displayAutomatically = a->isChecked(); - } - - void CViewBaseNonTemplate::setSingleSelection() - { - this->setSelectionMode(SingleSelection); - } - - void CViewBaseNonTemplate::setExtendedSelection() - { - if (this->allowsMultipleSelectedRows()) - { - this->setSelectionMode(ExtendedSelection); - } - } - - void CViewBaseNonTemplate::setMultiSelection() - { - if (this->allowsMultipleSelectedRows()) - { - this->setSelectionMode(MultiSelection); - } - } - - void CViewBaseNonTemplate::removeSelectedRowsChecked() - { - if (!m_enableDeleteSelectedRows) { return; } - this->removeSelectedRows(); - } - - void CViewBaseNonTemplate::updatedIndicator() - { - this->update(); - } - - void CViewBaseNonTemplate::dragEnterEvent(QDragEnterEvent *event) - { - if (!event || !this->acceptDrop(event->mimeData())) { return; } - this->setBackgroundRole(QPalette::Highlight); - event->acceptProposedAction(); - } - - void CViewBaseNonTemplate::dragMoveEvent(QDragMoveEvent *event) - { - if (!event || !this->acceptDrop(event->mimeData())) { return; } - event->acceptProposedAction(); - } - - void CViewBaseNonTemplate::dragLeaveEvent(QDragLeaveEvent *event) - { - if (!event) { return; } - event->accept(); - } - - void CViewBaseNonTemplate::dropEvent(QDropEvent *event) - { - if (!event) { return; } - QTableView::dropEvent(event); - } - template CViewBase::CViewBase(QWidget *parent, ModelClass *model) : CViewBaseNonTemplate(parent), m_model(model) { @@ -973,13 +95,6 @@ namespace BlackGui return c; } - int CViewBaseNonTemplate::getPresizeRandomElementsSize(int containerSize) const - { - containerSize = containerSize >= 0 ? containerSize : this->rowCount(); - const int presizeRandomElements = containerSize > 1000 ? containerSize / 100 : containerSize / 40; - return presizeRandomElements; - } - template CWorker *CViewBase::updateContainerAsync(const ContainerType &container, bool sort, bool resize) { @@ -1802,34 +917,5 @@ namespace BlackGui if (!index.isValid()) { return; } emit objectSelected(CVariant::fromValue(at(index))); } - - // see here for the reason of thess forward instantiations - // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - template class CViewBase; - } // namespace } // namespace diff --git a/src/blackgui/views/viewbaseaviation.cpp b/src/blackgui/views/viewbaseaviation.cpp new file mode 100644 index 000000000..3d04e67e7 --- /dev/null +++ b/src/blackgui/views/viewbaseaviation.cpp @@ -0,0 +1,26 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "viewbase.cpp" + +namespace BlackGui +{ + namespace Views + { + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + } // namespace +} // namespace diff --git a/src/blackgui/views/viewbasemisc.cpp b/src/blackgui/views/viewbasemisc.cpp new file mode 100644 index 000000000..4723f2d23 --- /dev/null +++ b/src/blackgui/views/viewbasemisc.cpp @@ -0,0 +1,22 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "viewbase.cpp" + +namespace BlackGui +{ + namespace Views + { + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + } // namespace +} // namespace diff --git a/src/blackgui/views/viewbasenetwork.cpp b/src/blackgui/views/viewbasenetwork.cpp new file mode 100644 index 000000000..25f44643a --- /dev/null +++ b/src/blackgui/views/viewbasenetwork.cpp @@ -0,0 +1,21 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "viewbase.cpp" + +namespace BlackGui +{ + namespace Views + { + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + } // namespace +} // namespace diff --git a/src/blackgui/views/viewbasenontemplate.cpp b/src/blackgui/views/viewbasenontemplate.cpp new file mode 100644 index 000000000..8e2033384 --- /dev/null +++ b/src/blackgui/views/viewbasenontemplate.cpp @@ -0,0 +1,889 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackconfig/buildconfig.h" +#include "blackgui/components/texteditdialog.h" +#include "blackgui/dockwidgetinfoarea.h" +#include "blackgui/filters/filterdialog.h" +#include "blackgui/filters/filterwidget.h" +#include "blackgui/guiutility.h" +#include "blackgui/loadindicator.h" +#include "blackgui/menus/fontmenus.h" +#include "blackgui/menus/menudelegate.h" +#include "blackgui/views/viewbase.h" +#include "blackgui/shortcut.h" +#include "blackmisc/logmessage.h" + +#include +#include +#include +#include +#include + +using namespace BlackConfig; +using namespace BlackMisc; +using namespace BlackGui; +using namespace BlackGui::Menus; +using namespace BlackGui::Models; +using namespace BlackGui::Filters; +using namespace BlackGui::Settings; +using namespace BlackGui::Components; + +namespace BlackGui +{ + namespace Views + { + CViewBaseNonTemplate::CViewBaseNonTemplate(QWidget *parent) : + QTableView(parent) + { + this->setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, &QWidget::customContextMenuRequested, this, &CViewBaseNonTemplate::customMenuRequested); + connect(this, &QTableView::clicked, this, &CViewBaseNonTemplate::ps_clicked); + connect(this, &QTableView::doubleClicked, this, &CViewBaseNonTemplate::ps_doubleClicked); + this->horizontalHeader()->setSortIndicatorShown(true); + + // setting resize mode rowsResizeModeToContent() causes extremly slow views + // default, see: m_rowResizeMode + + // scroll modes + this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + this->setWordWrap(true); + this->setTextElideMode(Qt::ElideMiddle); + + // shortcuts + QShortcut *filter = new QShortcut(CShortcut::keyDisplayFilter(), this); + connect(filter, &QShortcut::activated, this, &CViewBaseNonTemplate::displayFilterDialog); + filter->setObjectName("Filter shortcut for " + this->objectName()); + filter->setContext(Qt::WidgetShortcut); + + QShortcut *clearSelection = new QShortcut(CShortcut::keyClearSelection(), this); + connect(clearSelection, &QShortcut::activated, this, &CViewBaseNonTemplate::clearSelection); + clearSelection->setObjectName("Clear selection shortcut for " + this->objectName()); + clearSelection->setContext(Qt::WidgetShortcut); + + QShortcut *saveJson = new QShortcut(CShortcut::keySaveViews(), this); + connect(saveJson, &QShortcut::activated, this, &CViewBaseNonTemplate::saveJsonAction); + saveJson->setObjectName("Save JSON for " + this->objectName()); + saveJson->setContext(Qt::WidgetShortcut); + + QShortcut *deleteRow = new QShortcut(CShortcut::keyDelete(), this); + connect(deleteRow, &QShortcut::activated, this, &CViewBaseNonTemplate::removeSelectedRowsChecked); + deleteRow->setObjectName("Remove selected rows for " + this->objectName()); + deleteRow->setContext(Qt::WidgetShortcut); + + QShortcut *copy = new QShortcut(CShortcut::keyCopy(), this); + connect(copy, &QShortcut::activated, this, &CViewBaseNonTemplate::copy); + copy->setObjectName("Copy selection shortcut for " + this->objectName()); + copy->setContext(Qt::WidgetShortcut); + + QShortcut *resize = new QShortcut(CShortcut::keyResizeView(), this); + connect(resize, &QShortcut::activated, this, &CViewBaseNonTemplate::fullResizeToContents); + resize->setObjectName("Resize view shortcut for " + this->objectName()); + resize->setContext(Qt::WidgetShortcut); + } + + CViewBaseNonTemplate::~CViewBaseNonTemplate() + { + // dtor + } + + bool CViewBaseNonTemplate::setParentDockWidgetInfoArea(CDockWidgetInfoArea *parentDockableWidget) + { + const bool c = CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget); + return c; + } + + void CViewBaseNonTemplate::resizeToContents() + { + this->performModeBasedResizeToContent(); + } + + void CViewBaseNonTemplate::setFilterWidgetImpl(QWidget *filterWidget) + { + if (filterWidget == m_filterWidget) { return; } + + // dialog or filter widget + if (m_filterWidget) + { + disconnect(m_filterWidget); + this->menuRemoveItems(MenuFilter); + if (m_filterWidget->parent() == this) { m_filterWidget->deleteLater(); } + m_filterWidget = nullptr; + } + + if (filterWidget) + { + this->menuAddItems(MenuFilter); + m_filterWidget = filterWidget; + } + } + + void CViewBaseNonTemplate::setFilterDialog(CFilterDialog *filterDialog) + { + if (filterDialog == m_filterWidget) { return; } + this->setFilterWidgetImpl(filterDialog); + if (filterDialog) + { + const bool s = connect(filterDialog, &CFilterDialog::finished, this, &CViewBaseNonTemplate::ps_filterDialogFinished); + Q_ASSERT_X(s, Q_FUNC_INFO, "filter dialog connect"); + Q_UNUSED(s); + } + } + + void CViewBaseNonTemplate::setFilterWidget(CFilterWidget *filterWidget) + { + if (filterWidget == m_filterWidget) { return; } + this->setFilterWidgetImpl(filterWidget); + if (filterWidget) + { + bool s = connect(filterWidget, &CFilterWidget::changeFilter, this, &CViewBaseNonTemplate::ps_filterWidgetChangedFilter); + Q_ASSERT_X(s, Q_FUNC_INFO, "filter connect"); + s = connect(this, &CViewBaseNonTemplate::modelDataChanged, filterWidget, &CFilterWidget::onRowCountChanged); + Q_ASSERT_X(s, Q_FUNC_INFO, "filter connect"); + Q_UNUSED(s); + } + } + + void CViewBaseNonTemplate::enableLoadIndicator(bool enable) + { + m_enabledLoadIndicator = enable; + } + + bool CViewBaseNonTemplate::isShowingLoadIndicator() const + { + return m_loadIndicator && m_enabledLoadIndicator && m_showingLoadIndicator; + } + + void CViewBaseNonTemplate::setSelectionModel(QItemSelectionModel *model) + { + if (this->selectionModel()) { disconnect(this->selectionModel()); } + QTableView::setSelectionModel(model); + if (this->selectionModel()) + { + connect(this->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &CViewBaseNonTemplate::ps_rowSelected); + } + } + + QWidget *CViewBaseNonTemplate::mainApplicationWindowWidget() const + { + return CGuiUtility::mainApplicationWidget(); + } + + CStatusMessage CViewBaseNonTemplate::showFileLoadDialog(const QString &directory) + { + return this->ps_loadJson(directory); + } + + CStatusMessage CViewBaseNonTemplate::showFileSaveDialog(bool selectedOnly, const QString &directory) + { + return this->ps_saveJson(selectedOnly, directory); + } + + IMenuDelegate *CViewBaseNonTemplate::setCustomMenu(IMenuDelegate *menu, bool nestPreviousMenu) + { + if (menu && nestPreviousMenu) + { + // new menu with nesting + menu->setNestedDelegate(m_menu); + m_menu = menu; + } + else if (!menu && nestPreviousMenu) + { + // nested new menu + m_menu = m_menu->getNestedDelegate(); + } + else + { + // no nesting + m_menu = menu; + } + return menu; + } + + CMenuActions CViewBaseNonTemplate::initMenuActions(CViewBaseNonTemplate::MenuFlag menu) + { + if (m_menuFlagActions.contains(menu)) { return m_menuFlagActions.value(menu); } + + CMenuActions ma; + switch (menu) + { + case MenuRefresh: + { + static const QMetaMethod requestSignal = QMetaMethod::fromSignal(&CViewBaseNonTemplate::requestUpdate); + if (!this->isSignalConnected(requestSignal)) break; + ma.addAction(CIcons::refresh16(), "Update", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::ps_triggerReload }); break; + } + case MenuBackend: + { + static const QMetaMethod requestSignal = QMetaMethod::fromSignal(&CViewBaseNonTemplate::requestNewBackendData); + if (!this->isSignalConnected(requestSignal)) break; + ma.addAction(CIcons::refresh16(), "Reload from backend", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::ps_triggerReloadFromBackend }); break; + } + case MenuDisplayAutomatically: + { + QAction *a = ma.addAction(CIcons::appMappings16(), "Automatically display (when loaded)", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::toggleAutoDisplay }); + a->setCheckable(true); + a->setChecked(this->displayAutomatically()); + break; + } + case MenuRemoveSelectedRows: { ma.addAction(CIcons::delete16(), "Remove selected rows", CMenuAction::pathViewAddRemove(), { this, &CViewBaseNonTemplate::removeSelectedRowsChecked }, CShortcut::keyDelete()); break; } + case MenuClear: { ma.addAction(CIcons::delete16(), "Clear", CMenuAction::pathViewAddRemove(), { this, &CViewBaseNonTemplate::clear }); break; } + case MenuFilter: + { + if (m_filterWidget) + { + const bool dialog = qobject_cast(m_filterWidget); + if (dialog) ma.addAction(CIcons::filter16(), "Show filter " + CShortcut::toParenthesisString(CShortcut::keyDisplayFilter()), CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::displayFilterDialog }, CShortcut::keyDisplayFilter()); + ma.addAction(CIcons::filter16(), "Remove Filter", CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::ps_removeFilter }); + } + break; + } + case MenuMaterializeFilter: { ma.addAction(CIcons::tableRelationship16(), "Materialize filtered data", CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::materializeFilter }); break; } + case MenuLoad: { ma.addAction(CIcons::disk16(), "Load from file ", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::loadJsonAction }); break; } + case MenuSave: + { + ma.addAction(CIcons::disk16(), "Save data in file " + CShortcut::toParenthesisString(CShortcut::keySaveViews()), CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::saveJsonAction }, CShortcut::keySaveViews()); + if (this->hasSelection()) + { + ma.addAction(CIcons::disk16(), "Save selected data in file", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::saveSelectedJsonAction }); break; + } + break; + } + case MenuCut: + { + if (!QApplication::clipboard()) break; + ma.addAction(CIcons::cut16(), "Cut", CMenuAction::pathViewCutPaste(), { this, &CViewBaseNonTemplate::cut }, QKeySequence(QKeySequence::Paste)); + break; + } + case MenuPaste: + { + if (!QApplication::clipboard()) break; + ma.addAction(CIcons::paste16(), "Paste", CMenuAction::pathViewCutPaste(), { this, &CViewBaseNonTemplate::paste }, QKeySequence(QKeySequence::Paste)); + break; + } + case MenuCopy: + { + if (!QApplication::clipboard()) break; + ma.addAction(CIcons::copy16(), "Copy", CMenuAction::pathViewCutPaste(), { this, &CViewBaseNonTemplate::copy }, QKeySequence(QKeySequence::Copy)); + break; + } + default: + break; + } + m_menuFlagActions.insert(menu, ma); + return ma; + } + + void CViewBaseNonTemplate::settingsChanged() + { + if (!this->allowsMultipleSelectedRows()) { return; } + const CGeneralGuiSettings settings = m_guiSettings.getThreadLocal(); + m_originalSelectionMode = settings.getPreferredSelection(); + if (this->isCurrentlyAllowingMultipleRowSelections()) + { + this->setSelectionMode(settings.getPreferredSelection()); + } + } + + void CViewBaseNonTemplate::rememberLastJsonDirectory(const QString &selectedFileOrDir) + { + if (selectedFileOrDir.isEmpty()) { return; } + const QString dir = CDirectories::fileNameToDirectory(selectedFileOrDir); + QDir d(dir); + if (!d.exists()) { return; } + + // existing dir + CDirectories directories = m_dirSettings.get(); + directories.setPropertyByIndex(m_dirSettingsIndex, CVariant::fromValue(dir)); + const CStatusMessage msg = m_dirSettings.setAndSave(directories); + CLogMessage::preformatted(msg); + } + + QString CViewBaseNonTemplate::getRememberedLastJsonDirectory() const + { + const CDirectories directories = m_dirSettings.get(); + return directories.propertyByIndex(m_dirSettingsIndex).toQString(); + } + + Components::CTextEditDialog *CViewBaseNonTemplate::textEditDialog() + { + if (!m_textEditDialog) + { + m_textEditDialog = new CTextEditDialog(this); + } + return m_textEditDialog; + } + + void CViewBaseNonTemplate::customMenu(CMenuActions &menuActions) + { + // delegate? + if (m_menu) { m_menu->customMenu(menuActions); } + + // standard view menus + if (m_menus.testFlag(MenuRefresh)) { menuActions.addActions(this->initMenuActions(MenuRefresh)); } + if (m_menus.testFlag(MenuBackend)) { menuActions.addActions(this->initMenuActions(MenuBackend)); } + if (m_showingLoadIndicator) + { + // just in case, if this ever will be dangling + menuActions.addAction(CIcons::preloader16(), "Hide load indicator", CMenuAction::pathViewUpdates(), nullptr, { this, &CViewBaseNonTemplate::hideLoacIndicatorForced }); + } + + if (m_menus.testFlag(MenuClear)) { menuActions.addActions(this->initMenuActions(MenuClear)); } + if (m_menus.testFlag(MenuDisplayAutomatically)) + { + // here I expect only one action + QAction *a = menuActions.addActions(this->initMenuActions(MenuDisplayAutomatically)).first(); + a->setChecked(this->displayAutomatically()); + } + if (m_menus.testFlag(MenuRemoveSelectedRows)) + { + if (this->hasSelection()) + { + menuActions.addActions(this->initMenuActions(MenuRemoveSelectedRows)); + } + } + + if (m_menus.testFlag(MenuCopy)) { menuActions.addActions(this->initMenuActions(MenuCopy)); } + if (m_menus.testFlag(MenuCut)) { menuActions.addActions(this->initMenuActions(MenuCut)); } + if (m_menus.testFlag(MenuPaste)) { menuActions.addActions(this->initMenuActions(MenuPaste)); } + if (m_menus.testFlag(MenuFont) && m_fontMenu) + { + menuActions.addActions(m_fontMenu->getActions(), CMenuAction::pathFont()); + } + + if (m_menus.testFlag(MenuFilter) && m_filterWidget) + { + menuActions.addActions(this->initMenuActions(MenuFilter)); + if (m_menus.testFlag(MenuMaterializeFilter)) + { + menuActions.addActions(this->initMenuActions(MenuMaterializeFilter)); + } + } + + // selection menus, not in menu action list because it depends on current selection + const SelectionMode sm = this->selectionMode(); + if (sm == MultiSelection || sm == ExtendedSelection) + { + menuActions.addAction("Select all", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::selectAll }, Qt::CTRL + Qt::Key_A); + } + if (sm != NoSelection) + { + menuActions.addAction("Clear selection " + CShortcut::toParenthesisString(CShortcut::keyClearSelection()), CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::clearSelection }, CShortcut::keyClearSelection()); + } + if ((m_originalSelectionMode == MultiSelection || m_originalSelectionMode == ExtendedSelection) && m_menus.testFlag(MenuToggleSelectionMode)) + { + if (sm != MultiSelection) + { + menuActions.addAction("Switch to multi selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::setMultiSelection }); + } + + if (sm != ExtendedSelection) + { + menuActions.addAction("Switch to extended selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::setExtendedSelection }); + } + + if (sm != SingleSelection) + { + menuActions.addAction("Switch to single selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::setSingleSelection }); + } + } + + // load/save + if (m_menus.testFlag(MenuLoad)) { menuActions.addActions(this->initMenuActions(MenuLoad)); } + if (m_menus.testFlag(MenuSave) && !isEmpty()) { menuActions.addActions(this->initMenuActions(MenuSave)); } + + // resizing + menuActions.addAction(CIcons::resize16(), "&Resize " + CShortcut::toParenthesisString(CShortcut::keyResizeView()), CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::presizeOrFullResizeToContents }); + + // resize to content might decrease performance, + // so I only allow changing to "content resizing" if size matches + const bool enabled = !this->reachedResizeThreshold(); + const bool autoResize = (m_resizeMode == ResizingAuto); + + // when not set to auto, then lets set how we want to resize rows + if (m_rowResizeMode == Interactive) + { + QAction *a = menuActions.addAction(CIcons::resizeVertical16(), " Resize rows to content (auto), can be slow", CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::rowsResizeModeToContent }); + a->setEnabled(enabled && !autoResize); + } + else + { + QAction *a = menuActions.addAction(CIcons::resizeVertical16(), "Resize rows interactively", CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::rowsResizeModeToInteractive }); + a->setEnabled(!autoResize); + } + + // export actions, display in text edit + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + menuActions.addAction(CIcons::tableSheet16(), "Display as JSON", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::displayJsonPopup }); + if (this->hasSelection()) + { + menuActions.addAction(CIcons::tableSheet16(), "Display selected as JSON", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::displaySelectedJsonPopup });; + } + } + + + QAction *actionInteractiveResize = menuActions.addAction(CIcons::viewMultiColumn(), "Resize (auto)", CMenuAction::pathViewResize(), nullptr); + actionInteractiveResize->setObjectName(this->objectName().append("ActionResizing")); + actionInteractiveResize->setCheckable(true); + actionInteractiveResize->setChecked(autoResize); + actionInteractiveResize->setEnabled(enabled); + connect(actionInteractiveResize, &QAction::toggled, this, &CViewBaseNonTemplate::toggleResizeMode); + } + + void CViewBaseNonTemplate::resizeEvent(QResizeEvent *event) + { + if (this->isShowingLoadIndicator()) + { + // re-center + this->centerLoadIndicator(); + } + QTableView::resizeEvent(event); + } + + int CViewBaseNonTemplate::getHorizontalHeaderFontHeight() const + { + const QFontMetrics m(this->getHorizontalHeaderFont()); + const int h = m.height(); + return h; + } + + bool CViewBaseNonTemplate::hasSelection() const + { + return this->selectionModel()->hasSelection(); + } + + QModelIndexList CViewBaseNonTemplate::selectedRows() const + { + return this->selectionModel()->selectedRows(); + } + + QModelIndexList CViewBaseNonTemplate::unselectedRows() const + { + QModelIndexList selected = this->selectedRows(); + QModelIndexList unselected; + const int rows = this->rowCount(); + for (int r = 0; r < rows; r++) + { + const QModelIndex mi = this->model()->index(r, 0); + if (selected.contains(mi)) { continue; } + unselected.push_back(mi); + } + return unselected; + } + + void CViewBaseNonTemplate::selectRows(const QSet &rows) + { + if (!this->selectionModel()) { return; } + + // multiple times faster than multiple than this->selectRow() + this->clearSelection(); + QItemSelection selectedItems; + const int columns = this->model()->columnCount() - 1; + for (int r : rows) + { + selectedItems.select(this->model()->index(r, 0), this->model()->index(r, columns)); + } + this->selectionModel()->select(selectedItems, QItemSelectionModel::Select); + } + + int CViewBaseNonTemplate::selectedRowCount() const + { + if (!this->hasSelection()) { return 0;} + return this->selectedRows().count(); + } + + int CViewBaseNonTemplate::unselectedRowCount() const + { + return this->rowCount() - this->selectedRowCount(); + } + + bool CViewBaseNonTemplate::hasSingleSelectedRow() const + { + return this->selectedRowCount() == 1; + } + + bool CViewBaseNonTemplate::hasMultipleSelectedRows() const + { + return this->selectedRowCount() > 1; + } + + bool CViewBaseNonTemplate::allowsMultipleSelectedRows() const + { + return m_originalSelectionMode == ExtendedSelection || m_originalSelectionMode == MultiSelection; + } + + bool CViewBaseNonTemplate::isCurrentlyAllowingMultipleRowSelections() const + { + QAbstractItemView::SelectionMode m = this->selectionMode(); + return m == QAbstractItemView::MultiSelection || m == QAbstractItemView::ExtendedSelection; + } + + void CViewBaseNonTemplate::init() + { + this->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // faster mode + this->horizontalHeader()->setStretchLastSection(true); + const int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); + this->verticalHeader()->setDefaultSectionSize(fh); // for height + this->verticalHeader()->setMinimumSectionSize(fh); // for height + + switch (m_rowResizeMode) + { + case Interactive: this->rowsResizeModeToInteractive(); break; + case Content: this->rowsResizeModeToContent(); break; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "wrong resize mode"); + break; + } + + // call this deferred, otherwise the values are overridden with any values + // from the UI builder + const QPointer guard(this); + QTimer::singleShot(500, this, [ = ]() + { + if (!guard) { return; } + CViewBaseNonTemplate::settingsChanged(); + }); + } + + QString CViewBaseNonTemplate::getFileDialogFileName(bool load) const + { + // some logic to find a useful default name + if (load) + { + return CFileUtils::appendFilePaths(this->getRememberedLastJsonDirectory(), CFileUtils::jsonWildcardAppendix()); + } + + // Save file path + const QString dir = m_dirSettings.get().propertyByIndex(m_dirSettingsIndex).toQString(); + QString name(m_saveFileName); + if (name.isEmpty()) + { + // create a name + if (this->getDockWidgetInfoArea()) + { + name = this->getDockWidgetInfoArea()->windowTitle(); + } + else + { + name = this->metaObject()->className(); + } + } + if (!name.endsWith(CFileUtils::jsonAppendix(), Qt::CaseInsensitive)) + { + name += CFileUtils::jsonAppendix(); + } + return CFileUtils::appendFilePaths(dir, name); + } + + void CViewBaseNonTemplate::menuRemoveItems(Menu menusToRemove) + { + m_menus &= (~menusToRemove); + } + + void CViewBaseNonTemplate::menuAddItems(Menu menusToAdd) + { + m_menus |= menusToAdd; + if (menusToAdd.testFlag(MenuRemoveSelectedRows)) + { + m_enableDeleteSelectedRows = true; + } + } + + void CViewBaseNonTemplate::displayFilterDialog() + { + if (!m_menus.testFlag(MenuFilter)) { return; } + if (!m_filterWidget) { return; } + m_filterWidget->show(); + } + + void CViewBaseNonTemplate::loadJsonAction() + { + if (!m_menus.testFlag(MenuLoad)) { return; } + const CStatusMessage m = this->ps_loadJson(); + if (!m.isEmpty()) + { + CLogMessage::preformatted(m); + } + } + + void CViewBaseNonTemplate::saveJsonAction() + { + if (this->isEmpty()) { return; } + if (!m_menus.testFlag(MenuSave)) { return; } + const CStatusMessage m = this->ps_saveJson(false); + if (!m.isEmpty()) + { + CLogMessage::preformatted(m); + } + } + + void CViewBaseNonTemplate::saveSelectedJsonAction() + { + if (this->isEmpty()) { return; } + if (!m_menus.testFlag(MenuSave)) { return; } + const CStatusMessage m = this->ps_saveJson(true); + if (!m.isEmpty()) + { + CLogMessage::preformatted(m); + } + } + + void CViewBaseNonTemplate::displayJsonPopup() + { + + } + + void CViewBaseNonTemplate::displaySelectedJsonPopup() + { + + } + + void CViewBaseNonTemplate::ps_triggerReload() + { + this->showLoadIndicatorWithTimeout(m_loadIndicatorTimeoutMsDefault); + emit this->requestUpdate(); + } + + void CViewBaseNonTemplate::ps_triggerReloadFromBackend() + { + this->showLoadIndicatorWithTimeout(m_loadIndicatorTimeoutMsDefault); + emit this->requestNewBackendData(); + } + + void CViewBaseNonTemplate::onModelChanged() + { + this->updateSortIndicator(); + } + + void CViewBaseNonTemplate::rowsResizeModeToInteractive() + { + const int height = this->verticalHeader()->minimumSectionSize(); + QHeaderView *verticalHeader = this->verticalHeader(); + Q_ASSERT_X(verticalHeader, Q_FUNC_INFO, "Missing vertical header"); + verticalHeader->setSectionResizeMode(QHeaderView::Interactive); + verticalHeader->setDefaultSectionSize(height); + m_rowResizeMode = Interactive; + } + + void CViewBaseNonTemplate::rowsResizeModeToContent() + { + QHeaderView *verticalHeader = this->verticalHeader(); + Q_ASSERT(verticalHeader); + verticalHeader->setSectionResizeMode(QHeaderView::ResizeToContents); + m_rowResizeMode = Content; + } + + void CViewBaseNonTemplate::rowsResizeModeBasedOnThreshold(int elements) + { + if (elements > ResizeRowsToContentThreshold) + { + this->rowsResizeModeToInteractive(); + } + else + { + this->rowsResizeModeToContent(); + } + } + + int CViewBaseNonTemplate::showLoadIndicator(int containerSizeDependent, int timeoutMs, bool processEvents) + { + if (!m_enabledLoadIndicator) { return -1; } + if (m_showingLoadIndicator) { return -1; } + if (this->hasDockWidgetArea()) + { + if (!this->isVisibleWidget()) { return -1; } + } + + if (containerSizeDependent >= 0) + { + // really with indicator? + if (containerSizeDependent < ResizeSubsetThreshold) { return -1; } + } + m_showingLoadIndicator = true; + emit this->loadIndicatorVisibilityChanged(m_showingLoadIndicator); + + if (!m_loadIndicator) + { + m_loadIndicator = new CLoadIndicator(64, 64, this); + } + this->centerLoadIndicator(); + return m_loadIndicator->startAnimation(timeoutMs > 0 ? timeoutMs : m_loadIndicatorTimeoutMsDefault, processEvents); + } + + int CViewBaseNonTemplate::showLoadIndicatorWithTimeout(int timeoutMs, bool processEvents) + { + return this->showLoadIndicator(-1, timeoutMs, processEvents); + } + + void CViewBaseNonTemplate::centerLoadIndicator() + { + if (!m_loadIndicator) { return; } + const QPoint middle = this->viewport()->geometry().center(); + m_loadIndicator->centerLoadIndicator(middle); + } + + void CViewBaseNonTemplate::hideLoadIndicator(int loadingId) + { + if (!m_showingLoadIndicator) { return; } + m_showingLoadIndicator = false; + emit this->loadIndicatorVisibilityChanged(m_showingLoadIndicator); + if (!m_loadIndicator) { return; } + m_loadIndicator->stopAnimation(loadingId); + } + + bool CViewBaseNonTemplate::isResizeConditionMet(int containerSize) const + { + if (m_resizeMode == ResizingAlways) { return true; } + if (m_resizeMode == PresizeSubset) { return false; } + if (m_resizeMode == ResizingOff) { return false; } + if (m_resizeMode == ResizingOnce) { return m_resizeCount < 1; } + if (m_resizeMode == ResizingAuto) + { + if (reachedResizeThreshold(containerSize)) { return false; } + if (m_resizeAutoNthTime < 2) { return true; } + return (m_resizeCount % m_resizeAutoNthTime) == 0; + } + return false; + } + + void CViewBaseNonTemplate::fullResizeToContents() + { + // resize to maximum magic trick from: + // http://stackoverflow.com/q/3433664/356726 + this->setVisible(false); + const QRect vpOriginal = this->viewport()->geometry(); + if (m_forceColumnsToMaxSize) + { + // vpNew.setWidth(std::numeric_limits::max()); // largest finite value + const QRect screenGeometry = QApplication::desktop()->screenGeometry(); + QRect vpNew = vpOriginal; + vpNew.setWidth(screenGeometry.width()); + this->viewport()->setGeometry(vpNew); + } + + this->resizeColumnsToContents(); // columns + + // useless if mode is Interactive + if (m_rowResizeMode == Content) + { + this->resizeRowsToContents(); // rows + } + m_resizeCount++; + + // re-stretch + if (m_forceStretchLastColumnWhenResized) { this->horizontalHeader()->setStretchLastSection(true); } + if (m_forceColumnsToMaxSize) { this->viewport()->setGeometry(vpOriginal); } + + // if I store the original visibility and then + // set it back here, the whole view disappears + this->setVisible(true); + } + + void CViewBaseNonTemplate::customMenuRequested(QPoint pos) + { + QMenu menu; + CMenuActions menuActions; + this->customMenu(menuActions); + if (menuActions.isEmpty()) { return; } + menuActions.toQMenu(menu, true); + + // Nested dock widget menu + const CDockWidgetInfoArea *dockWidget = this->getDockWidgetInfoArea(); + if (dockWidget) + { + if (!menu.isEmpty()) { menu.addSeparator(); } + const QString mm = QString("Dock widget '%1'").arg(dockWidget->windowTitleOrBackup()); + QMenu *dockWidgetSubMenu = menu.addMenu(CIcons::text16(), mm); + dockWidget->addToContextMenu(dockWidgetSubMenu); + } + + const QPoint globalPos = this->mapToGlobal(pos); + menu.exec(globalPos); + } + + void CViewBaseNonTemplate::toggleResizeMode(bool checked) + { + m_resizeMode = checked ? ResizingAuto : ResizingOff; + } + + void CViewBaseNonTemplate::toggleAutoDisplay() + { + const QAction *a = qobject_cast(QObject::sender()); + if (!a) { return; } + Q_ASSERT_X(a->isCheckable(), Q_FUNC_INFO, "object not checkable"); + m_displayAutomatically = a->isChecked(); + } + + void CViewBaseNonTemplate::setSingleSelection() + { + this->setSelectionMode(SingleSelection); + } + + void CViewBaseNonTemplate::setExtendedSelection() + { + if (this->allowsMultipleSelectedRows()) + { + this->setSelectionMode(ExtendedSelection); + } + } + + void CViewBaseNonTemplate::setMultiSelection() + { + if (this->allowsMultipleSelectedRows()) + { + this->setSelectionMode(MultiSelection); + } + } + + void CViewBaseNonTemplate::removeSelectedRowsChecked() + { + if (!m_enableDeleteSelectedRows) { return; } + this->removeSelectedRows(); + } + + void CViewBaseNonTemplate::updatedIndicator() + { + this->update(); + } + + void CViewBaseNonTemplate::dragEnterEvent(QDragEnterEvent *event) + { + if (!event || !this->acceptDrop(event->mimeData())) { return; } + this->setBackgroundRole(QPalette::Highlight); + event->acceptProposedAction(); + } + + void CViewBaseNonTemplate::dragMoveEvent(QDragMoveEvent *event) + { + if (!event || !this->acceptDrop(event->mimeData())) { return; } + event->acceptProposedAction(); + } + + void CViewBaseNonTemplate::dragLeaveEvent(QDragLeaveEvent *event) + { + if (!event) { return; } + event->accept(); + } + + void CViewBaseNonTemplate::dropEvent(QDropEvent *event) + { + if (!event) { return; } + QTableView::dropEvent(event); + } + + int CViewBaseNonTemplate::getPresizeRandomElementsSize(int containerSize) const + { + containerSize = containerSize >= 0 ? containerSize : this->rowCount(); + const int presizeRandomElements = containerSize > 1000 ? containerSize / 100 : containerSize / 40; + return presizeRandomElements; + } + } // namespace +} // namespace diff --git a/src/blackgui/views/viewbasesimulation.cpp b/src/blackgui/views/viewbasesimulation.cpp new file mode 100644 index 000000000..85a3f5de4 --- /dev/null +++ b/src/blackgui/views/viewbasesimulation.cpp @@ -0,0 +1,21 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "viewbase.cpp" + +namespace BlackGui +{ + namespace Views + { + template class CViewBase; + template class CViewBase; + template class CViewBase; + template class CViewBase; + } // namespace +} // namespace diff --git a/src/blackgui/views/viewbaseweather.cpp b/src/blackgui/views/viewbaseweather.cpp new file mode 100644 index 000000000..46d075903 --- /dev/null +++ b/src/blackgui/views/viewbaseweather.cpp @@ -0,0 +1,20 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "viewbase.cpp" + +namespace BlackGui +{ + namespace Views + { + template class CViewBase; + template class CViewBase; + template class CViewBase; + } // namespace +} // namespace