From 9567d1aedfc8664ccc7e958caf49a219a718bd68 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sun, 31 Aug 2014 15:31:19 +0200 Subject: [PATCH] refs #319, added column formatters * Specialized formatters (e.g. for bool, for PQs etc.) allow specialized look and feel for each column * Fixed isValidComFrequency used for formatters * Changed models to use formatters * Outdated propertyByIndexAsString methods removed * During the above tasks: fixed override keyword in some places * Column tooltips * refs #323, wrong column name fix --- src/blackgui/models/aircraftlistmodel.cpp | 22 +- src/blackgui/models/airportlistmodel.cpp | 15 +- src/blackgui/models/atcstationlistmodel.cpp | 36 +- src/blackgui/models/atcstationlistmodel.h | 3 + src/blackgui/models/clientlistmodel.cpp | 19 +- src/blackgui/models/columnformatters.cpp | 302 ++++++++++++++++ src/blackgui/models/columnformatters.h | 327 ++++++++++++++++++ src/blackgui/models/columns.cpp | 102 ++---- src/blackgui/models/columns.h | 87 +++-- src/blackgui/models/keyboardkeylistmodel.cpp | 9 +- src/blackgui/models/listmodelbase.cpp | 65 ++-- src/blackgui/models/listmodelbase.h | 93 ++--- .../models/namevariantpairlistmodel.cpp | 6 +- src/blackgui/models/serverlistmodel.cpp | 16 +- src/blackgui/models/serverlistmodel.h | 3 +- .../models/statusmessagelistmodel.cpp | 8 +- src/blackgui/models/statusmessagelistmodel.h | 2 +- src/blackgui/models/userlistmodel.cpp | 24 +- src/blackmisc/avatcstation.cpp | 46 +-- src/blackmisc/avatcstation.h | 3 - src/blackmisc/aviocomsystem.h | 8 + 21 files changed, 893 insertions(+), 303 deletions(-) create mode 100644 src/blackgui/models/columnformatters.cpp create mode 100644 src/blackgui/models/columnformatters.h diff --git a/src/blackgui/models/aircraftlistmodel.cpp b/src/blackgui/models/aircraftlistmodel.cpp index 29bf1699b..651d80878 100644 --- a/src/blackgui/models/aircraftlistmodel.cpp +++ b/src/blackgui/models/aircraftlistmodel.cpp @@ -24,16 +24,16 @@ namespace BlackGui */ CAircraftListModel::CAircraftListModel(QObject *parent) : CListModelBase("ViewAircraftList", parent) { - this->m_columns.addColumn(CColumn("callsign", CAircraft::IndexCallsign)); - this->m_columns.addColumn(CColumn("pilotrealname", { CAircraft::IndexPilot, CUser::IndexRealName })); - this->m_columns.addColumn(CColumn("distance", CAircraft::IndexDistance, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("frequency", { CAircraft::IndexCom1System, CComSystem::IndexActiveFrequency }, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("model", { CAircraft::IndexIcao, CAircraftIcao::IndexAsString})); - this->m_columns.addColumn(CColumn("transponder", { CAircraft::IndexTransponder, CTransponder::IndexTransponderCodeAndModeFormatted })); - this->m_columns.addColumn(CColumn("latitude", { CAircraft::IndexSituation, CAircraftSituation::IndexLatitude }, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("longitude", { CAircraft::IndexSituation, CAircraftSituation::IndexLongitude }, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("altitude", { CAircraft::IndexSituation, CAircraftSituation::IndexAltitude }, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("groundspeed", { CAircraft::IndexSituation, CAircraftSituation::IndexGroundspeed }, Qt::AlignRight | Qt::AlignVCenter)); + this->m_columns.addColumn(CColumn::standardValueObject("callsign", CAircraft::IndexCallsign)); + this->m_columns.addColumn(CColumn::standardString("pilotrealname", { CAircraft::IndexPilot, CUser::IndexRealName })); + this->m_columns.addColumn(CColumn("distance", CAircraft::IndexDistance, new CAirspaceDistanceFormatter())); + this->m_columns.addColumn(CColumn("frequency", { CAircraft::IndexCom1System, CComSystem::IndexActiveFrequency }, new CComFrequencyFormatter())); + this->m_columns.addColumn(CColumn::standardString("icao", { CAircraft::IndexIcao, CAircraftIcao::IndexAsString})); + this->m_columns.addColumn(CColumn::standardString("transponder", { CAircraft::IndexTransponder, CTransponder::IndexTransponderCodeAndModeFormatted })); + this->m_columns.addColumn(CColumn("latitude", { CAircraft::IndexSituation, CAircraftSituation::IndexLatitude }, new CLatLonFormatter())); + this->m_columns.addColumn(CColumn("longitude", { CAircraft::IndexSituation, CAircraftSituation::IndexLongitude }, new CLatLonFormatter())); + this->m_columns.addColumn(CColumn::standardValueObject("altitude", { CAircraft::IndexSituation, CAircraftSituation::IndexAltitude }, CDefaultFormatter::alignRightVCenter())); + this->m_columns.addColumn(CColumn("gs", { CAircraft::IndexSituation, CAircraftSituation::IndexGroundspeed }, new CAircraftSpeedFormatter())); // default sort order this->setSortColumnByPropertyIndex(CAircraft::IndexDistance); @@ -49,7 +49,7 @@ namespace BlackGui (void)QT_TRANSLATE_NOOP("ViewAircraftList", "height"); (void)QT_TRANSLATE_NOOP("ViewAircraftList", "transponder"); (void)QT_TRANSLATE_NOOP("ViewAircraftList", "groundspeed"); - (void)QT_TRANSLATE_NOOP("ViewAircraftList", "model"); + (void)QT_TRANSLATE_NOOP("ViewAircraftList", "icao"); } } } diff --git a/src/blackgui/models/airportlistmodel.cpp b/src/blackgui/models/airportlistmodel.cpp index bf7664cfa..46bb8f036 100644 --- a/src/blackgui/models/airportlistmodel.cpp +++ b/src/blackgui/models/airportlistmodel.cpp @@ -14,6 +14,7 @@ using namespace BlackMisc::Aviation; using namespace BlackMisc::Geo; +using namespace BlackMisc::PhysicalQuantities; namespace BlackGui { @@ -25,13 +26,13 @@ namespace BlackGui CAirportListModel::CAirportListModel(QObject *parent) : CListModelBase("ViewAirportList", parent) { - this->m_columns.addColumn(CColumn("ICAO", CAirport::IndexIcao)); - this->m_columns.addColumn(CColumn("distance", CAirport::IndexDistance, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("bearing", CAirport::IndexBearing, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("name", CAirport::IndexDescriptiveName)); - this->m_columns.addColumn(CColumn("elevation", CAirport::IndexElevation, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("latitude", CCoordinateGeodetic::IndexLatitude, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("longitude", CCoordinateGeodetic::IndexLongitude, Qt::AlignRight | Qt::AlignVCenter)); + this->m_columns.addColumn(CColumn::standardValueObject("ICAO", CAirport::IndexIcao)); + this->m_columns.addColumn(CColumn("distance", CAirport::IndexDistance, new CAirspaceDistanceFormatter())); + this->m_columns.addColumn(CColumn("bearing", CAirport::IndexBearing, new CAngleDegreeFormatter())); + this->m_columns.addColumn(CColumn::standardString("name", CAirport::IndexDescriptiveName)); + this->m_columns.addColumn(CColumn("elevation", CAirport::IndexElevation, new CPhysiqalQuantiyFormatter(CLengthUnit::ft(), 0))); + this->m_columns.addColumn(CColumn("latitude", CAirport::IndexLatitude, new CLatLonFormatter())); + this->m_columns.addColumn(CColumn("longitude", CAirport::IndexLatitude, new CLatLonFormatter())); // default sort order this->setSortColumnByPropertyIndex(CAirport::IndexDistance); diff --git a/src/blackgui/models/atcstationlistmodel.cpp b/src/blackgui/models/atcstationlistmodel.cpp index cc34c4839..306d2e9a9 100644 --- a/src/blackgui/models/atcstationlistmodel.cpp +++ b/src/blackgui/models/atcstationlistmodel.cpp @@ -12,6 +12,7 @@ #include "blackmisc/nwuser.h" #include "blackmisc/blackmiscfreefunctions.h" +using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::Audio; using namespace BlackMisc::Aviation; using namespace BlackMisc::Network; @@ -27,6 +28,7 @@ namespace BlackGui CListModelBase("ViewAtcList", parent), m_stationMode(NotSet) { this->setStationMode(stationMode); + // force strings for translation in resource files (void)QT_TRANSLATE_NOOP("ViewAtcList", "callsign"); (void)QT_TRANSLATE_NOOP("ViewAtcList", "distance"); @@ -47,14 +49,16 @@ namespace BlackGui { case NotSet: case StationsOnline: - this->m_columns.addColumn(CColumn("callsign", { CAtcStation::IndexCallsign, CCallsign::IndexCallsignStringAsSet })); - this->m_columns.addColumn(CColumn(CAtcStation::IndexPixmap, true)); - this->m_columns.addColumn(CColumn("distance", CAtcStation::IndexDistance, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("frequency", CAtcStation::IndexFrequency, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("controllername", { CAtcStation::IndexController, CUser::IndexRealName })); - this->m_columns.addColumn(CColumn("bookedfrom", CAtcStation::IndexBookedFrom)); - this->m_columns.addColumn(CColumn("bookeduntil", CAtcStation::IndexBookedUntil)); - this->m_columns.addColumn(CColumn("voiceroomurl", { CAtcStation::IndexVoiceRoom, CVoiceRoom::IndexUrl })); + this->m_columns.addColumn(CColumn::standardString("callsign", { CAtcStation::IndexCallsign, CCallsign::IndexCallsignStringAsSet })); + this->m_columns.addColumn(CColumn("type", CAtcStation::IndexIcon)); + this->m_columns.addColumn(CColumn("distance", CAtcStation::IndexDistance, new CAirspaceDistanceFormatter())); + this->m_columns.addColumn(CColumn("frequency", CAtcStation::IndexFrequency, new CComFrequencyFormatter())); + this->m_columns.addColumn(CColumn::standardString("controllername", { CAtcStation::IndexController, CUser::IndexRealName })); + this->m_columns.addColumn(CColumn("bookedfrom", CAtcStation::IndexBookedFrom, + new CDateTimeFormatter(CDateTimeFormatter::formatHm()))); + this->m_columns.addColumn(CColumn("bookeduntil", CAtcStation::IndexBookedUntil, + new CDateTimeFormatter(CDateTimeFormatter::formatHm()))); + this->m_columns.addColumn(CColumn::standardString("voiceroomurl", { CAtcStation::IndexVoiceRoom, CVoiceRoom::IndexUrl })); // default sort order this->setSortColumnByPropertyIndex(CAtcStation::IndexDistance); @@ -62,13 +66,15 @@ namespace BlackGui break; case StationsBooked: - this->m_columns.addColumn(CColumn("callsign", { CAtcStation::IndexCallsign, CCallsign::IndexCallsignStringAsSet})); - this->m_columns.addColumn(CColumn(CAtcStation::IndexPixmap, true)); - this->m_columns.addColumn(CColumn("controllername", {CAtcStation::IndexController, CUser::IndexRealName })); - this->m_columns.addColumn(CColumn("bookedfrom", CAtcStation::IndexBookedFrom)); - this->m_columns.addColumn(CColumn("bookeduntil", CAtcStation::IndexBookedUntil)); - this->m_columns.addColumn(CColumn("frequency", CAtcStation::IndexFrequency, Qt::AlignRight | Qt::AlignVCenter)); - this->m_columns.addColumn(CColumn("online", CAtcStation::IndexIsOnline)); + this->m_columns.addColumn(CColumn::standardString("callsign", { CAtcStation::IndexCallsign, CCallsign::IndexCallsignStringAsSet })); + this->m_columns.addColumn(CColumn(CAtcStation::IndexIcon)); + this->m_columns.addColumn(CColumn("", CAtcStation::IndexIsOnline, new CBoolLedFormatter("online", "offline"))); + this->m_columns.addColumn(CColumn::standardString("controllername", { CAtcStation::IndexController, CUser::IndexRealName })); + this->m_columns.addColumn(CColumn("bookedfrom", CAtcStation::IndexBookedFrom, + new CDateTimeFormatter(CDateTimeFormatter::formatYmdhm()))); + this->m_columns.addColumn(CColumn("bookeduntil", CAtcStation::IndexBookedUntil, + new CDateTimeFormatter(CDateTimeFormatter::formatYmdhm()))); + this->m_columns.addColumn(CColumn("frequency", CAtcStation::IndexFrequency, new CComFrequencyFormatter())); // default sort order this->setSortColumnByPropertyIndex(CAtcStation::IndexBookedFrom); diff --git a/src/blackgui/models/atcstationlistmodel.h b/src/blackgui/models/atcstationlistmodel.h index ef777c731..c9a26909d 100644 --- a/src/blackgui/models/atcstationlistmodel.h +++ b/src/blackgui/models/atcstationlistmodel.h @@ -43,6 +43,9 @@ namespace BlackGui //! Set station mode void setStationMode(AtcStationMode stationMode); + //! Station mode + AtcStationMode getStationMode() const { return this->m_stationMode; } + public slots: //! Used to quickly update single station (better response for the user) void changedAtcStationConnectionStatus(const BlackMisc::Aviation::CAtcStation &station, bool added); diff --git a/src/blackgui/models/clientlistmodel.cpp b/src/blackgui/models/clientlistmodel.cpp index 3dafed649..a2f58febc 100644 --- a/src/blackgui/models/clientlistmodel.cpp +++ b/src/blackgui/models/clientlistmodel.cpp @@ -9,7 +9,7 @@ #include "clientlistmodel.h" #include "blackmisc/blackmiscfreefunctions.h" -#include "blackmisc/icon.h" +#include "blackmisc/icons.h" #include #include @@ -26,14 +26,15 @@ namespace BlackGui CClientListModel::CClientListModel(QObject *parent) : CListModelBase("ViewClientList", parent) { - this->m_columns.addColumn(CColumn(CClient::IndexPixmap, true)); - this->m_columns.addColumn(CColumn("callsign", CClient::IndexCallsign)); - this->m_columns.addColumn(CColumn("realname", { CClient::IndexUser, CUser::IndexRealName })); - this->m_columns.addColumn(CColumn(CClient::IndexVoiceCapabilitiesPixmap, true)); - this->m_columns.addColumn(CColumn("capabilities", CClient::IndexCapabilitiesString)); - this->m_columns.addColumn(CColumn("model", {CClient::IndexModel, CAircraftModel::IndexModelString})); - this->m_columns.addColumn(CColumn("queried?", {CClient::IndexModel, CAircraftModel::IndexIsQueriedModelString})); - this->m_columns.addColumn(CColumn("server", CClient::IndexServer)); + this->m_columns.addColumn(CColumn("client", CClient::IndexIcon)); + this->m_columns.addColumn(CColumn::standardValueObject("callsign", CClient::IndexCallsign)); + this->m_columns.addColumn(CColumn::standardString("realname", { CClient::IndexUser, CUser::IndexRealName })); + this->m_columns.addColumn(CColumn("capabilities", CClient::IndexVoiceCapabilitiesIcon)); + this->m_columns.addColumn(CColumn::standardString("capabilities", CClient::IndexCapabilitiesString)); + this->m_columns.addColumn(CColumn::standardString("model", {CClient::IndexModel, CAircraftModel::IndexModelString})); + this->m_columns.addColumn(CColumn("q.?", "queried", {CClient::IndexModel, CAircraftModel::IndexIsQueriedModelString}, + new CBoolIconFormatter(CIcons::StandardIconTick, CIcons::StandardIconCross, "queried", "not queried"))); + this->m_columns.addColumn(CColumn::standardString("server", CClient::IndexServer)); // force strings for translation in resource files // force strings for translation in resource files diff --git a/src/blackgui/models/columnformatters.cpp b/src/blackgui/models/columnformatters.cpp new file mode 100644 index 000000000..b34820da7 --- /dev/null +++ b/src/blackgui/models/columnformatters.cpp @@ -0,0 +1,302 @@ +/* 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 "columnformatters.h" +#include "blackmisc/geolatitude.h" +#include "blackmisc/variant.h" +#include "blackmisc/iconlist.h" +#include "blackmisc/icons.h" + +using namespace BlackMisc; + +namespace BlackGui +{ + namespace Models + { + + QVariant CDefaultFormatter::displayRole(const QVariant &dataQVariant) const + { + // seems to be absurd, but calls the correct methods on CValueObjects + // so QVariant -> QString -> QVariant is correct + if (static_cast(dataQVariant.type()) == QMetaType::QString) return dataQVariant; // shortcut + return BlackMisc::qVariantToString(dataQVariant, m_useI18n); + } + + QVariant CDefaultFormatter::decorationRole(const QVariant &dataQVariant) const + { + // direct return if type is already correct + if (static_cast(dataQVariant.type()) == QMetaType::QPixmap) + { + return dataQVariant; + } + else if (static_cast(dataQVariant.type()) == QMetaType::QIcon) + { + return dataQVariant; + } + + // convert to pixmap + if (static_cast(dataQVariant.type()) == QMetaType::QImage) + { + QImage img = dataQVariant.value(); + return QPixmap::fromImage(img); + } + + // Our CIcon class + if (dataQVariant.canConvert()) + { + BlackMisc::CIcon i = dataQVariant.value(); + return i.toPixmap(); + } + + // nope + return QPixmap(); + } + + QVariant CDefaultFormatter::alignmentRole() const + { + if (!this->hasAlignment()) + { + return QVariant(alignDefault()); // default + } + else + { + return QVariant(m_alignment); + } + } + + QVariant CDefaultFormatter::data(int role, const QVariant &inputData) const + { + Qt::ItemDataRole roleEnum = static_cast(role); + + // always supported + if (roleEnum == Qt::TextAlignmentRole) return { alignmentRole() }; + + // checked + if (this->m_supportedRoles.isEmpty()) return QVariant(); + if (!this->m_supportedRoles.contains(role)) return QVariant(); + switch (roleEnum) + { + case Qt::DisplayRole: + // formatted to string + return displayRole(inputData); + case Qt::ToolTipRole: + // formatted to string + return tooltipRole(inputData); + case Qt::DecorationRole: + // formatted as pixmap, icon, or color + return decorationRole(inputData); + default: + break; + } + return QVariant(); + } + + QVariant CPixmapFormatter::displayRole(const QVariant &dataQVariant) const + { + Q_UNUSED(dataQVariant); + Q_ASSERT_X(false, "CPixmapFormatter", "this role should be disabled with pixmaps"); + return QVariant(); + } + + QVariant CPixmapFormatter::tooltipRole(const QVariant &dataQVariant) const + { + if (dataQVariant.isNull()) return ""; + if (dataQVariant.canConvert()) + { + BlackMisc::CIcon icon = dataQVariant.value(); + return icon.getDescriptiveText(); + } + return ""; + } + + QVariant CValueObjectFormatter::displayRole(const QVariant &valueObject) const + { + if (valueObject.isNull()) return QVariant(); + const BlackMisc::CValueObject *cvo = BlackMisc::CValueObject::fromQVariant(valueObject); + Q_ASSERT(cvo); + if (!cvo) return QVariant(); + return QVariant(cvo->toQString(m_useI18n)); + } + + QVariant CValueObjectFormatter::decorationRole(const QVariant &valueObject) const + { + if (valueObject.isNull()) return ""; + const BlackMisc::CValueObject *cvo = BlackMisc::CValueObject::fromQVariant(valueObject); + Q_ASSERT(cvo); + if (!cvo) return QPixmap(); + return QVariant(cvo->toPixmap()); + } + + QVariant CDateTimeFormatter::displayRole(const QVariant &dateTime) const + { + if (dateTime.isNull()) return ""; + if (dateTime.type() == QMetaType::QDateTime) + { + QDateTime dt = dateTime.value(); + return dt.toString(m_formatString); + } + else if (dateTime.type() == QMetaType::QDate) + { + QDate d = dateTime.value(); + return d.toString(m_formatString); + } + else if (dateTime.type() == QMetaType::QTime) + { + QTime t = dateTime.value(); + return t.toString(m_formatString); + } + else + { + Q_ASSERT_X(false, "formatQVariant", "No QDate, QTime or QDateTime"); + return ""; + } + } + + QVariant CAirspaceDistanceFormatter::displayRole(const QVariant &dataQVariant) const + { + if (dataQVariant.canConvert()) + { + // special treatment for some cases + BlackMisc::PhysicalQuantities::CLength l = dataQVariant.value(); + if (!l.isNull() && (l.isPositiveWithEpsilonConsidered() || l.isZeroEpsilonConsidered())) + { + return CPhysiqalQuantiyFormatter::displayRole(dataQVariant); + } + else + { + return ""; + } + } + else + { + Q_ASSERT_X(false, "CAirspaceDistanceFormatter::formatQVariant", "No CLength class"); + return ""; + } + } + + QVariant CComFrequencyFormatter::displayRole(const QVariant &dataQVariant) const + { + if (dataQVariant.canConvert()) + { + // speical treatment for some cases + BlackMisc::PhysicalQuantities::CFrequency f = dataQVariant.value(); + if (BlackMisc::Aviation::CComSystem::isValidComFrequency(f)) + { + return CPhysiqalQuantiyFormatter::displayRole(dataQVariant); + } + else + { + return ""; + } + } + else + { + Q_ASSERT_X(false, "CAviationComFrequencyFormatter::formatQVariant", "No CFrequency class"); + return ""; + } + } + + QVariant CAircraftSpeedFormatter::displayRole(const QVariant &dataQVariant) const + { + // special treatment for some cases + BlackMisc::PhysicalQuantities::CSpeed s = dataQVariant.value(); + if (!s.isNull() && (s.isPositiveWithEpsilonConsidered() || s.isZeroEpsilonConsidered())) + { + return CPhysiqalQuantiyFormatter::displayRole(dataQVariant); + } + else + { + return ""; + } + } + + QVariant CStringFormatter::displayRole(const QVariant &dataQVariant) const + { + if (dataQVariant.canConvert()) { return dataQVariant; } + Q_ASSERT_X(false, "CStringFormatter", "no string value"); + return QVariant(); + } + + QVariant CBoolTextFormatter::displayRole(const QVariant &dataQVariant) const + { + if (dataQVariant.canConvert()) + { + bool v = dataQVariant.toBool(); + return v ? QVariant(m_trueName) : QVariant(m_falseName); + } + Q_ASSERT_X(false, "CBoolTextFormatter", "no boolean value"); + return QVariant(); + } + + CBoolLedFormatter::CBoolLedFormatter(int alignment) : CBoolLedFormatter("on", "off", alignment) + { } + + CBoolLedFormatter::CBoolLedFormatter(const QString &onName, const QString &offName, int alignment) : + CBoolTextFormatter(alignment, onName, offName, roleDecorationAndToolTip()) + { + CLedWidget *led = ledDefault(); + led->setOn(true); + this->m_pixmapOnLed = led->asPixmap(); + led->setOn(false); + this->m_pixmapOffLed = led->asPixmap(); + delete led; + } + + QVariant CBoolLedFormatter::displayRole(const QVariant &dataQVariant) const + { + Q_UNUSED(dataQVariant); + Q_ASSERT_X(false, "CBoolLedFormatter", "this role should be disabled with led boolean"); + return QVariant(); + } + + QVariant CBoolLedFormatter::decorationRole(const QVariant &dataQVariant) const + { + if (dataQVariant.canConvert()) + { + bool v = dataQVariant.toBool(); + return v ? m_pixmapOnLed : m_pixmapOffLed; + } + Q_ASSERT_X(false, "CBoolLedFormatter", "no boolean value"); + return QVariant(); + } + + CBoolIconFormatter::CBoolIconFormatter(int alignment) : + CBoolIconFormatter(CIcons::StandardIconTick, CIcons::StandardIconEmpty16, "on", "off", alignment) + { } + + CBoolIconFormatter::CBoolIconFormatter(CIcons::IconIndex onIcon, CIcons::IconIndex offIcon, const QString &onName, const QString &offName, int alignment) : + CBoolIconFormatter(CIconList::iconForIndex(onIcon), CIconList::iconForIndex(offIcon), onName, offName, alignment) + { } + + CBoolIconFormatter::CBoolIconFormatter(const CIcon &onIcon, const CIcon &offIcon, const QString &onName, const QString &offName, int alignment) : + CBoolTextFormatter(alignment, onName, offName, roleDecorationAndToolTip()), m_iconOn(onIcon), m_iconOff(offIcon) + { + this->m_iconOn.setDescriptiveText(onName); + this->m_iconOff.setDescriptiveText(offName); + } + + QVariant CBoolIconFormatter::displayRole(const QVariant &dataQVariant) const + { + Q_UNUSED(dataQVariant); + Q_ASSERT_X(false, "CBoolIconFormatter", "this role should be disabled with icon boolean"); + return QVariant(); + } + + QVariant CBoolIconFormatter::decorationRole(const QVariant &dataQVariant) const + { + if (dataQVariant.canConvert()) + { + bool v = dataQVariant.toBool(); + return v ? m_iconOn.toPixmap() : m_iconOff.toPixmap(); + } + Q_ASSERT_X(false, "CBoolIconFormatter", "no boolean value"); + return QVariant(); + } + } +} diff --git a/src/blackgui/models/columnformatters.h b/src/blackgui/models/columnformatters.h new file mode 100644 index 000000000..51c19a599 --- /dev/null +++ b/src/blackgui/models/columnformatters.h @@ -0,0 +1,327 @@ +/* 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. + */ + +//! \file + +#ifndef BLACKGUI_COLUMNFORMATTERS_H +#define BLACKGUI_COLUMNFORMATTERS_H + +#include "blackgui/led.h" +#include "blackmisc/pqallquantities.h" +#include "blackmisc/aviocomsystem.h" +#include "blackmisc/blackmiscfreefunctions.h" +#include "blackmisc/icon.h" +#include +#include +#include + +namespace BlackGui +{ + namespace Models + { + //! Column formatter default implementation, also serving as interface + class CDefaultFormatter + { + public: + //! Constructor + CDefaultFormatter(int alignment = alignDefault(), bool i18n = true, const QList &supportedRoles = { Qt::DisplayRole }) : + m_supportedRoles(supportedRoles), m_alignment(alignment), m_useI18n(i18n) {} + + //! Virtual destructor + virtual ~CDefaultFormatter() {} + + //! Value provided as QVariant, formatter converts to QString. + //! Used with Qt::DisplayRole displaying a text. + virtual QVariant displayRole(const QVariant &dataQVariant) const; + + //! Value provided as QVariant, formatter converts to QString. + //! Used with Qt::ToolTipRole displaying a text. + virtual QVariant tooltipRole(const QVariant &value) const + { + return displayRole(value); + } + + //! Value provided as QVariant, formatted as icon (Qt docu: "The data to be rendered as a decoration in the form of an icon"). + //! Used with Qt::DecorationRole displaying an icon, method returns pixmap, icon, or color (see docu) + virtual QVariant decorationRole(const QVariant &dataQVariant) const; + + //! Qt::Alignment (as QVariant) + virtual QVariant alignmentRole() const; + + //! Alignment available? + virtual bool hasAlignment() const { return m_alignment >= 0; } + + //! Receives QVariant of column data, and returns QVariant wrapping string, pixmap, or other values depending on role + virtual QVariant data(int role, const QVariant &inputData) const; + + //! Default value + static int alignDefault() { return alignLeftVCenter(); } + + //! Align left/vertically centered + static int alignLeftVCenter() { return Qt::AlignVCenter | Qt::AlignLeft; } + + //! Align centered + static int alignCentered() { return Qt::AlignVCenter | Qt::AlignHCenter; } + + //! Align right/vertically centered + static int alignRightVCenter() { return Qt::AlignVCenter | Qt::AlignRight; } + + //! Display on role + static const QList &roleDisplay() { static const QList r({ Qt::DisplayRole}); return r; } + + //! Display on role + static const QList &roleDecorationAndToolTip() { static const QList r({ Qt::DecorationRole, Qt::ToolTipRole}); return r; } + + protected: + QList m_supportedRoles = QList({ Qt::DisplayRole}); //!< supports decoration role + int m_alignment = -1; //!< alignment horizontal/vertically / Qt::Alignment + bool m_useI18n = true; //!< i18n? + }; + + //! Pixmap formatter + class CPixmapFormatter : public CDefaultFormatter + { + public: + //! Constructor + CPixmapFormatter(int alignment = alignDefault(), const QList &supportedRoles = roleDecorationAndToolTip()) : CDefaultFormatter(alignment, false, supportedRoles) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + + //! \copydoc CDefaultFormatter::tooltipRole + virtual QVariant tooltipRole(const QVariant &dataQVariant) const override; + }; + + //! String formatter, if known the variant already contains the appropriate string + class CStringFormatter : public CDefaultFormatter + { + public: + //! Constructor + CStringFormatter(int alignment = alignDefault()) : CDefaultFormatter(alignment, false, roleDisplay()) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + }; + + //! Bool value, format as text + class CBoolTextFormatter : public CDefaultFormatter + { + public: + //! Constructor + CBoolTextFormatter(int alignment = alignDefault(), const QString &trueName = "true", const QString &falseName = "false", const QList &supportedRoles = roleDisplay()) : + CDefaultFormatter(alignment, false, supportedRoles), m_trueName(trueName), m_falseName(falseName) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + + protected: + QString m_trueName = "true"; //!< displayed when true + QString m_falseName = "false"; //!< displayed when false + }; + + //! Format as bool LED value + class CBoolLedFormatter : public CBoolTextFormatter + { + public: + + //! Constructor + CBoolLedFormatter(int alignment = alignDefault()); + + //! Constructor + CBoolLedFormatter(const QString &onName, const QString &offName, int alignment = alignDefault()); + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + + //! Display the LED + virtual QVariant decorationRole(const QVariant &dataQVariant) const override; + + //! \copydoc CDefaultFormatter::tooltipRole + virtual QVariant tooltipRole(const QVariant &dataQVariant) const override + { + return CBoolTextFormatter::displayRole(dataQVariant); + } + + //! Default LED + static BlackGui::CLedWidget *ledDefault() + { + return new BlackGui::CLedWidget(false, BlackGui::CLedWidget::Yellow, BlackGui::CLedWidget::Black, BlackGui::CLedWidget::Rounded); + } + + protected: + QPixmap m_pixmapOnLed; //!< Pixmap used when on + QPixmap m_pixmapOffLed; //!< Pixmap used when off + }; + + //! Format as bool pixmap + class CBoolIconFormatter : public CBoolTextFormatter + { + public: + + //! Constructor + CBoolIconFormatter(int alignment = alignDefault()); + + //! Constructor + CBoolIconFormatter(const BlackMisc::CIcon &onIcon, const BlackMisc::CIcon &offIcon, const QString &onName, const QString &offName, int alignment = alignDefault()); + + //! Constructor + CBoolIconFormatter(BlackMisc::CIcons::IconIndex onIcon, BlackMisc::CIcons::IconIndex offIcon, const QString &onName, const QString &offName, int alignment = alignDefault()); + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + + //! Display the icon + virtual QVariant decorationRole(const QVariant &dataQVariant) const override; + + //! \copydoc CDefaultFormatter::tooltipRole + virtual QVariant tooltipRole(const QVariant &dataQVariant) const override + { + return CBoolTextFormatter::displayRole(dataQVariant); + } + + protected: + BlackMisc::CIcon m_iconOn; //!< Used when on + BlackMisc::CIcon m_iconOff; //!< Used when off + }; + + //! Default formatter when column contains CValueObject + class CValueObjectFormatter : public CDefaultFormatter + { + public: + //! Constructor + CValueObjectFormatter(int alignment = alignDefault(), bool i18n = true, QList supportedRoles = {Qt::DisplayRole}) : CDefaultFormatter(alignment, i18n, supportedRoles) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &valueObject) const override; + + //! \copydoc CDefaultFormatter::asPixmap + virtual QVariant decorationRole(const QVariant &valueObject) const; + }; + + //! Formatter when column contains QDateTime, QDate or QTime + class CDateTimeFormatter : public CDefaultFormatter + { + public: + //! Constructor + CDateTimeFormatter(const QString formatString = formatYmd(), int alignment = alignDefault(), bool i18n = true) : + CDefaultFormatter(alignment, i18n, { Qt::DisplayRole }), m_formatString(formatString) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dateTime) const override; + + //! Year month day + static const QString &formatYmd() { static const QString f = "yyyy-MM-dd"; return f; } + + //! Year month day hour minute + static const QString &formatYmdhm() { static const QString f = "yyyy-MM-dd HH:mm"; return f; } + + //! Hour minute + static const QString &formatHm() { static const QString f = "HH:mm"; return f; } + + //! Hour minute second + static const QString &formatHms() { static const QString f = "HH:mm:ss"; return f; } + + private: + QString m_formatString = "yyyy-MM-dd HH:mm"; //!< how the value is displayed + }; + + //! Formatter for physical quantities + template class CPhysiqalQuantiyFormatter : public CValueObjectFormatter + { + public: + //! Constructor + CPhysiqalQuantiyFormatter(MU unit = MU::defaultUnit(), int digits = 2, int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true, QList supportedRoles = { Qt::DisplayRole }) : CValueObjectFormatter(alignment, i18n, supportedRoles), m_unit(unit), m_digits(digits), m_withUnit(withUnit) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &physicalQuantity) const override + { + if (physicalQuantity.canConvert()) + { + PQ pq = physicalQuantity.value(); + if (!m_unit.isNull()) + { + pq.switchUnit(m_unit); + } + return pq.valueRoundedWithUnit(m_digits, m_useI18n); + } + else + { + Q_ASSERT_X(false, "CPhysiqalQuantiyFormatter::displayRole", "No CPhysicalQuantity class"); + return ""; + } + } + + //! Set unit + virtual void setUnit(const MU &unit) { m_unit = unit; } + + //! Enable unit display + virtual void enableUnit(bool enable) { m_withUnit = enable; } + + //! Digits + virtual void setDigits(int digits) { m_digits = digits; } + + protected: + MU m_unit; //!< unit + int m_digits = 2; //!< digits + bool m_withUnit = true; //!< format with unit? + }; + + //! COM frequencies + class CComFrequencyFormatter : public CPhysiqalQuantiyFormatter + { + public: + //! Constructor + CComFrequencyFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true) : CPhysiqalQuantiyFormatter(BlackMisc::PhysicalQuantities::CFrequencyUnit::MHz(), 3, alignment, withUnit, i18n) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + }; + + //! Angle in degrees + class CAngleDegreeFormatter : public CPhysiqalQuantiyFormatter + { + public: + //! Constructor + CAngleDegreeFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true) : CPhysiqalQuantiyFormatter(BlackMisc::PhysicalQuantities::CAngleUnit::deg(), 0, alignment, withUnit, i18n) {} + }; + + //! Latitude or Longitude formatter + class CLatLonFormatter : public CValueObjectFormatter + { + public: + //! Constructor + CLatLonFormatter(int alignment = alignRightVCenter()) : CValueObjectFormatter(alignment) {} + }; + + //! Airspace distance + class CAirspaceDistanceFormatter : public CPhysiqalQuantiyFormatter + { + public: + //! Constructor + CAirspaceDistanceFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true) : CPhysiqalQuantiyFormatter(BlackMisc::PhysicalQuantities::CLengthUnit::NM(), 1, alignment, withUnit, i18n) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + }; + + //! Airspace distance + class CAircraftSpeedFormatter : public CPhysiqalQuantiyFormatter + { + public: + //! Constructor + CAircraftSpeedFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true) : CPhysiqalQuantiyFormatter(BlackMisc::PhysicalQuantities::CSpeedUnit::kts(), 0, alignment, withUnit, i18n) {} + + //! \copydoc CDefaultFormatter::displayRole + virtual QVariant displayRole(const QVariant &dataQVariant) const override; + }; + + } // namespace +} // namespace + +#endif // guard diff --git a/src/blackgui/models/columns.cpp b/src/blackgui/models/columns.cpp index c36ee9b4e..297b5e232 100644 --- a/src/blackgui/models/columns.cpp +++ b/src/blackgui/models/columns.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2013 - * Swift Project Community / Contributors + * 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, @@ -8,26 +8,24 @@ */ #include "columns.h" -#include #include namespace BlackGui { namespace Models { - CColumn::CColumn(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment, bool editable) : - m_columnName(headerName), m_alignment(alignment), m_propertyIndex(propertyIndex), - m_editable(editable), m_icon(false) + + CColumn::CColumn(const QString &headerName, const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex, CDefaultFormatter *formatter, bool editable) : + m_columnName(headerName), m_columnToolTip(toolTip), m_formatter(formatter ? formatter : new CDefaultFormatter()), m_propertyIndex(propertyIndex), + m_editable(editable) {} - CColumn::CColumn(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, bool editable) : - m_columnName(headerName), m_alignment(-1), m_propertyIndex(propertyIndex), - m_editable(editable), m_icon(false) + CColumn::CColumn(const BlackMisc::CPropertyIndex &propertyIndex) : + m_formatter(new CPixmapFormatter()), m_propertyIndex(propertyIndex), m_editable(false) {} - CColumn::CColumn(const BlackMisc::CPropertyIndex &propertyIndex, bool isIcon) : - m_alignment(-1), m_propertyIndex(propertyIndex), - m_editable(false), m_icon(isIcon) + CColumn::CColumn(const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex) : + m_columnToolTip(toolTip), m_formatter(new CPixmapFormatter()), m_propertyIndex(propertyIndex), m_editable(false) {} const char *CColumn::getTranslationContextChar() const @@ -40,10 +38,9 @@ namespace BlackGui return this->m_columnName.toUtf8().constData(); } - QVariant CColumn::aligmentAsQVariant() const + const char *CColumn::getColumnToolTipChar() const { - if (this->hasAlignment()) return QVariant(this->m_alignment); - return QVariant(Qt::AlignVCenter | Qt::AlignLeft); // default + return this->m_columnToolTip.toUtf8().constData(); } QString CColumn::getColumnName(bool i18n) const @@ -52,18 +49,30 @@ namespace BlackGui return QCoreApplication::translate(this->getTranslationContextChar(), this->getColumnNameChar()); } - /* - * Header - */ + QString CColumn::getColumnToolTip(bool i18n) const + { + if (!i18n || this->m_columnToolTip.isEmpty()) return this->m_columnToolTip; + return QCoreApplication::translate(this->getTranslationContextChar(), this->getColumnToolTipChar()); + } + + CColumn CColumn::standardValueObject(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment) + { + return CColumn(headerName, propertyIndex, new CValueObjectFormatter(alignment)); + } + + CColumn CColumn::standardString(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment) + { + return CColumn(headerName, propertyIndex, new CStringFormatter(alignment)); + } + + // --------------- columns ---------------------------------------------- + CColumns::CColumns(const QString &translationContext, QObject *parent) : QObject(parent), m_translationContext(translationContext) { // void } - /* - * Add column - */ void CColumns::addColumn(CColumn column) { Q_ASSERT(!this->m_translationContext.isEmpty()); @@ -71,36 +80,24 @@ namespace BlackGui this->m_columns.push_back(column); } - /* - * Property index to name - */ QString CColumns::propertyIndexToColumnName(const BlackMisc::CPropertyIndex &propertyIndex, bool i18n) const { int column = this->propertyIndexToColumn(propertyIndex); return this->m_columns.at(column).getColumnName(i18n); } - /* - * Index to name - */ QString CColumns::columnToName(int column, bool i18n) const { - Q_ASSERT(column >= 0 && column < this->m_columns.size()); + Q_ASSERT(isValidColumn(column)); return this->m_columns.at(column).getColumnName(i18n); } - /* - * Get property index - */ BlackMisc::CPropertyIndex CColumns::columnToPropertyIndex(int column) const { - Q_ASSERT(column >= 0 && column < this->m_columns.size()); + Q_ASSERT(isValidColumn(column)); return this->m_columns.at(column).getPropertyIndex(); } - /* - * Property index to column - */ int CColumns::propertyIndexToColumn(const BlackMisc::CPropertyIndex &propertyIndex) const { for (int i = 0; i < this->m_columns.size(); i++) @@ -113,9 +110,6 @@ namespace BlackGui return -1; } - /* - * Name to property index - */ int CColumns::nameToPropertyIndex(const QString &name) const { for (int i = 0; i < this->m_columns.size(); i++) @@ -126,53 +120,29 @@ namespace BlackGui return -1; } - /* - * Size - */ int CColumns::size() const { return this->m_columns.size(); } - /* - * Alignment? - */ bool CColumns::hasAlignment(const QModelIndex &index) const { - if (index.column() < 0 || index.column() >= this->m_columns.size()) return false; + if (!isValidColumn(index)) return false; return this->m_columns.at(index.column()).hasAlignment(); } - /* - * Editable? - */ bool CColumns::isEditable(const QModelIndex &index) const { - if (index.column() < 0 || index.column() >= this->m_columns.size()) return false; + if (!isValidColumn(index)) return false; return this->m_columns.at(index.column()).isEditable(); } - /* - * Is icon? - */ - bool CColumns::isIcon(const QModelIndex &index) const + const CDefaultFormatter *CColumns::getFormatter(const QModelIndex &index) const { - if (index.column() < 0 || index.column() >= this->m_columns.size()) return false; - return this->m_columns.at(index.column()).isIcon(); + if (!isValidColumn(index)) return nullptr; + return this->m_columns.at(index.column()).getFormatter(); } - /* - * Aligment as QVariant - */ - QVariant CColumns::aligmentAsQVariant(const QModelIndex &index) const - { - if (index.column() < 0 || index.column() >= this->m_columns.size()) return QVariant(); - return this->m_columns.at(index.column()).aligmentAsQVariant(); - } - - /* - * Context - */ const char *CColumns::getTranslationContextChar() const { return this->m_translationContext.toUtf8().constData(); diff --git a/src/blackgui/models/columns.h b/src/blackgui/models/columns.h index 1de217280..0088afe20 100644 --- a/src/blackgui/models/columns.h +++ b/src/blackgui/models/columns.h @@ -15,8 +15,11 @@ #include "blackmisc/valueobject.h" // for qHash overload, include before Qt stuff due GCC issue #include "blackmisc/collection.h" #include "blackmisc/propertyindex.h" +#include "columnformatters.h" +#include #include #include +#include namespace BlackGui { @@ -26,64 +29,63 @@ namespace BlackGui class CColumn { public: - /*! - * Constructor - * \param headerName - * \param propertyIndex as in CValueObject::propertyByIndex - * \param alignment Qt::Alignment - * \param editable - */ - CColumn(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment = -1, bool editable = false); + //! Constructor + CColumn(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, CDefaultFormatter *formatter, bool editable = false) : + CColumn(headerName, "", propertyIndex, formatter, editable) + { } - /*! - * Constructor - * \param headerName - * \param propertyIndex as in CValueObject::propertyByIndex - * \param editable - */ - CColumn(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, bool editable); + //! Constructor + CColumn(const QString &headerName, const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex, CDefaultFormatter *formatter, bool editable = false); - /*! - * Constructor column is icon - * \remarks only make sense with isIcon as true - * \param propertyIndex as in CValueObject::propertyByIndex - * \param isIcon icon, should be used with true only - */ - CColumn(const BlackMisc::CPropertyIndex &propertyIndex, bool isIcon); + //! Constructor, icon with tool tip + CColumn(const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex); + + //! Constructor used for icons + CColumn(const BlackMisc::CPropertyIndex &propertyIndex); //! Alignment for this column? - bool hasAlignment() const { return this->m_alignment >= 0; } + bool hasAlignment() const { return (!this->m_formatter.isNull() && this->m_formatter->hasAlignment()); } //! Editable? bool isEditable() const { return this->m_editable; } - //! Icon? - bool isIcon() const { return this->m_icon; } + //! Formatter + void setFormatter(CDefaultFormatter *formatter) { Q_ASSERT(formatter); m_formatter.reset(formatter); } + + //! Formatter + const CDefaultFormatter *getFormatter() const { return this->m_formatter.data(); } //! Aligment as QVariant - QVariant aligmentAsQVariant() const; + QVariant getAlignment() const; //! Column name QString getColumnName(bool i18n = false) const; + //! Column tooltip + QString getColumnToolTip(bool i18n = false) const; + //! Property index const BlackMisc::CPropertyIndex &getPropertyIndex() const { return this->m_propertyIndex;} //! Translation context - void setTranslationContext(const QString &translationContext) - { - this->m_translationContext = translationContext; - } + void setTranslationContext(const QString &translationContext) { this->m_translationContext = translationContext; } + + //! Get a standard value object formatted column + static CColumn standardValueObject(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment = CDefaultFormatter::alignDefault()); + + //! Get a standard string object formatted column + static CColumn standardString(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment = CDefaultFormatter::alignDefault()); private: QString m_translationContext; QString m_columnName; - int m_alignment; + QString m_columnToolTip; + QSharedPointer m_formatter; BlackMisc::CPropertyIndex m_propertyIndex; bool m_editable; - bool m_icon; const char *getTranslationContextChar() const; const char *getColumnNameChar() const; + const char *getColumnToolTipChar() const; }; /*! @@ -129,11 +131,26 @@ namespace BlackGui //! Is this column editable? bool isEditable(const QModelIndex &index) const; - //! Is icon? - bool isIcon(const QModelIndex &index) const; + //! Valid column? + bool isValidColumn(const QModelIndex &index) const + { + return (index.column() >= 0 && index.column() < this->m_columns.size()); + } + + //! Valid column? + bool isValidColumn(int column) const + { + return column >= 0 && column < this->m_columns.size(); + } //! Aligment as QVariant - QVariant aligmentAsQVariant(const QModelIndex &index) const; + QVariant getAlignment(const QModelIndex &index) const; + + //! Translation context + const QString &getTranslationContext() const { return m_translationContext; } + + //! Formatter + const CDefaultFormatter *getFormatter(const QModelIndex &index) const; //! Column at position const CColumn &at(int columnNumber) const { return this->m_columns.at(columnNumber); } diff --git a/src/blackgui/models/keyboardkeylistmodel.cpp b/src/blackgui/models/keyboardkeylistmodel.cpp index 4ec1d2fdb..34c288e9f 100644 --- a/src/blackgui/models/keyboardkeylistmodel.cpp +++ b/src/blackgui/models/keyboardkeylistmodel.cpp @@ -28,10 +28,11 @@ namespace BlackGui CKeyboardKeyListModel::CKeyboardKeyListModel(QObject *parent) : CListModelBase("ViewKeyboardKeyList", parent) { - this->m_columns.addColumn(CColumn("key", CSettingKeyboardHotkey::IndexKeyAsStringRepresentation, true)); - this->m_columns.addColumn(CColumn("modifier 1", CSettingKeyboardHotkey::IndexModifier1AsString, true)); - this->m_columns.addColumn(CColumn("modifier 2", CSettingKeyboardHotkey::IndexModifier2AsString, true)); - this->m_columns.addColumn(CColumn("function", CSettingKeyboardHotkey::IndexFunctionAsString)); + const bool editable = true; + this->m_columns.addColumn(CColumn("key", CSettingKeyboardHotkey::IndexKeyAsStringRepresentation, new CStringFormatter(), editable)); + this->m_columns.addColumn(CColumn("modifier 1", CSettingKeyboardHotkey::IndexModifier1AsString, new CStringFormatter(), editable)); + this->m_columns.addColumn(CColumn("modifier 2", CSettingKeyboardHotkey::IndexModifier2AsString, new CStringFormatter(), editable)); + this->m_columns.addColumn(CColumn::standardString("function", CSettingKeyboardHotkey::IndexFunctionAsString)); this->setSortColumnByPropertyIndex(CSettingKeyboardHotkey::IndexFunctionAsString); this->m_sortOrder = Qt::AscendingOrder; diff --git a/src/blackgui/models/listmodelbase.cpp b/src/blackgui/models/listmodelbase.cpp index 3d3e694e9..176d2b767 100644 --- a/src/blackgui/models/listmodelbase.cpp +++ b/src/blackgui/models/listmodelbase.cpp @@ -57,11 +57,20 @@ namespace BlackGui template QVariant CListModelBase::headerData(int section, Qt::Orientation orientation, int role) const { - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + if (orientation == Qt::Horizontal) { - if (section < 0 || section >= this->m_columns.size()) return QVariant(); - QString header = this->m_columns.at(section).getColumnName(false); - return QVariant(header); + if (role == Qt::DisplayRole) + { + if (section < 0 || section >= this->m_columns.size()) { return QVariant(); } + QString header = this->m_columns.at(section).getColumnName(false); + return QVariant(header); + } + else if (role == Qt::ToolTipRole) + { + if (section < 0 || section >= this->m_columns.size()) { return QVariant(); } + QString header = this->m_columns.at(section).getColumnToolTip(false); + return header.isEmpty() ? QVariant() : QVariant(header); + } } return QVariant(); } @@ -72,33 +81,16 @@ namespace BlackGui template QVariant CListModelBase::data(const QModelIndex &index, int role) const { - // checks - if (index.row() < 0 || index.row() >= this->m_container.size() || - index.column() < 0 || index.column() >= this->columnCount(index)) - { - return QVariant(); - } + // check / init + if (!this->isValidIndex(index)) { return QVariant(); } + const CDefaultFormatter *formatter = this->m_columns.getFormatter(index); + Q_ASSERT(formatter); + if (!formatter) { return QVariant(); } - if (role == Qt::DisplayRole) - { - if (this->m_columns.isIcon(index)) return QVariant(); - ObjectType obj = this->m_container[index.row()]; - BlackMisc::CPropertyIndex propertyIndex = this->columnToPropertyIndex(index.column()); - QString propertyString = obj.propertyByIndexAsString(propertyIndex, true); - return QVariant::fromValue(propertyString); - } - else if (role == Qt::DecorationRole) - { - if (!this->m_columns.isIcon(index)) return QVariant(); - ObjectType obj = this->m_container[index.row()]; - BlackMisc::CPropertyIndex propertyIndex = this->columnToPropertyIndex(index.column()); - return obj.propertyByIndex(propertyIndex); - } - else if (role == Qt::TextAlignmentRole) - { - return this->m_columns.aligmentAsQVariant(index); - } - return QVariant(); + //! Formatted data + ObjectType obj = this->m_container[index.row()]; + BlackMisc::CPropertyIndex propertyIndex = this->columnToPropertyIndex(index.column()); + return formatter->data(role, obj.propertyByIndex(propertyIndex)); } /* @@ -107,15 +99,11 @@ namespace BlackGui template int CListModelBase::update(const ContainerType &container) { - ContainerType copyList = (container.size() > 1 && this->hasValidSortColumn() ? - this->sortListByColumn(container, this->getSortColumn(), this->m_sortOrder) : - container); + qDebug() << "update" << this->objectName() << "size" << container.size(); this->beginResetModel(); - this->m_container.clear(); - foreach(ObjectType object, copyList) - { - this->m_container.push_back(object); - } + this->m_container = (container.size() > 1 && this->hasValidSortColumn() ? + this->sortListByColumn(container, this->getSortColumn(), this->m_sortOrder) : + container); this->endResetModel(); return this->m_container.size(); } @@ -202,6 +190,7 @@ namespace BlackGui BlackMisc::CPropertyIndex propertyIndex = this->m_columns.columnToPropertyIndex(column); Q_ASSERT(!propertyIndex.isEmpty()); if (propertyIndex.isEmpty()) return list; // at release build do nothing + qDebug() << "sort" << this->objectName() << "column" << column << propertyIndex.toQString(); // sort the values return list.sorted diff --git a/src/blackgui/models/listmodelbase.h b/src/blackgui/models/listmodelbase.h index 355eabadb..f37131b8f 100644 --- a/src/blackgui/models/listmodelbase.h +++ b/src/blackgui/models/listmodelbase.h @@ -27,32 +27,6 @@ namespace BlackGui template class CListModelBase : public QAbstractListModel { - protected: - ContainerType m_container; //!< used container - CColumns m_columns; //!< columns metadata - int m_sortedColumn; //!< current sort column - Qt::SortOrder m_sortOrder; //!< sort order (asc/desc) - - /*! - * Constructor - * \param translationContext I18N context - * \param parent - */ - CListModelBase(const QString &translationContext, QObject *parent = nullptr) - : QAbstractListModel(parent), m_columns(translationContext), m_sortedColumn(-1), m_sortOrder(Qt::AscendingOrder) - { - // void - } - - /*! - * Sort list by given order - * \param list used list - * \param column column inder - * \param order sort order (asccending / descending) - * \return - */ - ContainerType sortListByColumn(const ContainerType &list, int column, Qt::SortOrder order); - public: //! Destructor @@ -73,12 +47,17 @@ namespace BlackGui return this->columnToPropertyIndex(index.column()); } - //! Set sort column - virtual void setSortColumn(int column) + //! Valid index (in range) + virtual bool isValidIndex(const QModelIndex &index) const { - this->m_sortedColumn = column; + if (!index.isValid()) return false; + return (index.row() >= 0 && index.row() < this->m_container.size() && + index.column() >= 0 && index.column() < this->columnCount(index)); } + //! Set sort column + virtual void setSortColumn(int column) { this->m_sortedColumn = column; } + /*! * Set column for sorting * \param propertyIndex index of column to be sorted by @@ -89,28 +68,19 @@ namespace BlackGui } //! Get sort column property index - virtual int getSortColumn() const - { - return this->m_sortedColumn; - } + virtual int getSortColumn() const { return this->m_sortedColumn; } //! Has valid sort column? virtual bool hasValidSortColumn() const { - return this->m_sortedColumn >=0 && this->m_sortedColumn < this->m_columns.size(); + return this->m_sortedColumn >= 0 && this->m_sortedColumn < this->m_columns.size(); } //! Get sort order - virtual Qt::SortOrder getSortOrder() const - { - return this->m_sortOrder; - } + virtual Qt::SortOrder getSortOrder() const { return this->m_sortOrder; } //! Used container data - virtual const ContainerType &getContainer() const - { - return this->m_container; - } + virtual const ContainerType &getContainer() const { return this->m_container; } //! \copydoc QAbstractListModel::data() virtual QVariant data(const QModelIndex &index, int role) const override; @@ -122,6 +92,7 @@ namespace BlackGui Qt::ItemFlags flags(const QModelIndex &index) const override; //! Update by new container + //! \remarks a sorting is performed if a valid sort column is set virtual int update(const ContainerType &container); //! Update single element @@ -138,7 +109,7 @@ namespace BlackGui { if (index.row() < 0 || index.row() >= this->m_container.size()) { - const static ObjectType def; + const static ObjectType def; // default object return def; } else @@ -148,7 +119,7 @@ namespace BlackGui } //! \copydoc QAbstractListModel::sort() - virtual void sort(int column, Qt::SortOrder order); + virtual void sort(int column, Qt::SortOrder order) override; //! Similar to ContainerType::push_back virtual void push_back(const ObjectType &object); @@ -161,6 +132,40 @@ namespace BlackGui //! Clear the list virtual void clear(); + + //! Translation context + virtual const QString &getTranslationContext() const + { + return m_columns.getTranslationContext(); + } + + protected: + ContainerType m_container; //!< used container + CColumns m_columns; //!< columns metadata + int m_sortedColumn; //!< current sort column + Qt::SortOrder m_sortOrder; //!< sort order (asc/desc) + + /*! + * Constructor + * \param translationContext I18N context + * \param parent + */ + CListModelBase(const QString &translationContext, QObject *parent = nullptr) + : QAbstractListModel(parent), m_columns(translationContext), m_sortedColumn(-1), m_sortOrder(Qt::AscendingOrder) + { + // non unique default name, set translation context as default + this->setObjectName(translationContext); + } + + /*! + * Sort container by given column / order. This is used by sort(). + * \param list used list + * \param column column inder + * \param order sort order (ascending / descending) + * \return + */ + ContainerType sortListByColumn(const ContainerType &list, int column, Qt::SortOrder order); + }; } } diff --git a/src/blackgui/models/namevariantpairlistmodel.cpp b/src/blackgui/models/namevariantpairlistmodel.cpp index 0843a8d5e..84501ed33 100644 --- a/src/blackgui/models/namevariantpairlistmodel.cpp +++ b/src/blackgui/models/namevariantpairlistmodel.cpp @@ -30,10 +30,10 @@ namespace BlackGui this->m_columns.clear(); if (withIcon) { - this->m_columns.addColumn(CColumn(CNameVariantPair::IndexPixmap, true)); + this->m_columns.addColumn(CColumn(CNameVariantPair::IndexPixmap)); } - this->m_columns.addColumn(CColumn("name", CNameVariantPair::IndexName)); - this->m_columns.addColumn(CColumn("value", CNameVariantPair::IndexVariant)); + this->m_columns.addColumn(CColumn::standardString("name", CNameVariantPair::IndexName)); + this->m_columns.addColumn(CColumn("value", CNameVariantPair::IndexVariant, new CDefaultFormatter())); // default sort order this->setSortColumnByPropertyIndex(CNameVariantPair::IndexName); diff --git a/src/blackgui/models/serverlistmodel.cpp b/src/blackgui/models/serverlistmodel.cpp index 0236530d7..98613d2f2 100644 --- a/src/blackgui/models/serverlistmodel.cpp +++ b/src/blackgui/models/serverlistmodel.cpp @@ -21,12 +21,12 @@ namespace BlackGui CServerListModel::CServerListModel(QObject *parent) : CListModelBase("ViewServerList", parent) { - this->m_columns.addColumn(CColumn("name", CServer::IndexName)); - this->m_columns.addColumn(CColumn("description", CServer::IndexDescription)); - this->m_columns.addColumn(CColumn("address", CServer::IndexAddress)); - this->m_columns.addColumn(CColumn("port", CServer::IndexPort)); - this->m_columns.addColumn(CColumn("realname", { CServer::IndexUser, CUser::IndexRealName})); - this->m_columns.addColumn(CColumn("userid", { CServer::IndexUser, CUser::IndexId})); + this->m_columns.addColumn(CColumn::standardString("name", CServer::IndexName)); + this->m_columns.addColumn(CColumn::standardString("description", CServer::IndexDescription)); + this->m_columns.addColumn(CColumn::standardString("address", CServer::IndexAddress)); + this->m_columns.addColumn(CColumn::standardString("port", CServer::IndexPort)); + this->m_columns.addColumn(CColumn::standardString("realname", { CServer::IndexUser, CUser::IndexRealName})); + this->m_columns.addColumn(CColumn::standardString("userid", { CServer::IndexUser, CUser::IndexId})); // force strings for translation in resource files (void)QT_TRANSLATE_NOOP("ViewServerList", "name"); @@ -61,5 +61,5 @@ namespace BlackGui return CListModelBase::data(index, role); } } - } -} + } // class +} // namespace diff --git a/src/blackgui/models/serverlistmodel.h b/src/blackgui/models/serverlistmodel.h index b84fe462d..4bd1c546e 100644 --- a/src/blackgui/models/serverlistmodel.h +++ b/src/blackgui/models/serverlistmodel.h @@ -54,8 +54,7 @@ namespace BlackGui } //! \copydoc CListModelBase::data - virtual QVariant data(const QModelIndex &index, int role) const; - + virtual QVariant data(const QModelIndex &index, int role) const override; }; } } diff --git a/src/blackgui/models/statusmessagelistmodel.cpp b/src/blackgui/models/statusmessagelistmodel.cpp index 9eb1becdf..28f1d4d09 100644 --- a/src/blackgui/models/statusmessagelistmodel.cpp +++ b/src/blackgui/models/statusmessagelistmodel.cpp @@ -26,10 +26,10 @@ namespace BlackGui CStatusMessageListModel::CStatusMessageListModel(QObject *parent) : CListModelBase("ViewStatusMessageList", parent) { - this->m_columns.addColumn(CColumn("time", CStatusMessage::IndexTimestamp)); - this->m_columns.addColumn(CColumn("", CStatusMessage::IndexSeverity, Qt::AlignCenter)); - this->m_columns.addColumn(CColumn("message", CStatusMessage::IndexMessage)); - this->m_columns.addColumn(CColumn("type", CStatusMessage::IndexTypeAsString)); + this->m_columns.addColumn(CColumn("time", CStatusMessage::IndexTimestamp, new CDateTimeFormatter(CDateTimeFormatter::formatHms()))); + this->m_columns.addColumn(CColumn("severity", CStatusMessage::IndexSeverity)); + this->m_columns.addColumn(CColumn::standardString("message", CStatusMessage::IndexMessage)); + this->m_columns.addColumn(CColumn::standardString("type", CStatusMessage::IndexTypeAsString)); this->m_sortedColumn = CStatusMessage::IndexTimestamp; this->m_sortOrder = Qt::DescendingOrder; diff --git a/src/blackgui/models/statusmessagelistmodel.h b/src/blackgui/models/statusmessagelistmodel.h index 9f27c65e7..db060a8ef 100644 --- a/src/blackgui/models/statusmessagelistmodel.h +++ b/src/blackgui/models/statusmessagelistmodel.h @@ -36,7 +36,7 @@ namespace BlackGui virtual ~CStatusMessageListModel() {} //! \copydoc CListModelBase::data - QVariant data(const QModelIndex &modelIndex, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &modelIndex, int role = Qt::DisplayRole) const override; }; } } diff --git a/src/blackgui/models/userlistmodel.cpp b/src/blackgui/models/userlistmodel.cpp index d2e3243f3..1d375315f 100644 --- a/src/blackgui/models/userlistmodel.cpp +++ b/src/blackgui/models/userlistmodel.cpp @@ -25,6 +25,7 @@ namespace BlackGui CListModelBase("ViewUserList", parent), m_userMode(NotSet) { this->setUserMode(userMode); + // force strings for translation in resource files (void)QT_TRANSLATE_NOOP("ViewUserList", "callsign"); (void)QT_TRANSLATE_NOOP("ViewUserList", "realname"); @@ -44,17 +45,24 @@ namespace BlackGui { case NotSet: case UserDetailed: - this->m_columns.addColumn(CColumn(CUser::IndexPixmap, true)); - this->m_columns.addColumn(CColumn("realname", CUser::IndexRealName)); - this->m_columns.addColumn(CColumn("callsign", CUser::IndexCallsign)); - this->m_columns.addColumn(CColumn("userid", CUser::IndexId)); - // this->m_columns.addColumn(CUser::IndexEmail, "email"); + this->m_columns.addColumn(CColumn(CUser::IndexIcon)); + this->m_columns.addColumn(CColumn::standardString("realname", CUser::IndexRealName)); + this->m_columns.addColumn(CColumn::standardValueObject("callsign", CUser::IndexCallsign)); + this->m_columns.addColumn(CColumn::standardString("userid", CUser::IndexId)); + + // default sort order + this->setSortColumnByPropertyIndex(CUser::IndexRealName); + this->m_sortOrder = Qt::AscendingOrder; break; case UserShort: - this->m_columns.addColumn(CColumn(CUser::IndexPixmap, true)); - this->m_columns.addColumn(CColumn("realname", CUser::IndexRealName)); - this->m_columns.addColumn(CColumn("callsign", CUser::IndexCallsign)); + this->m_columns.addColumn(CColumn(CUser::IndexIcon)); + this->m_columns.addColumn(CColumn::standardString("realname", CUser::IndexRealName)); + this->m_columns.addColumn(CColumn::standardValueObject("callsign", CUser::IndexCallsign)); + + // default sort order + this->setSortColumnByPropertyIndex(CUser::IndexCallsign); + this->m_sortOrder = Qt::AscendingOrder; break; default: diff --git a/src/blackmisc/avatcstation.cpp b/src/blackmisc/avatcstation.cpp index aa88c91a6..2d401d56f 100644 --- a/src/blackmisc/avatcstation.cpp +++ b/src/blackmisc/avatcstation.cpp @@ -351,10 +351,7 @@ namespace BlackMisc case IndexVoiceRoom: return QVariant(this->m_voiceRoom.propertyByIndex(index.copyFrontRemoved())); default: - if (ICoordinateGeodetic::canHandleIndex(index)) - { - return ICoordinateGeodetic::propertyByIndex(index); - } + if (ICoordinateGeodetic::canHandleIndex(index)) { return ICoordinateGeodetic::propertyByIndex(index); } return CValueObject::propertyByIndex(index); } } @@ -424,49 +421,8 @@ namespace BlackMisc bool CAtcStation::isA(int metaTypeId) const { if (metaTypeId == qMetaTypeId()) { return true; } - return this->CValueObject::isA(metaTypeId); } - /* - * Property as string by index - */ - QString CAtcStation::propertyByIndexAsString(const BlackMisc::CPropertyIndex &index, bool i18n) const - { - QVariant qv = this->propertyByIndex(index); - ColumnIndex i = index.frontCasted(); - - // special treatment - // this is required as it is possible an ATC station is not containing all - // properties - switch (i) - { - case IndexFrequency: - if (!CComSystem::isValidCivilAviationFrequency(qv.value())) - return ""; - else - return qv.value().valueRoundedWithUnit(3, i18n); - break; - case IndexDistance: - { - CLength distance = qv.value(); - if (distance.isNegativeWithEpsilonConsidered()) return ""; - return distance.toQString(i18n); - } - - case IndexBookedFrom: - case IndexBookedUntil: - { - QDateTime dt = qv.value(); - if (dt.isNull() || !dt.isValid()) return ""; - return dt.toString("yyyy-MM-dd HH:mm"); - break; - } - default: - break; - } - return BlackMisc::qVariantToString(qv, i18n); - } - } // namespace } // namespace diff --git a/src/blackmisc/avatcstation.h b/src/blackmisc/avatcstation.h index e8b154c47..91e1c8d6c 100644 --- a/src/blackmisc/avatcstation.h +++ b/src/blackmisc/avatcstation.h @@ -278,9 +278,6 @@ namespace BlackMisc //! \copydoc CValueObject::setPropertyByIndex(variant, index) virtual void setPropertyByIndex(const QVariant &variant, const CPropertyIndex &index) override; - //! \copydoc CValueObject::propertyByIndexAsString() - virtual QString propertyByIndexAsString(const BlackMisc::CPropertyIndex &index, bool i18n = false) const override; - //! \copydoc CValueObject::toJson virtual QJsonObject toJson() const override; diff --git a/src/blackmisc/aviocomsystem.h b/src/blackmisc/aviocomsystem.h index ea8772735..434eacdd9 100644 --- a/src/blackmisc/aviocomsystem.h +++ b/src/blackmisc/aviocomsystem.h @@ -130,6 +130,7 @@ namespace BlackMisc //! Valid civil aviation frequency? static bool isValidCivilAviationFrequency(const BlackMisc::PhysicalQuantities::CFrequency &f) { + if (f.isNull()) return false; double fr = f.valueRounded(BlackMisc::PhysicalQuantities::CFrequencyUnit::MHz(), 3); return fr >= 118.0 && fr <= 136.975; } @@ -137,10 +138,17 @@ namespace BlackMisc //! Valid military aviation frequency? static bool isValidMilitaryFrequency(const BlackMisc::PhysicalQuantities::CFrequency &f) { + if (f.isNull()) return false; double fr = f.valueRounded(BlackMisc::PhysicalQuantities::CFrequencyUnit::MHz(), 3); return fr >= 220.0 && fr <= 399.95; } + //! Valid COM frequency (either civil or military) + static bool isValidComFrequency(const BlackMisc::PhysicalQuantities::CFrequency &f) + { + return isValidCivilAviationFrequency(f) || isValidMilitaryFrequency(f); + } + /*! * Round to channel spacing, set MHz as unit * \see ChannelSpacing