/* Copyright (C) 2013 * 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.h" #include "../models/statusmessagelistmodel.h" #include "../models/namevariantpairlistmodel.h" #include "../models/atcstationlistmodel.h" #include "../models/aircraftlistmodel.h" #include "../models/aircraftmodellistmodel.h" #include "../models/airportlistmodel.h" #include "../models/airportlistmodel.h" #include "../models/serverlistmodel.h" #include "../models/userlistmodel.h" #include "../models/clientlistmodel.h" #include "../models/simulatedaircraftlistmodel.h" #include "../models/keyboardkeylistmodel.h" #include "../guiutility.h" #include #include #include #include #include #include using namespace BlackMisc; using namespace BlackGui; using namespace BlackGui::Models; namespace BlackGui { namespace Views { void CViewBaseNonTemplate::resizeToContents() { this->performResizeToContents(); } CViewBaseNonTemplate::CViewBaseNonTemplate(QWidget *parent) : QTableView(parent) { this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &QWidget::customContextMenuRequested, this, &CViewBaseNonTemplate::ps_customMenuRequested); // scroll modes this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } void CViewBaseNonTemplate::setFilterDialog(QDialog *filterDialog) { if (filterDialog) { this->m_withMenuFilter = true; this->m_filterDialog.reset(filterDialog); connect(filterDialog, &QDialog::finished, this, &CViewBaseNonTemplate::ps_filterDialogFinished); } else { if (!this->m_filterDialog.isNull()) { disconnect(this->m_filterDialog.data()); } this->m_withMenuFilter = false; this->m_filterDialog.reset(nullptr); } } QWidget *CViewBaseNonTemplate::mainApplicationWindowWidget() const { return CGuiUtility::mainApplicationWindowWidget(); } void CViewBaseNonTemplate::customMenu(QMenu &menu) const { if (this->m_withMenuItemRefresh) { menu.addAction(BlackMisc::CIcons::refresh16(), "Update", this, SIGNAL(requestUpdate())); } if (this->m_withMenuItemClear) { menu.addAction(BlackMisc::CIcons::delete16(), "Clear", this, SLOT(ps_clear())); } if (this->m_withMenuFilter) { menu.addAction(BlackMisc::CIcons::tableSheet16(), "Filter", this, SLOT(ps_displayFilterDialog())); menu.addAction(BlackMisc::CIcons::tableSheet16(), "Remove Filter", this, SLOT(ps_removeFilter())); } if (!menu.isEmpty()) { menu.addSeparator(); } menu.addAction(BlackMisc::CIcons::resize16(), "Full resize", this, SLOT(fullResizeToContents())); // resize to content might decrease performance, // so I only allow changing to "content resizing" if size matches bool enabled = !this->reachedResizeThreshold(); QAction *actionInteractiveResize = new QAction(&menu); actionInteractiveResize->setObjectName(this->objectName().append("ActionResizing")); actionInteractiveResize->setIconText("Resize (auto)"); actionInteractiveResize->setIcon(CIcons::viewMultiColumn()); actionInteractiveResize->setCheckable(true); actionInteractiveResize->setChecked(this->m_resizeMode == ResizingAuto); actionInteractiveResize->setEnabled(enabled); menu.addAction(actionInteractiveResize); connect(actionInteractiveResize, &QAction::toggled, this, &CViewBaseNonTemplate::ps_toggleResizeMode); } int CViewBaseNonTemplate::getHorizontalHeaderFontHeight() const { QFontMetrics m(this->getHorizontalHeaderFont()); int h = m.height(); return h; } bool CViewBaseNonTemplate::hasSelection() const { return this->selectionModel()->hasSelection(); } QModelIndexList CViewBaseNonTemplate::selectedRows() const { return this->selectionModel()->selectedRows(); } void CViewBaseNonTemplate::init() { int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); this->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // faster mode this->horizontalHeader()->setStretchLastSection(true); this->verticalHeader()->setDefaultSectionSize(fh); // for height this->verticalHeader()->setMinimumSectionSize(fh); // for height this->initRowsResizeModeToInteractive(); } int CViewBaseNonTemplate::ps_updateContainer(const CVariant &variant, bool sort, bool resize) { return this->performUpdateContainer(variant, sort, resize); } void CViewBaseNonTemplate::ps_displayFilterDialog() { if (!this->m_withMenuFilter) { return; } if (!this->m_filterDialog) { return; } this->m_filterDialog->show(); } void CViewBaseNonTemplate::initRowsResizeModeToInteractive() { const int height = this->verticalHeader()->minimumSectionSize(); this->setRowsResizeModeToInteractive(height); } void CViewBaseNonTemplate::setRowsResizeModeToInteractive(int height) { QHeaderView *verticalHeader = this->verticalHeader(); Q_ASSERT(verticalHeader); verticalHeader->setSectionResizeMode(QHeaderView::Interactive); verticalHeader->setDefaultSectionSize(height); } bool CViewBaseNonTemplate::performResizing() const { if (m_resizeMode == ResizingOff) { return false; } if (m_resizeMode == ResizingOnce) { return m_resizeCount < 1; } if (m_resizeMode == ResizingAuto) { if (reachedResizeThreshold()) { return false; } if (m_resizeAutoNthTime < 2) { return true; } return (m_resizeCount % m_resizeAutoNthTime) == 0; } return false; } void CViewBaseNonTemplate::fullResizeToContents() { m_resizeCount++; this->resizeColumnsToContents(); this->resizeRowsToContents(); if (m_forceStretchLastColumnWhenResized) { // re-stretch this->horizontalHeader()->setStretchLastSection(true); } } void CViewBaseNonTemplate::ps_customMenuRequested(QPoint pos) { QMenu menu; this->customMenu(menu); if (menu.isEmpty()) { return; } QPoint globalPos = this->mapToGlobal(pos); menu.exec(globalPos); } void CViewBaseNonTemplate::ps_toggleResizeMode(bool checked) { if (checked) { this->m_resizeMode = ResizingAuto; } else { this->m_resizeMode = ResizingOff; } } template int CViewBase::updateContainer(const ContainerType &container, bool sort, bool resize) { Q_ASSERT(this->m_model); int c = this->m_model->update(container, sort); if (resize) { this->resizeToContents(); } return c; } template BlackMisc::CWorker *CViewBase::updateContainerAsync(const ContainerType &container, bool sort, bool resize) { ModelClass *model = this->derivedModel(); auto sortColumn = model->getSortColumn(); auto sortOrder = model->getSortOrder(); BlackMisc::CWorker *worker = BlackMisc::CWorker::fromTask(this, "ViewSort", [this, model, container, sort, resize, sortColumn, sortOrder]() { ContainerType sortedContainer = model->sortContainerByColumn(container, sortColumn, sortOrder); QMetaObject::invokeMethod(this, "ps_updateContainer", Q_ARG(BlackMisc::CVariant, sortedContainer.toCVariant()), Q_ARG(bool, false), Q_ARG(bool, resize)); }); worker->then(this, &CViewBase::asyncUpdateFinished); return worker; } template void CViewBase::updateContainerMaybeAsync(const ContainerType &container, bool sort, bool resize) { if (container.size() > asyncRowsCountThreshold && sort) { // larger container with sorting updateContainerAsync(container, sort, resize); } else { updateContainer(container, sort, resize); } } template void CViewBase::insert(const ObjectType &value, bool resize) { Q_ASSERT(this->m_model); this->m_model->insert(value); if (resize) { this->performResizeToContents(); } } template const ObjectType &CViewBase::at(const QModelIndex &index) const { Q_ASSERT(this->m_model); return this->m_model->at(index); } template const ContainerType &CViewBase::getContainer() const { Q_ASSERT(this->m_model); return this->m_model->getContainer(); } template ContainerType CViewBase::selectedObjects() const { if (!this->hasSelection()) { return ContainerType(); } ContainerType c; QModelIndexList indexes = this->selectedRows(); for (QModelIndex &i : indexes) { c.push_back(this->at(i)); } return c; } template int CViewBase::rowCount() const { Q_ASSERT(this->m_model); return this->m_model->rowCount(); } template int CViewBase::columnCount() const { Q_ASSERT(this->m_model); return this->m_model->columnCount(QModelIndex()); } template bool CViewBase::isEmpty() const { Q_ASSERT(this->m_model); return this->m_model->rowCount() < 1; } template void CViewBase::setObjectName(const QString &name) { // then name here is mainly set for debugging purposes so each model can be identified Q_ASSERT(m_model); QTableView::setObjectName(name); QString modelName = QString(name).append(':').append(this->m_model->getTranslationContext()); this->m_model->setObjectName(modelName); } template void CViewBase::setSortIndicator() { if (this->m_model->hasValidSortColumn()) { Q_ASSERT(this->horizontalHeader()); this->horizontalHeader()->setSortIndicator( this->m_model->getSortColumn(), this->m_model->getSortOrder()); } } template void CViewBase::standardInit(ModelClass *model) { Q_ASSERT(model || this->m_model); if (model) { this->m_model = model; connect(this->m_model, &ModelClass::rowCountChanged, this, &CViewBase::rowCountChanged); connect(this->m_model, &ModelClass::objectChanged, this, &CViewBase::objectChanged); } this->setModel(this->m_model); // via QTableView CViewBaseNonTemplate::init(); this->setSortIndicator(); } template void CViewBase::performResizeToContents() { // small set or large set? if (this->performResizing()) { this->fullResizeToContents(); } else { this->m_resizeCount++; // skipped resize } } template int CViewBase::performUpdateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize) { ContainerType c; c.convertFromCVariant(variant); return this->updateContainer(c, sort, resize); } template bool CViewBase::ps_filterDialogFinished(int status) { QDialog::DialogCode statusCode = static_cast(status); if (statusCode == QDialog::Rejected) { this->derivedModel()->removeFilter(); return true; // handled } return false; } template void CViewBase::ps_removeFilter() { this->derivedModel()->removeFilter(); } // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html 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