From 961456ff19ddf7c97dcd74369eec44a2c59bd12f Mon Sep 17 00:00:00 2001 From: Joachim Kohlhammer Date: Sat, 14 Mar 2026 23:36:39 +0100 Subject: [PATCH] Calendar: New option to hide planned activities from summary (#4843) * Options: * Include all planned activities * Include no planned activities * Include upcoming planned activities * Include upcoming or missed planned activities * Tooltip shows if an activity is skipped in the summary * Added new filter type to Specification [publish binaries] --- src/Charts/CalendarWindow.cpp | 35 +++++++++++++++++++++ src/Charts/CalendarWindow.h | 4 +++ src/Core/Specification.cpp | 52 +++++++++++++++++++++++++++++-- src/Core/Specification.h | 25 ++++++++++++++- src/Gui/CalendarData.h | 1 + src/Gui/CalendarItemDelegates.cpp | 5 ++- 6 files changed, 118 insertions(+), 4 deletions(-) diff --git a/src/Charts/CalendarWindow.cpp b/src/Charts/CalendarWindow.cpp index c7c8d6818..1be956c27 100644 --- a/src/Charts/CalendarWindow.cpp +++ b/src/Charts/CalendarWindow.cpp @@ -297,6 +297,7 @@ CalendarWindow::CalendarWindow(Context *context) setStartHour(8); setEndHour(21); setShowSecondaryLabel(true); + setSummaryIncludePlanned(0); QVBoxLayout *mainLayout = new QVBoxLayout(); setChartLayout(mainLayout); @@ -539,6 +540,22 @@ CalendarWindow::isSummaryVisibleDay } +int +CalendarWindow::getSummaryIncludePlanned +() const +{ + return includePlannedCombo->currentIndex(); +} + + +void +CalendarWindow::setSummaryIncludePlanned +(int type) +{ + includePlannedCombo->setCurrentIndex(type); +} + + void CalendarWindow::setSummaryVisibleDay (bool visible) @@ -799,6 +816,11 @@ CalendarWindow::mkControls endHourSpin = new QSpinBox(); endHourSpin->setSuffix(":00"); endHourSpin->setMaximum(24); + includePlannedCombo = new QComboBox(); + includePlannedCombo->addItem(tr("Always"), QVariant::fromValue(PlanFilterType::IncludeAll)); + includePlannedCombo->addItem(tr("If upcoming or missed"), QVariant::fromValue(PlanFilterType::IncludeIfUpcomingOrMissed)); + includePlannedCombo->addItem(tr("If upcoming"), QVariant::fromValue(PlanFilterType::IncludeIfUpcoming)); + includePlannedCombo->addItem(tr("Never"), QVariant::fromValue(PlanFilterType::IncludeNone)); summaryDayCheck = new QCheckBox(tr("Day View")); summaryDayCheck->setChecked(true); summaryWeekCheck = new QCheckBox(tr("Week View")); @@ -836,6 +858,7 @@ CalendarWindow::mkControls generalForm->addRow(tr("Default End Time"), endHourSpin); generalForm->addItem(new QSpacerItem(0, 20 * dpiYFactor)); generalForm->addRow(new QLabel(HLO + tr("Summary Options") + HLC)); + generalForm->addRow(tr("Include Planned"), includePlannedCombo); generalForm->addRow(tr("Show Summary In"), summaryDayCheck); generalForm->addRow("", summaryWeekCheck); generalForm->addRow("", summaryMonthCheck); @@ -866,6 +889,7 @@ CalendarWindow::mkControls connect(primaryFallbackCombo, &QComboBox::currentIndexChanged, this, &CalendarWindow::updateActivities); connect(secondaryCombo, &QComboBox::currentIndexChanged, this, &CalendarWindow::updateActivities); connect(tertiaryCombo, &QComboBox::currentIndexChanged, this, &CalendarWindow::updateActivities); + connect(includePlannedCombo, &QComboBox::currentIndexChanged, this, &CalendarWindow::updateActivities); connect(summaryDayCheck, &QCheckBox::toggled, this, &CalendarWindow::setSummaryVisibleDay); connect(summaryWeekCheck, &QCheckBox::toggled, this, &CalendarWindow::setSummaryVisibleWeek); connect(summaryMonthCheck, &QCheckBox::toggled, this, &CalendarWindow::setSummaryVisibleMonth); @@ -969,6 +993,11 @@ CalendarWindow::getActivities } } + PlanFilter planFilter(PlanFilterType::IncludeAll); + if (includePlannedCombo->currentIndex() != -1) { + planFilter.setType(includePlannedCombo->currentData().value()); + } + for (RideItem *rideItem : rides) { if ( rideItem == nullptr || ! rideItem->dateTime.isValid()) { @@ -1021,6 +1050,7 @@ CalendarWindow::getActivities activity.dirty = rideItem->isDirty(); if (rideItem->planned) { activity.originalPlanLabel = buildOriginalLabel(rideItem); + activity.isExcludedFromSummary = ! planFilter.pass(rideItem); } RideItem *linkedRide = context->athlete->rideCache->getLinkedActivity(rideItem); @@ -1061,6 +1091,11 @@ CalendarWindow::getSummaries FilterSet filterSet(context->isfiltered, context->filters); Specification spec; spec.setFilterSet(filterSet); + PlanFilterType planFilterType = PlanFilterType::IncludeAll; + if (includePlannedCombo->currentIndex() != -1) { + planFilterType = includePlannedCombo->currentData().value(); + } + spec.setPlanFilter(planFilterType); for (int timeBucket = 0; timeBucket < numTimeBuckets; ++timeBucket) { QDate firstDayOfTimeBucket = firstDay.addDays(timeBucket * timeBucketSize); QDate lastDayOfTimeBucket = firstDayOfTimeBucket.addDays(timeBucketSize - 1); diff --git a/src/Charts/CalendarWindow.h b/src/Charts/CalendarWindow.h index 0601003cd..661e611f8 100644 --- a/src/Charts/CalendarWindow.h +++ b/src/Charts/CalendarWindow.h @@ -79,6 +79,7 @@ class CalendarWindow : public GcChartWindow Q_PROPERTY(int firstDayOfWeek READ getFirstDayOfWeek WRITE setFirstDayOfWeek USER true) Q_PROPERTY(int startHour READ getStartHour WRITE setStartHour USER true) Q_PROPERTY(int endHour READ getEndHour WRITE setEndHour USER true) + Q_PROPERTY(int summaryIncludePlanned READ getSummaryIncludePlanned WRITE setSummaryIncludePlanned USER true) Q_PROPERTY(bool summaryVisibleDay READ isSummaryVisibleDay WRITE setSummaryVisibleDay USER true) Q_PROPERTY(bool summaryVisibleWeek READ isSummaryVisibleWeek WRITE setSummaryVisibleWeek USER true) Q_PROPERTY(bool summaryVisibleMonth READ isSummaryVisibleMonth WRITE setSummaryVisibleMonth USER true) @@ -96,6 +97,7 @@ class CalendarWindow : public GcChartWindow int getFirstDayOfWeek() const; int getStartHour() const; int getEndHour() const; + int getSummaryIncludePlanned() const; bool isSummaryVisibleDay() const; bool isSummaryVisibleWeek() const; bool isSummaryVisibleMonth() const; @@ -115,6 +117,7 @@ class CalendarWindow : public GcChartWindow void setFirstDayOfWeek(int fdw); void setStartHour(int hour); void setEndHour(int hour); + void setSummaryIncludePlanned(int type); void setSummaryVisibleDay(bool visible); void setSummaryVisibleWeek(bool visible); void setSummaryVisibleMonth(bool svm); @@ -139,6 +142,7 @@ class CalendarWindow : public GcChartWindow QComboBox *firstDayOfWeekCombo; QSpinBox *startHourSpin; QSpinBox *endHourSpin; + QComboBox *includePlannedCombo; QCheckBox *summaryDayCheck; QCheckBox *summaryWeekCheck; QCheckBox *summaryMonthCheck; diff --git a/src/Core/Specification.cpp b/src/Core/Specification.cpp index 345ad0b32..60d6774c8 100644 --- a/src/Core/Specification.cpp +++ b/src/Core/Specification.cpp @@ -21,7 +21,49 @@ #include "IntervalItem.h" #include "RideFile.h" -Specification::Specification(DateRange dr, FilterSet fs) : dr(dr), fs(fs), it(NULL), recintsecs(0), ri(NULL) {} + +PlanFilter::PlanFilter +(PlanFilterType type) +: type(type) +{ +} + + +void +PlanFilter::setType +(PlanFilterType type) +{ + this->type = type; +} + + +PlanFilterType +PlanFilter::getType +() const +{ + return type; +} + + +bool +PlanFilter::pass +(RideItem const * const rideItem) const +{ + if (!rideItem->planned || type == PlanFilterType::IncludeAll) { + return true; + } else if (type == PlanFilterType::IncludeIfUpcomingOrMissed) { + return !rideItem->hasLinkedActivity(); + } else if (type == PlanFilterType::IncludeIfUpcoming) { + return rideItem->dateTime.date() >= QDate::currentDate() + && !rideItem->hasLinkedActivity(); + } else if (type == PlanFilterType::IncludeNone) { + return false; + } + return true; +} + + +Specification::Specification(DateRange dr, FilterSet fs, PlanFilter pf) : dr(dr), fs(fs), pf(pf), it(NULL), recintsecs(0), ri(NULL) {} Specification::Specification(IntervalItem *it, double recintsecs) : it(it), recintsecs(recintsecs), ri(NULL) {} Specification::Specification() : it(NULL), recintsecs(0), ri(NULL) {} @@ -36,7 +78,7 @@ Specification::pass(QDate date) const bool Specification::pass(RideItem*item) const { - return (dr.pass(item->dateTime.date()) && fs.pass(item->fileName)); + return (dr.pass(item->dateTime.date()) && fs.pass(item->fileName) && pf.pass(item)); } bool @@ -58,6 +100,12 @@ void Specification::setFilterSet(FilterSet fs) { this->fs = fs; +} + +void +Specification::setPlanFilter(PlanFilter pf) +{ + this->pf = pf; } void diff --git a/src/Core/Specification.h b/src/Core/Specification.h index a43e6dfb6..3128b071e 100644 --- a/src/Core/Specification.h +++ b/src/Core/Specification.h @@ -74,12 +74,33 @@ class FilterSet int count() { return filters_.count(); } }; +enum class PlanFilterType { + IncludeAll, + IncludeIfUpcomingOrMissed, + IncludeIfUpcoming, + IncludeNone +}; +Q_DECLARE_METATYPE(PlanFilterType) + +class PlanFilter +{ + public: + PlanFilter(PlanFilterType type = PlanFilterType::IncludeAll); + + void setType(PlanFilterType type); + PlanFilterType getType() const; + bool pass(RideItem const * const rideItem) const; + + private: + PlanFilterType type = PlanFilterType::IncludeAll; +}; + class RideFileIterator; class Specification { friend class ::RideFileIterator; public: - Specification(DateRange dr, FilterSet fs); + Specification(DateRange dr, FilterSet fs, PlanFilter pf = PlanFilter()); Specification(IntervalItem *it, double recintsecs); Specification(); @@ -101,6 +122,7 @@ class Specification // set criteria void setDateRange(DateRange dr); void setFilterSet(FilterSet fs); + void setPlanFilter(PlanFilter pf); void setIntervalItem(IntervalItem *it, double recintsecs); void setRideItem(RideItem *ri); @@ -123,6 +145,7 @@ class Specification private: DateRange dr; FilterSet fs; + PlanFilter pf; IntervalItem *it; double recintsecs; RideItem *ri; diff --git a/src/Gui/CalendarData.h b/src/Gui/CalendarData.h index 15a89f890..306ebe081 100644 --- a/src/Gui/CalendarData.h +++ b/src/Gui/CalendarData.h @@ -65,6 +65,7 @@ struct CalendarEntry { QTime start; int durationSecs = 0; int type = 0; + bool isExcludedFromSummary = false; bool isRelocatable = false; bool hasTrainMode = false; bool dirty = false; diff --git a/src/Gui/CalendarItemDelegates.cpp b/src/Gui/CalendarItemDelegates.cpp index 0a56a5c16..e8ff1f54a 100644 --- a/src/Gui/CalendarItemDelegates.cpp +++ b/src/Gui/CalendarItemDelegates.cpp @@ -1756,7 +1756,10 @@ toolTipDayEntry(const QPoint &pos, QAbstractItemView *view, const CalendarDay &d status = QObject::tr("planned"); } if (calEntry.dirty) { - status += ", " + QObject::tr("modified"); + status += " • " + QObject::tr("modified"); + } + if (calEntry.isExcludedFromSummary) { + status += " • " + QObject::tr("not in summary"); } QString tooltip = QString("
").arg(4 * dpiXFactor);