mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
Crashes related to seasons (#4834)
* Added safeguards * Blocking signals during construction * DateRange no longer inherits from QObject since signal 'changed' was nowhere connected in the codebase * Safeguards when dragging seasons; preventing phases from being dragged * Preventing the use of dangling pointers when creating a new season [publish binaries]
This commit is contained in:
committed by
GitHub
parent
1d37396334
commit
d813585927
@@ -519,6 +519,14 @@ AgendaWindow::getActivities
|
||||
(const QDate &firstDay, const QDate &today, const QDate &lastDay) const
|
||||
{
|
||||
QHash<QDate, QList<AgendaEntry>> activities;
|
||||
if (! context || ! context->athlete || ! context->athlete->rideCache) {
|
||||
return activities;
|
||||
}
|
||||
const QList<RideItem*> rides = context->athlete->rideCache->rides();
|
||||
if (rides.isEmpty()) {
|
||||
return activities;
|
||||
}
|
||||
|
||||
const RideMetricFactory &factory = RideMetricFactory::instance();
|
||||
QList<RideMetric const *> rideMetrics;
|
||||
QStringList rideMetricNames;
|
||||
@@ -538,11 +546,15 @@ AgendaWindow::getActivities
|
||||
}
|
||||
|
||||
int showTertiaryFor = getShowTertiaryFor();
|
||||
for (RideItem *rideItem : context->athlete->rideCache->rides()) {
|
||||
for (RideItem *rideItem : rides) {
|
||||
if ( rideItem == nullptr
|
||||
|| ! rideItem->planned
|
||||
|| rideItem->dateTime.date() < firstDay
|
||||
|| rideItem->dateTime.date() > lastDay
|
||||
|| ! rideItem->dateTime.isValid()) {
|
||||
continue;
|
||||
}
|
||||
QDate rideDate = rideItem->dateTime.date();
|
||||
if ( ! rideItem->planned
|
||||
|| rideDate < firstDay
|
||||
|| rideDate > lastDay
|
||||
|| rideItem->hasLinkedActivity()) {
|
||||
continue;
|
||||
}
|
||||
@@ -586,7 +598,7 @@ AgendaWindow::getActivities
|
||||
activity.secondaryValues << tr("N/A");
|
||||
activity.secondaryLabels << "";
|
||||
}
|
||||
if (showTertiaryFor == 0 || (showTertiaryFor == 1 && rideItem->dateTime.date() == today)) {
|
||||
if (showTertiaryFor == 0 || (showTertiaryFor == 1 && rideDate == today)) {
|
||||
activity.tertiary = rideItem->getText(getTertiaryField(), "").trimmed();
|
||||
activity.tertiary = Utils::unprotect(activity.tertiary);
|
||||
}
|
||||
@@ -598,7 +610,7 @@ AgendaWindow::getActivities
|
||||
activity.start = rideItem->dateTime.time();
|
||||
activity.type = ENTRY_TYPE_PLANNED_ACTIVITY;
|
||||
activity.hasTrainMode = sport == "Bike" && ! buildWorkoutFilter(rideItem).isEmpty();
|
||||
activities[rideItem->dateTime.date()] << activity;
|
||||
activities[rideDate] << activity;
|
||||
}
|
||||
for (auto dayIt = activities.begin(); dayIt != activities.end(); ++dayIt) {
|
||||
std::sort(dayIt.value().begin(), dayIt.value().end(), [](const AgendaEntry &a, const AgendaEntry &b) {
|
||||
|
||||
@@ -948,6 +948,15 @@ CalendarWindow::getActivities
|
||||
(const QDate &firstDay, const QDate &lastDay) const
|
||||
{
|
||||
QHash<QDate, QList<CalendarEntry>> activities;
|
||||
|
||||
if (! context || ! context->athlete || ! context->athlete->rideCache) {
|
||||
return activities;
|
||||
}
|
||||
const QList<RideItem*> rides = context->athlete->rideCache->rides();
|
||||
if (rides.isEmpty()) {
|
||||
return activities;
|
||||
}
|
||||
|
||||
const RideMetricFactory &factory = RideMetricFactory::instance();
|
||||
const RideMetric *rideMetric = factory.rideMetric(getSecondaryMetric());
|
||||
QString rideMetricName;
|
||||
@@ -960,10 +969,14 @@ CalendarWindow::getActivities
|
||||
}
|
||||
}
|
||||
|
||||
for (RideItem *rideItem : context->athlete->rideCache->rides()) {
|
||||
if ( rideItem->dateTime.date() < firstDay
|
||||
|| rideItem->dateTime.date() > lastDay
|
||||
|| rideItem == nullptr) {
|
||||
for (RideItem *rideItem : rides) {
|
||||
if ( rideItem == nullptr
|
||||
|| ! rideItem->dateTime.isValid()) {
|
||||
continue;
|
||||
}
|
||||
QDate rideDate = rideItem->dateTime.date();
|
||||
if ( rideDate < firstDay
|
||||
|| rideDate > lastDay) {
|
||||
continue;
|
||||
}
|
||||
if ( (context->isfiltered && ! context->filters.contains(rideItem->fileName))
|
||||
@@ -1011,7 +1024,7 @@ CalendarWindow::getActivities
|
||||
}
|
||||
|
||||
RideItem *linkedRide = context->athlete->rideCache->getLinkedActivity(rideItem);
|
||||
if (linkedRide != nullptr) {
|
||||
if (linkedRide != nullptr && linkedRide->dateTime.isValid()) {
|
||||
activity.linkedReference = linkedRide->fileName;
|
||||
activity.linkedPrimary = getPrimary(linkedRide);
|
||||
activity.linkedStartDT = linkedRide->dateTime;
|
||||
@@ -1020,7 +1033,7 @@ CalendarWindow::getActivities
|
||||
}
|
||||
}
|
||||
|
||||
activities[rideItem->dateTime.date()] << activity;
|
||||
activities[rideDate] << activity;
|
||||
}
|
||||
for (auto dayIt = activities.begin(); dayIt != activities.end(); ++dayIt) {
|
||||
std::sort(dayIt.value().begin(), dayIt.value().end(), [](const CalendarEntry &a, const CalendarEntry &b) {
|
||||
|
||||
@@ -189,7 +189,7 @@ QDateTime convertToLocalTime(QString timestamp)
|
||||
return QDateTime::fromString(timestamp);
|
||||
}
|
||||
|
||||
DateRange::DateRange(QDate from, QDate to, QString name, QColor color) : QObject()
|
||||
DateRange::DateRange(QDate from, QDate to, QString name, QColor color)
|
||||
{
|
||||
this->from=from;
|
||||
this->to=to;
|
||||
@@ -198,7 +198,7 @@ DateRange::DateRange(QDate from, QDate to, QString name, QColor color) : QObject
|
||||
valid = from.isValid() && to.isValid();
|
||||
}
|
||||
|
||||
DateRange::DateRange(const DateRange &other) : QObject()
|
||||
DateRange::DateRange(const DateRange &other)
|
||||
{
|
||||
from=other.from;
|
||||
to=other.to;
|
||||
@@ -215,7 +215,6 @@ DateRange& DateRange::operator=(const DateRange &other)
|
||||
name=other.name;
|
||||
color=other.color;
|
||||
valid = from.isValid() && to.isValid();
|
||||
emit changed(from, to);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,8 @@ extern ShowDaysAsUnit showDaysAs(int days);
|
||||
*/
|
||||
QDateTime convertToLocalTime(QString timestamp);
|
||||
|
||||
class DateRange : QObject
|
||||
class DateRange
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DateRange(const DateRange& other);
|
||||
DateRange(QDate from = QDate(), QDate to = QDate(), QString name ="", QColor=QColor(127,127,127));
|
||||
@@ -81,14 +79,9 @@ class DateRange : QObject
|
||||
}
|
||||
bool isValid() const { return valid; }
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void changed(QDate from, QDate to);
|
||||
|
||||
protected:
|
||||
bool valid;
|
||||
};
|
||||
Q_DECLARE_METATYPE(DateRange)
|
||||
|
||||
class DateSettingsEdit : public QWidget
|
||||
{
|
||||
|
||||
@@ -2350,6 +2350,9 @@ void
|
||||
Calendar::activateDateRange
|
||||
(const DateRange &dr, bool allowKeepMonth, bool canHavePhasesOrEvents)
|
||||
{
|
||||
if (dateRange == dr) {
|
||||
return;
|
||||
}
|
||||
QDate currentDate = selectedDate();
|
||||
dateRange = dr;
|
||||
monthView->limitDateRange(dr, allowKeepMonth, canHavePhasesOrEvents);
|
||||
|
||||
@@ -400,6 +400,9 @@ LTMSidebar::dateRangeTreeWidgetSelectionChanged()
|
||||
phase = NULL;
|
||||
}
|
||||
} else if (which->type() >= Phase::phase) {
|
||||
if (which->parent() == nullptr) {
|
||||
return;
|
||||
}
|
||||
int seasonIdx = allDateRanges->indexOfChild(which->parent());
|
||||
int phaseIdx = which->parent()->indexOfChild(which);
|
||||
if (which != allDateRanges) {
|
||||
@@ -492,7 +495,7 @@ LTMSidebar::resetSeasons()
|
||||
addSeason->setExpanded(true);
|
||||
addPhase->setSelected(true);
|
||||
}
|
||||
addPhase->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
|
||||
addPhase->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
addPhase->setText(0, phase.getName());
|
||||
}
|
||||
|
||||
@@ -1091,6 +1094,14 @@ LTMSidebar::deleteFilter()
|
||||
void
|
||||
LTMSidebar::dateRangeMoved(QTreeWidgetItem*item, int oldposition, int newposition)
|
||||
{
|
||||
if ( oldposition < 0
|
||||
|| oldposition >= seasons->seasons.count()
|
||||
|| newposition < 0
|
||||
|| newposition >= seasons->seasons.count()) {
|
||||
return;
|
||||
}
|
||||
QSignalBlocker blocker(dateRangeTree);
|
||||
|
||||
// report the move in the seasons
|
||||
seasons->seasons.move(oldposition, newposition);
|
||||
|
||||
@@ -1100,9 +1111,15 @@ LTMSidebar::dateRangeMoved(QTreeWidgetItem*item, int oldposition, int newpositio
|
||||
active = false;
|
||||
|
||||
// deselect actual selection
|
||||
dateRangeTree->selectedItems().first()->setSelected(false);
|
||||
dateRangeTree->clearSelection();
|
||||
// select the move/drop item
|
||||
item->setSelected(true);
|
||||
if (item) {
|
||||
item->setSelected(true);
|
||||
dateRangeTree->setCurrentItem(item);
|
||||
}
|
||||
|
||||
blocker.unblock();
|
||||
dateRangeTreeWidgetSelectionChanged();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1123,11 +1140,22 @@ LTMSidebar::addRange()
|
||||
newOne.setAbsoluteEnd(temp);
|
||||
}
|
||||
|
||||
// Ensure the current season is nullptr during creation of the new season
|
||||
// to prevent usage of a dangling pointer (see comment #ref1# below)
|
||||
context->notifySeasonChanged(nullptr);
|
||||
|
||||
// save
|
||||
seasons->seasons.insert(0, newOne);
|
||||
seasons->writeSeasons();
|
||||
active = false;
|
||||
|
||||
if (seasons->seasons.count() > 0) {
|
||||
// #ref1#
|
||||
// Ensure the pointer to the season is guaranteed to be valid if the
|
||||
// QList<Season> (Seasons::seasons) was reorganized during insertion
|
||||
// and its objects were relocated
|
||||
context->notifySeasonChanged(&seasons->seasons[0]);
|
||||
}
|
||||
// signal its changed!
|
||||
resetSeasons();
|
||||
}
|
||||
@@ -1426,7 +1454,7 @@ LTMSidebar::addPhase()
|
||||
|
||||
QTreeWidgetItem *addPhase = new QTreeWidgetItem(selectedDateRange, myphase.getType());
|
||||
addPhase->setSelected(true);
|
||||
addPhase->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
|
||||
addPhase->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
addPhase->setText(0, myphase.getName());
|
||||
|
||||
// save changes away
|
||||
|
||||
Reference in New Issue
Block a user