Modify phases and events in calendar (#4753)

* Phases and events can be created, edited, deleted in the calendar (all
  views)
* LTMSidebar reacts to modified events
* Phase and Event dialogs show the associated season
* Menu entries are only active within the current season
This commit is contained in:
Joachim Kohlhammer
2025-12-14 13:36:52 +01:00
committed by GitHub
parent 4488b6e4f4
commit f17257885b
9 changed files with 512 additions and 83 deletions

View File

@@ -786,7 +786,7 @@ AgendaWindow::editEventEntry
SeasonEvent *seasonEvent = nullptr;
for (Season &s : context->athlete->seasons->seasons) {
for (SeasonEvent &event : s.events) {
// FIXME: Ugly comparison required becuase SeasonEvent::id is not populated
// FIXME: Ugly comparison required because SeasonEvent::id is not populated
if ( event.name == entry.primary
&& ( (event.priority == 0 && entry.secondary == tr("Uncategorized"))
|| (event.priority == 1 && entry.secondary == tr("Category A"))

View File

@@ -30,6 +30,7 @@
#include "RepeatScheduleWizard.h"
#include "WorkoutFilter.h"
#include "IconManager.h"
#include "SeasonDialogs.h"
#define HLO "<h4>"
#define HLC "</h4>"
@@ -153,6 +154,12 @@ CalendarWindow::CalendarWindow(Context *context)
connect(calendar, &Calendar::dayChanged, this, &CalendarWindow::updateActivities);
connect(calendar, &Calendar::monthChanged, this, &CalendarWindow::updateActivities);
connect(calendar, &Calendar::viewChanged, this, &CalendarWindow::updateActivities);
connect(calendar, &Calendar::addEvent, this, &CalendarWindow::addEvent);
connect(calendar, &Calendar::editEvent, this, &CalendarWindow::editEvent);
connect(calendar, &Calendar::delEvent, this, &CalendarWindow::delEvent);
connect(calendar, &Calendar::addPhase, this, &CalendarWindow::addPhase);
connect(calendar, &Calendar::editPhase, this, &CalendarWindow::editPhase);
connect(calendar, &Calendar::delPhase, this, &CalendarWindow::delPhase);
QTimer::singleShot(0, this, [this]() {
configChanged(CONFIG_APPEARANCE);
@@ -811,7 +818,11 @@ CalendarWindow::getPhasesEvents
entry.iconFile = ":images/breeze/task-process-0.svg";
}
entry.color = GColor(CCALEVENT);
entry.reference = event.id;
if (event.id.isEmpty()) {
entry.reference = QString("0x%1").arg(reinterpret_cast<quintptr>(&event), 0, 16);
} else {
entry.reference = event.id;
}
entry.start = QTime(0, 0, 0);
entry.durationSecs = 0;
entry.type = ENTRY_TYPE_EVENT;
@@ -897,10 +908,10 @@ CalendarWindow::updateSeason
{
if (season == nullptr) {
DateRange dr(QDate(), QDate(), "");
calendar->activateDateRange(dr, allowKeepMonth);
calendar->activateDateRange(dr, allowKeepMonth, false);
} else {
DateRange dr(DateRange(season->getStart(), season->getEnd(), season->getName()));
calendar->activateDateRange(dr, allowKeepMonth);
calendar->activateDateRange(dr, allowKeepMonth, season->canHavePhasesOrEvents());
}
}
@@ -938,3 +949,209 @@ CalendarWindow::movePlannedActivity
}
return ret;
}
void
CalendarWindow::addEvent
(const QDate &date)
{
Season const *currentSeason = context->currentSeason();
if (currentSeason == nullptr) {
return;
}
if (! currentSeason->canHavePhasesOrEvents()) {
return;
}
Season *season = nullptr;
for (Season &s : context->athlete->seasons->seasons) {
if (&s == currentSeason) {
season = &s;
break;
}
}
if (season == nullptr) {
return;
}
SeasonEvent myevent("", date);
EditSeasonEventDialog dialog(context, &myevent, *season);
if (dialog.exec()) {
season->events.append(myevent);
context->athlete->seasons->writeSeasons();
}
}
void
CalendarWindow::editEvent
(const CalendarEntry &entry)
{
if (entry.type != ENTRY_TYPE_EVENT) {
return;
}
Season *season = nullptr;
SeasonEvent *seasonEvent = nullptr;
for (Season &s : context->athlete->seasons->seasons) {
for (SeasonEvent &event : s.events) {
QString evId = event.id;
if (evId.isEmpty()) {
evId = QString("0x%1").arg(reinterpret_cast<quintptr>(&event), 0, 16);
}
if (entry.reference == evId) {
season = &s;
seasonEvent = &event;
break;
}
}
if (seasonEvent != nullptr) {
break;
}
}
if (seasonEvent == nullptr) {
return;
}
EditSeasonEventDialog dialog(context, seasonEvent, *season);
if (dialog.exec()) {
context->athlete->seasons->writeSeasons();
}
}
void
CalendarWindow::delEvent
(const CalendarEntry &entry)
{
if (entry.type != ENTRY_TYPE_EVENT) {
return;
}
bool done = false;
for (Season &s : context->athlete->seasons->seasons) {
int idx = 0;
for (SeasonEvent &event : s.events) {
QString evId = event.id;
if (evId.isEmpty()) {
evId = QString("0x%1").arg(reinterpret_cast<quintptr>(&event), 0, 16);
}
if (entry.reference == evId) {
s.events.removeAt(idx);
context->athlete->seasons->writeSeasons();
done = true;
break;
}
++idx;
}
if (done) {
break;
}
}
}
void
CalendarWindow::addPhase
(const QDate &date)
{
Season const *currentSeason = context->currentSeason();
if (currentSeason == nullptr) {
return;
}
if (! currentSeason->canHavePhasesOrEvents()) {
return;
}
Season *phaseSeason = nullptr;
for (Season &s : context->athlete->seasons->seasons) {
if (s.id() == currentSeason->id()) {
phaseSeason = &s;
break;
}
}
if (phaseSeason == nullptr) {
return;
}
QDate seasonStart = phaseSeason->getStart();
QDate seasonEnd = phaseSeason->getEnd();
if (seasonStart == seasonEnd) {
return;
}
qint64 daysBefore = std::min(static_cast<qint64>(6), seasonStart.daysTo(date));
qint64 daysAfter = std::min(static_cast<qint64>(6), date.daysTo(seasonEnd));
QDate start(date);
QDate end(date);
if (daysAfter > 0) {
end = end.addDays(daysAfter);
} else {
start = start.addDays(-daysBefore);
}
Phase myphase("", start, end);
EditPhaseDialog dialog(context, &myphase, *phaseSeason);
if (dialog.exec()) {
phaseSeason->phases.append(myphase);
context->athlete->seasons->writeSeasons();
}
}
void
CalendarWindow::editPhase
(const CalendarEntry &entry)
{
if (entry.type != ENTRY_TYPE_PHASE) {
return;
}
Season const *currentSeason = context->currentSeason();
if (currentSeason == nullptr) {
return;
}
Season *phaseSeason = nullptr;
for (Season &s : context->athlete->seasons->seasons) {
if (s.id() == currentSeason->id()) {
phaseSeason = &s;
break;
}
}
if (phaseSeason == nullptr) {
return;
}
for (Phase &editPhase : phaseSeason->phases) {
if (entry.reference == editPhase.id().toString()) {
EditPhaseDialog dialog(context, &editPhase, *phaseSeason);
if (dialog.exec()) {
context->athlete->seasons->writeSeasons();
}
break;
}
}
}
void
CalendarWindow::delPhase
(const CalendarEntry &entry)
{
if (entry.type != ENTRY_TYPE_PHASE) {
return;
}
Season const *currentSeason = context->currentSeason();
if (currentSeason == nullptr) {
return;
}
Season *phaseSeason = nullptr;
for (Season &s : context->athlete->seasons->seasons) {
if (s.id() == currentSeason->id()) {
phaseSeason = &s;
break;
}
}
if (phaseSeason == nullptr) {
return;
}
int idx = 0;
for (Phase &editPhase : phaseSeason->phases) {
if (entry.reference == editPhase.id().toString()) {
phaseSeason->phases.removeAt(idx);
context->athlete->seasons->writeSeasons();
break;
}
++idx;
}
}

View File

@@ -123,6 +123,12 @@ class CalendarWindow : public GcChartWindow
void updateActivitiesIfInRange(RideItem *rideItem);
void updateSeason(Season const *season, bool allowKeepMonth = false);
bool movePlannedActivity(RideItem *rideItem, const QDate &destDay, const QTime &destTime = QTime());
void addEvent(const QDate &date);
void editEvent(const CalendarEntry &entry);
void delEvent(const CalendarEntry &entry);
void addPhase(const QDate &date);
void editPhase(const CalendarEntry &entry);
void delPhase(const CalendarEntry &entry);
};
#endif

View File

@@ -502,6 +502,17 @@ Season::hasPhaseOrEvent
}
bool
Season::canHavePhasesOrEvents
() const
{
return ( getType() == Season::season
|| getType() == Season::cycle
|| getType() == Season::adhoc)
&& isAbsolute();
}
bool Season::LessThanForStarts(const Season &a, const Season &b)
{
return a.getStart().toJulianDay() < b.getStart().toJulianDay();

View File

@@ -197,6 +197,7 @@ class Season
bool isAbsolute() const;
bool hasPhaseOrEvent() const;
bool canHavePhasesOrEvents() const;
static bool LessThanForStarts(const Season &a, const Season &b);

View File

@@ -19,6 +19,7 @@
#include "SeasonDialogs.h"
#include "Seasons.h"
#include "Athlete.h"
#include "Colors.h"
#include <QString>
#include <QFormLayout>
@@ -152,9 +153,9 @@ EditSeasonDialog::EditSeasonDialog
endValueStack->addWidget(new QLabel(tr("Up to current day and month")));
statusLabel = new QLabel();
statusLabel->setWordWrap(true);
statusLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
warningLabel = new QLabel();
warningLabel->setWordWrap(true);
warningLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
seedEdit = new QDoubleSpinBox();
seedEdit->setDecimals(0);
@@ -174,7 +175,7 @@ EditSeasonDialog::EditSeasonDialog
applyButton = buttonBox->addButton(tr("&OK"), QDialogButtonBox::AcceptRole);
QPushButton *cancelButton = buttonBox->addButton(tr("&Cancel"), QDialogButtonBox::RejectRole);
QFormLayout *formLayout = new QFormLayout(this);
QFormLayout *formLayout = newQFormLayout(this);
formLayout->addRow(tr("Name"), nameEdit);
formLayout->addRow(tr("Type"), typeCombo);
formLayout->addRow(startCombo, startValueStack);
@@ -475,6 +476,9 @@ EditSeasonEventDialog::EditSeasonEventDialog(Context *context, SeasonEvent *even
{
setWindowTitle(tr("Edit Event"));
QLabel *seasonLabel = new QLabel();
seasonLabel->setText(season.getName());
nameEdit = new QLineEdit(this);
nameEdit->setText(event->name);
@@ -494,7 +498,8 @@ EditSeasonEventDialog::EditSeasonEventDialog(Context *context, SeasonEvent *even
applyButton = buttonBox->addButton(tr("&OK"), QDialogButtonBox::AcceptRole);
QPushButton *cancelButton = buttonBox->addButton(tr("&Cancel"), QDialogButtonBox::RejectRole);
QFormLayout *formLayout = new QFormLayout(this);
QFormLayout *formLayout = newQFormLayout(this);
formLayout->addRow(tr("Season"), seasonLabel);
formLayout->addRow(tr("Name"), nameEdit);
formLayout->addRow(tr("Date"), dateEdit);
formLayout->addRow(tr("Priority"), priorityEdit);
@@ -542,6 +547,9 @@ EditPhaseDialog::EditPhaseDialog(Context *context, Phase *phase, Season &season)
{
setWindowTitle(tr("Edit Date Range"));
QLabel *seasonLabel = new QLabel();
seasonLabel->setText(season.getName());
nameEdit = new QLineEdit();
nameEdit->setText(phase->getName());
@@ -583,7 +591,8 @@ EditPhaseDialog::EditPhaseDialog(Context *context, Phase *phase, Season &season)
applyButton = buttonBox->addButton(tr("&OK"), QDialogButtonBox::AcceptRole);
QPushButton *cancelButton = buttonBox->addButton(tr("&Cancel"), QDialogButtonBox::RejectRole);
QFormLayout *formLayout = new QFormLayout(this);
QFormLayout *formLayout = newQFormLayout(this);
formLayout->addRow(tr("Season"), seasonLabel);
formLayout->addRow(tr("Name"), nameEdit);
formLayout->addRow(tr("Type"), typeEdit);
formLayout->addRow(tr("From"), fromEdit);

View File

@@ -356,11 +356,12 @@ CalendarDayTable::fillEntries
void
CalendarDayTable::limitDateRange
(const DateRange &dr)
(const DateRange &dr, bool canHavePhasesOrEvents)
{
if (dr.from.isValid() && dr.to.isValid() && dr.from > dr.to) {
return;
}
this->canHavePhasesOrEvents = canHavePhasesOrEvents;
this->dr = dr;
if (! dr.pass(selectedDate())) {
if (dr.pass(lastVisibleDay())) {
@@ -687,73 +688,33 @@ CalendarDayTable::showContextMenu
(const QPoint &pos)
{
QModelIndex index = indexAt(pos);
if (! index.isValid()) {
return;
}
int row = index.row();
int column = index.column();
if ( ! index.isValid()
|| row != 1
|| column == 0) {
if (pressedIndex.isValid() && (row != 1 || column == 0)) {
QTableWidgetItem *item = this->item(pressedIndex.row(), pressedIndex.column());
if (item != nullptr) {
item->setData(CalendarDetailedDayDelegate::PressedEntryRole, QVariant());
}
}
QMenu *contextMenu = nullptr;
if (row == 0 && column > 0) {
contextMenu = makeHeaderMenu(index, pos);
} else if (row == 1 && column > 0) {
contextMenu = makeActivityMenu(index, pos);
}
if (contextMenu != nullptr) {
contextMenu->exec(viewport()->mapToGlobal(pos));
delete contextMenu;
if (pressedIndex.isValid()) {
QTableWidgetItem *item = this->item(pressedIndex.row(), pressedIndex.column());
if (item != nullptr) {
item->setData(CalendarDetailedDayDelegate::PressedEntryRole, QVariant());
}
}
return;
}
ColumnDelegatingItemDelegate *delegatingDelegate = static_cast<ColumnDelegatingItemDelegate*>(itemDelegateForRow(row));
CalendarDetailedDayDelegate *delegate = static_cast<CalendarDetailedDayDelegate*>(delegatingDelegate->getDelegate(column));
int entryIdx = delegate->entryTester.hitTest(index, pos);
CalendarDay day = index.data(CalendarDetailedDayDelegate::DayRole).value<CalendarDay>();
QMenu contextMenu(this);
if (entryIdx >= 0) {
CalendarEntry calEntry = day.entries[entryIdx];
switch (calEntry.type) {
case ENTRY_TYPE_ACTIVITY:
contextMenu.addAction(tr("View activity..."), this, [=]() {
emit viewActivity(calEntry);
});
contextMenu.addAction(tr("Delete activity"), this, [=]() {
emit delActivity(calEntry);
});
break;
case ENTRY_TYPE_PLANNED_ACTIVITY:
if (calEntry.hasTrainMode) {
contextMenu.addAction(tr("Show in train node..."), this, [=]() {
emit showInTrainMode(calEntry);
});
}
contextMenu.addAction(tr("View planned activity..."), this, [=]() {
emit viewActivity(calEntry);
});
contextMenu.addAction(tr("Delete planned activity"), this, [=]() {
emit delActivity(calEntry);
});
break;
case ENTRY_TYPE_OTHER:
default:
break;
}
} else {
QTime time = timeScaleData.timeFromYInTable(pos.y(), visualRect(index));
if ( day.date < QDate::currentDate()
|| ( day.date == QDate::currentDate()
&& time < QTime::currentTime())) {
contextMenu.addAction(tr("Add activity..."), this, [=]() {
emit addActivity(false, day.date, time);
});
} else {
contextMenu.addAction(tr("Add planned activity..."), this, [=]() {
emit addActivity(true, day.date, time);
});
}
}
contextMenu.exec(viewport()->mapToGlobal(pos));
if (pressedIndex.isValid()) {
QTableWidgetItem *item = this->item(pressedIndex.row(), pressedIndex.column());
if (item != nullptr) {
item->setData(CalendarDetailedDayDelegate::PressedEntryRole, QVariant());
}
}
}
@@ -768,6 +729,110 @@ CalendarDayTable::setDropIndicator
}
QMenu*
CalendarDayTable::makeHeaderMenu
(const QModelIndex &index, const QPoint &pos)
{
CalendarHeadlineDelegate *delegate = static_cast<CalendarHeadlineDelegate*>(itemDelegateForRow(index.row()));
int entryIdx = delegate->headlineTester.hitTest(index, pos);
CalendarDay day = index.data(CalendarHeadlineDelegate::DayRole).value<CalendarDay>();
QMenu *contextMenu = new QMenu(this);
if (entryIdx >= 0) {
const CalendarEntry &entry = day.headlineEntries[entryIdx];
switch (entry.type) {
case ENTRY_TYPE_EVENT: {
QAction *editEventAction = contextMenu->addAction(tr("Edit event..."), this, [=]() { emit editEvent(entry); });
QAction *delEventAction = contextMenu->addAction(tr("Delete event"), this, [=]() { emit delEvent(entry); });
if (! isInDateRange(day.date) || ! canHavePhasesOrEvents) {
editEventAction->setEnabled(false);
delEventAction->setEnabled(false);
}
break;
}
case ENTRY_TYPE_PHASE: {
QAction *editPhaseAction = contextMenu->addAction(tr("Edit phase..."), this, [=]() { emit editPhase(entry); });
QAction *delPhaseAction = contextMenu->addAction(tr("Delete phase..."), this, [=]() { emit delPhase(entry); });
if (! isInDateRange(day.date) || ! canHavePhasesOrEvents) {
editPhaseAction->setEnabled(false);
delPhaseAction->setEnabled(false);
}
break;
}
default:
break;
}
} else {
QAction *addPhaseAction = contextMenu->addAction(tr("Add phase..."), this, [=]() { emit addPhase(day.date); });
QAction *addEventAction = contextMenu->addAction(tr("Add event..."), this, [=]() { emit addEvent(day.date); });
if (! isInDateRange(day.date) || ! canHavePhasesOrEvents) {
addPhaseAction->setEnabled(false);
addEventAction->setEnabled(false);
}
}
return contextMenu;
}
QMenu*
CalendarDayTable::makeActivityMenu
(const QModelIndex &index, const QPoint &pos)
{
ColumnDelegatingItemDelegate *delegatingDelegate = static_cast<ColumnDelegatingItemDelegate*>(itemDelegateForRow(index.row()));
CalendarDetailedDayDelegate *delegate = static_cast<CalendarDetailedDayDelegate*>(delegatingDelegate->getDelegate(index.column()));
int entryIdx = delegate->entryTester.hitTest(index, pos);
CalendarDay day = index.data(CalendarDetailedDayDelegate::DayRole).value<CalendarDay>();
QMenu *contextMenu = new QMenu(this);
if (entryIdx >= 0) {
CalendarEntry calEntry = day.entries[entryIdx];
switch (calEntry.type) {
case ENTRY_TYPE_ACTIVITY:
contextMenu->addAction(tr("View activity..."), this, [=]() {
emit viewActivity(calEntry);
});
contextMenu->addAction(tr("Delete activity"), this, [=]() {
emit delActivity(calEntry);
});
break;
case ENTRY_TYPE_PLANNED_ACTIVITY:
if (calEntry.hasTrainMode) {
contextMenu->addAction(tr("Show in train node..."), this, [=]() {
emit showInTrainMode(calEntry);
});
}
contextMenu->addAction(tr("View planned activity..."), this, [=]() {
emit viewActivity(calEntry);
});
contextMenu->addAction(tr("Delete planned activity"), this, [=]() {
emit delActivity(calEntry);
});
break;
default:
break;
}
} else {
QTime time = timeScaleData.timeFromYInTable(pos.y(), visualRect(index));
if ( day.date < QDate::currentDate()
|| ( day.date == QDate::currentDate()
&& time < QTime::currentTime())) {
contextMenu->addAction(tr("Add activity..."), this, [=]() {
emit addActivity(false, day.date, time);
});
} else {
contextMenu->addAction(tr("Add planned activity..."), this, [=]() {
emit addActivity(true, day.date, time);
});
}
QAction *addPhaseAction = contextMenu->addAction(tr("Add phase..."), this, [=]() { emit addPhase(day.date); });
QAction *addEventAction = contextMenu->addAction(tr("Add event..."), this, [=]() { emit addEvent(day.date); });
if (! isInDateRange(day.date) || ! canHavePhasesOrEvents) {
addPhaseAction->setEnabled(false);
addEventAction->setEnabled(false);
}
}
return contextMenu;
}
//////////////////////////////////////////////////////////////////////////////
// CalendarMonthTable
@@ -958,12 +1023,13 @@ CalendarMonthTable::selectedDate
void
CalendarMonthTable::limitDateRange
(const DateRange &dr, bool allowKeepMonth)
(const DateRange &dr, bool allowKeepMonth, bool canHavePhasesOrEvents)
{
if (dr.from.isValid() && dr.to.isValid() && dr.from > dr.to) {
return;
}
this->dr = dr;
this->canHavePhasesOrEvents = canHavePhasesOrEvents;
if (currentItem() != nullptr && isInDateRange(currentItem()->data(DateRole).toDate())) {
setMonth(currentItem()->data(DateRole).toDate());
} else if (isInDateRange(QDate::currentDate())) {
@@ -1296,6 +1362,7 @@ CalendarMonthTable::showContextMenu
return;
}
int entryIdx = delegate->entryTester.hitTest(index, pos);
int headlineEntryIdx = delegate->headlineTester.hitTest(index, pos);
CalendarDay day = index.data(CalendarCompactDayDelegate::DayRole).value<CalendarDay>();
QMenu contextMenu(this);
@@ -1323,7 +1390,30 @@ CalendarMonthTable::showContextMenu
emit delActivity(calEntry);
});
break;
case ENTRY_TYPE_OTHER:
default:
break;
}
} else if (headlineEntryIdx >= 0) {
const CalendarEntry &entry = day.headlineEntries[headlineEntryIdx];
switch (entry.type) {
case ENTRY_TYPE_EVENT: {
QAction *editEventAction = contextMenu.addAction(tr("Edit event..."), this, [=]() { emit editEvent(entry); });
QAction *delEventAction = contextMenu.addAction(tr("Delete event"), this, [=]() { emit delEvent(entry); });
if (! isInDateRange(day.date) || ! canHavePhasesOrEvents) {
editEventAction->setEnabled(false);
delEventAction->setEnabled(false);
}
break;
}
case ENTRY_TYPE_PHASE: {
QAction *editPhaseAction = contextMenu.addAction(tr("Edit phase..."), this, [=]() { emit editPhase(entry); });
QAction *delPhaseAction = contextMenu.addAction(tr("Delete phase"), this, [=]() { emit delPhase(entry); });
if (! isInDateRange(day.date) || ! canHavePhasesOrEvents) {
editPhaseAction->setEnabled(false);
delPhaseAction->setEnabled(false);
}
break;
}
default:
break;
}
@@ -1345,6 +1435,15 @@ CalendarMonthTable::showContextMenu
}
emit addActivity(true, day.date, time);
});
}
QAction *addPhaseAction = contextMenu.addAction(tr("Add phase..."), this, [=]() { emit addPhase(day.date); });
QAction *addEventAction = contextMenu.addAction(tr("Add event..."), this, [=]() { emit addEvent(day.date); });
if (! isInDateRange(day.date) || ! canHavePhasesOrEvents) {
addPhaseAction->setEnabled(false);
addEventAction->setEnabled(false);
}
if (day.date >= QDate::currentDate()) {
contextMenu.addSeparator();
contextMenu.addAction(tr("Repeat schedule..."), this, [=]() {
emit repeatSchedule(day.date);
});
@@ -1415,6 +1514,12 @@ CalendarDayView::CalendarDayView
connect(dayTable, &CalendarDayTable::showInTrainMode, this, &CalendarDayView::showInTrainMode);
connect(dayTable, &CalendarDayTable::delActivity, this, &CalendarDayView::delActivity);
connect(dayTable, &CalendarDayTable::entryMoved, this, &CalendarDayView::entryMoved);
connect(dayTable, &CalendarDayTable::addPhase, this, &CalendarDayView::addPhase);
connect(dayTable, &CalendarDayTable::editPhase, this, &CalendarDayView::editPhase);
connect(dayTable, &CalendarDayTable::delPhase, this, &CalendarDayView::delPhase);
connect(dayTable, &CalendarDayTable::addEvent, this, &CalendarDayView::addEvent);
connect(dayTable, &CalendarDayTable::editEvent, this, &CalendarDayView::editEvent);
connect(dayTable, &CalendarDayTable::delEvent, this, &CalendarDayView::delEvent);
}
@@ -1470,9 +1575,10 @@ CalendarDayView::fillEntries
void
CalendarDayView::limitDateRange
(const DateRange &dr)
(const DateRange &dr, bool canHavePhasesOrEvents)
{
dayDateSelector->limitDateRange(dr);
dayTable->limitDateRange(dr, canHavePhasesOrEvents);
}
@@ -1698,6 +1804,12 @@ CalendarWeekView::CalendarWeekView
connect(weekTable, &CalendarDayTable::showInTrainMode, this, &CalendarWeekView::showInTrainMode);
connect(weekTable, &CalendarDayTable::delActivity, this, &CalendarWeekView::delActivity);
connect(weekTable, &CalendarDayTable::entryMoved, this, &CalendarWeekView::entryMoved);
connect(weekTable, &CalendarDayTable::addPhase, this, &CalendarWeekView::addPhase);
connect(weekTable, &CalendarDayTable::editPhase, this, &CalendarWeekView::editPhase);
connect(weekTable, &CalendarDayTable::delPhase, this, &CalendarWeekView::delPhase);
connect(weekTable, &CalendarDayTable::addEvent, this, &CalendarWeekView::addEvent);
connect(weekTable, &CalendarDayTable::editEvent, this, &CalendarWeekView::editEvent);
connect(weekTable, &CalendarDayTable::delEvent, this, &CalendarWeekView::delEvent);
setDay(date);
}
@@ -1753,9 +1865,9 @@ CalendarWeekView::fillEntries
void
CalendarWeekView::limitDateRange
(const DateRange &dr)
(const DateRange &dr, bool canHavePhasesOrEvents)
{
weekTable->limitDateRange(dr);
weekTable->limitDateRange(dr, canHavePhasesOrEvents);
}
@@ -1880,6 +1992,12 @@ Calendar::Calendar
connect(dayView, &CalendarDayView::showInTrainMode, this, &Calendar::showInTrainMode);
connect(dayView, &CalendarDayView::delActivity, this, &Calendar::delActivity);
connect(dayView, &CalendarDayView::entryMoved, this, &Calendar::moveActivity);
connect(dayView, &CalendarDayView::addPhase, this, &Calendar::addPhase);
connect(dayView, &CalendarDayView::editPhase, this, &Calendar::editPhase);
connect(dayView, &CalendarDayView::delPhase, this, &Calendar::delPhase);
connect(dayView, &CalendarDayView::addEvent, this, &Calendar::addEvent);
connect(dayView, &CalendarDayView::editEvent, this, &Calendar::editEvent);
connect(dayView, &CalendarDayView::delEvent, this, &Calendar::delEvent);
connect(weekView, &CalendarWeekView::dayChanged, [=](const QDate &date) {
if (currentView() == CalendarView::Week) {
@@ -1893,6 +2011,12 @@ Calendar::Calendar
connect(weekView, &CalendarWeekView::showInTrainMode, this, &Calendar::showInTrainMode);
connect(weekView, &CalendarWeekView::delActivity, this, &Calendar::delActivity);
connect(weekView, &CalendarWeekView::entryMoved, this, &Calendar::moveActivity);
connect(weekView, &CalendarWeekView::addPhase, this, &Calendar::addPhase);
connect(weekView, &CalendarWeekView::editPhase, this, &Calendar::editPhase);
connect(weekView, &CalendarWeekView::delPhase, this, &Calendar::delPhase);
connect(weekView, &CalendarWeekView::addEvent, this, &Calendar::addEvent);
connect(weekView, &CalendarWeekView::editEvent, this, &Calendar::editEvent);
connect(weekView, &CalendarWeekView::delEvent, this, &Calendar::delEvent);
connect(monthView, &CalendarMonthTable::entryDblClicked, [=](const CalendarDay &day, int entryIdx) {
viewActivity(day.entries[entryIdx]);
@@ -1912,6 +2036,12 @@ Calendar::Calendar
connect(monthView, &CalendarMonthTable::repeatSchedule, this, &Calendar::repeatSchedule);
connect(monthView, &CalendarMonthTable::delActivity, this, &Calendar::delActivity);
connect(monthView, &CalendarMonthTable::entryMoved, this, &Calendar::moveActivity);
connect(monthView, &CalendarMonthTable::addPhase, this, &Calendar::addPhase);
connect(monthView, &CalendarMonthTable::editPhase, this, &Calendar::editPhase);
connect(monthView, &CalendarMonthTable::delPhase, this, &Calendar::delPhase);
connect(monthView, &CalendarMonthTable::addEvent, this, &Calendar::addEvent);
connect(monthView, &CalendarMonthTable::editEvent, this, &Calendar::editEvent);
connect(monthView, &CalendarMonthTable::delEvent, this, &Calendar::delEvent);
connect(monthView, &CalendarMonthTable::insertRestday, this, &Calendar::insertRestday);
connect(monthView, &CalendarMonthTable::delRestday, this, &Calendar::delRestday);
connect(monthView, &CalendarMonthTable::monthChanged, [=](const QDate &month, const QDate &firstVisible, const QDate &lastVisible) {
@@ -2124,13 +2254,13 @@ Calendar::isInDateRange
void
Calendar::activateDateRange
(const DateRange &dr, bool allowKeepMonth)
(const DateRange &dr, bool allowKeepMonth, bool canHavePhasesOrEvents)
{
QDate currentDate = selectedDate();
dateRange = dr;
monthView->limitDateRange(dr, allowKeepMonth);
weekView->limitDateRange(dr);
dayView->limitDateRange(dr);
monthView->limitDateRange(dr, allowKeepMonth, canHavePhasesOrEvents);
weekView->limitDateRange(dr, canHavePhasesOrEvents);
dayView->limitDateRange(dr, canHavePhasesOrEvents);
if (currentView() == CalendarView::Day || currentView() == CalendarView::Week) {
setDate(currentDate, false);
} else if (currentView() == CalendarView::Month) {

View File

@@ -84,7 +84,7 @@ public:
QDate selectedDate() const;
bool isInDateRange(const QDate &date) const;
void fillEntries(const QHash<QDate, QList<CalendarEntry>> &activityEntries, const QList<CalendarSummary> &summaries, const QHash<QDate, QList<CalendarEntry>> &headlineEntries);
void limitDateRange(const DateRange &dr);
void limitDateRange(const DateRange &dr, bool canHavePhasesOrEvents = false);
void setFirstDayOfWeek(Qt::DayOfWeek firstDayOfWeek);
void setStartHour(int hour);
void setEndHour(int hour);
@@ -100,6 +100,12 @@ signals:
void viewActivity(const CalendarEntry &activity);
void addActivity(bool plan, const QDate &day, const QTime &time);
void delActivity(const CalendarEntry &activity);
void addEvent(const QDate &date);
void editEvent(const CalendarEntry &entry);
void delEvent(const CalendarEntry &entry);
void addPhase(const QDate &date);
void editPhase(const CalendarEntry &entry);
void delPhase(const CalendarEntry &entry);
protected:
void changeEvent(QEvent *event) override;
@@ -122,6 +128,7 @@ private:
Qt::DayOfWeek firstDayOfWeek = Qt::Monday;
QDate date;
DateRange dr;
bool canHavePhasesOrEvents = false;
CalendarDayTableType type;
int defaultStartHour = 8;
int defaultEndHour = 21;
@@ -133,6 +140,8 @@ private:
TimeScaleData timeScaleData;
void setDropIndicator(int y, BlockIndicator block);
QMenu *makeHeaderMenu(const QModelIndex &index, const QPoint &pos);
QMenu *makeActivityMenu(const QModelIndex &index, const QPoint &pos);
};
@@ -155,7 +164,7 @@ public:
QDate firstVisibleDay() const;
QDate lastVisibleDay() const;
QDate selectedDate() const;
void limitDateRange(const DateRange &dr, bool allowKeepMonth = false);
void limitDateRange(const DateRange &dr, bool allowKeepMonth = false, bool canHavePhasesOrEvents = false);
void setFirstDayOfWeek(Qt::DayOfWeek firstDayOfWeek);
signals:
@@ -180,6 +189,12 @@ signals:
void repeatSchedule(const QDate &day);
void insertRestday(const QDate &day);
void delRestday(const QDate &day);
void addEvent(const QDate &date);
void editEvent(const CalendarEntry &entry);
void delEvent(const CalendarEntry &entry);
void addPhase(const QDate &date);
void editPhase(const CalendarEntry &entry);
void delPhase(const CalendarEntry &entry);
protected:
void changeEvent(QEvent *event) override;
@@ -205,6 +220,7 @@ private:
QDate endDate; // last visible date
QDate currentDate; // currently selected date
DateRange dr;
bool canHavePhasesOrEvents = false;
QTimer dragTimer;
QPoint pressedPos;
@@ -232,7 +248,7 @@ public:
void setEndHour(int hour);
void setSummaryVisible(bool visible);
void fillEntries(const QHash<QDate, QList<CalendarEntry>> &activityEntries, const QList<CalendarSummary> &summaries, const QHash<QDate, QList<CalendarEntry>> &headlineEntries);
void limitDateRange(const DateRange &dr);
void limitDateRange(const DateRange &dr, bool canHavePhasesOrEvents = false);
QDate firstVisibleDay() const;
QDate lastVisibleDay() const;
QDate selectedDate() const;
@@ -245,6 +261,12 @@ signals:
void delActivity(const CalendarEntry &activity);
void entryMoved(const CalendarEntry &activity, const QDate &srcDay, const QDate &destDay, const QTime &destTime);
void dayChanged(const QDate &date);
void addEvent(const QDate &date);
void editEvent(const CalendarEntry &entry);
void delEvent(const CalendarEntry &entry);
void addPhase(const QDate &date);
void editPhase(const CalendarEntry &entry);
void delPhase(const CalendarEntry &entry);
private:
Measures * const athleteMeasures;
@@ -269,7 +291,7 @@ public:
void setEndHour(int hour);
void setSummaryVisible(bool visible);
void fillEntries(const QHash<QDate, QList<CalendarEntry>> &activityEntries, const QList<CalendarSummary> &summaries, const QHash<QDate, QList<CalendarEntry>> &headlineEntries);
void limitDateRange(const DateRange &dr);
void limitDateRange(const DateRange &dr, bool canHavePhasesOrEvents = false);
QDate firstVisibleDay() const;
QDate firstVisibleDay(const QDate &date) const;
QDate lastVisibleDay() const;
@@ -283,6 +305,12 @@ signals:
void delActivity(const CalendarEntry &activity);
void entryMoved(const CalendarEntry &activity, const QDate &srcDay, const QDate &destDay, const QTime &destTime);
void dayChanged(const QDate &date);
void addEvent(const QDate &date);
void editEvent(const CalendarEntry &entry);
void delEvent(const CalendarEntry &entry);
void addPhase(const QDate &date);
void editPhase(const CalendarEntry &entry);
void delPhase(const CalendarEntry &entry);
private:
CalendarDayTable *weekTable;
@@ -311,7 +339,7 @@ public:
public slots:
void setView(CalendarView view);
void activateDateRange(const DateRange &dr, bool allowKeepMonth = false);
void activateDateRange(const DateRange &dr, bool allowKeepMonth = false, bool canHavePhasesOrEvents = false);
void setFirstDayOfWeek(Qt::DayOfWeek firstDayOfWeek);
void setStartHour(int hour);
void setEndHour(int hour);
@@ -337,6 +365,12 @@ signals:
void moveActivity(const CalendarEntry &activity, const QDate &srcDay, const QDate &destDay, const QTime &destTime);
void insertRestday(const QDate &day);
void delRestday(const QDate &day);
void addEvent(const QDate &date);
void editEvent(const CalendarEntry &entry);
void delEvent(const CalendarEntry &entry);
void addPhase(const QDate &date);
void editPhase(const CalendarEntry &entry);
void delPhase(const CalendarEntry &entry);
private:
QToolBar *toolbar;

View File

@@ -460,6 +460,11 @@ LTMSidebar::resetSeasons()
// delete it and its children
dateRangeTree->clear();
// clear events - we need to add for currently selected season
for (int i = allEvents->childCount(); i > 0; i--) {
delete allEvents->takeChild(0);
}
// by default choose last 3 months not first one, since the first one is all dates
// and that means aggregating all data when first starting...
QString id = appsettings->cvalue(context->athlete->cyclist, GC_LTM_LAST_DATE_RANGE, "{00000000-0000-0000-0000-000000000012}").toString();
@@ -486,6 +491,22 @@ LTMSidebar::resetSeasons()
addPhase->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
addPhase->setText(0, phase.getName());
}
// Events
if (context->currentSeason() != nullptr && context->currentSeason()->id() == season.id()) {
// add this seasons events
for (int j = 0; j < season.events.count(); j++) {
SeasonEvent event = season.events.at(j);
QTreeWidgetItem *add = new QTreeWidgetItem(allEvents);
add->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
add->setText(0, event.name);
add->setText(1, event.date.toString("MMM d, yyyy"));
add->setText(2, SeasonEvent::priorityList().at(event.priority));
}
// make sure they fit
eventTree->header()->resizeSections(QHeaderView::ResizeToContents);
}
}
active = false;