From e76247a25be36efb2ed9c297452f0d91e1a081ca Mon Sep 17 00:00:00 2001 From: Sean Rhea Date: Sun, 3 Jan 2010 19:32:22 -0500 Subject: [PATCH] remove ride map from RideCalendar I think the previous implementation could have referenced already-deleted RideItem objects during calls to Split Ride. This commit removes the calendar's own map of RideItems, and instead uses the list of rides in MainWindow::allRideItems. Because I use binary search on that list, this implementation should be just as fast as the old one. But because I don't hang on to any RideItem pointers beyond a single call to RideCalendar::paintCell, it shouldn't be vulnerable to referencing already-deleted RideItem objects. --- src/MainWindow.cpp | 10 ++-- src/RideCalendar.cpp | 107 ++++++++++++++++++++++++++++--------------- src/RideCalendar.h | 7 ++- 3 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index c21103d59..260f0fcf2 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -129,7 +129,7 @@ MainWindow::MainWindow(const QDir &home) : splitter->setContentsMargins(10, 20, 10, 10); // attempting to follow some UI guides // Analysis toolbox contents - calendar = new RideCalendar; + calendar = new RideCalendar(this); calendar->setHome(home); treeWidget = new QTreeWidget; @@ -197,7 +197,7 @@ MainWindow::MainWindow(const QDir &home) : last = new RideItem(RIDE_TYPE, home.path(), name, dt, zones(), notesFileName); allRides->addChild(last); - calendar->addRide(reinterpret_cast(last)); + calendar->update(); } } @@ -445,7 +445,7 @@ MainWindow::addRide(QString name, bool bSelect /*=true*/) ++index; } allRides->insertChild(index, last); - calendar->addRide(last); + calendar->update(); criticalPowerWindow->newRideAdded(); if (bSelect) { @@ -473,14 +473,11 @@ MainWindow::removeCurrentRide() } } - calendar->removeRide(item); if (x>0) { itemToSelect = allRides->child(x-1); - calendar->addRide(reinterpret_cast(itemToSelect)); } if ((x+1)childCount()) { itemToSelect = allRides->child(x+1); - calendar->addRide(reinterpret_cast(itemToSelect)); } QString strOldFileName = item->fileName; @@ -508,6 +505,7 @@ MainWindow::removeCurrentRide() treeWidget->setCurrentItem(itemToSelect); rideTreeWidgetSelectionChanged(); + calendar->update(); } void diff --git a/src/RideCalendar.cpp b/src/RideCalendar.cpp index de63436e5..f59af033b 100644 --- a/src/RideCalendar.cpp +++ b/src/RideCalendar.cpp @@ -10,9 +10,13 @@ #include "RideItem.h" #include "RideCalendar.h" +#include "MainWindow.h" +#include "Settings.h" -RideCalendar::RideCalendar(QWidget *parent) - : QCalendarWidget(parent) +#include + +RideCalendar::RideCalendar(MainWindow *parent) : + QCalendarWidget(parent), mainWindow(parent) { this->setFirstDayOfWeek(Qt::Monday); this->addWorkoutCode(QString("race"), QColor(255,128,128)); @@ -21,9 +25,72 @@ RideCalendar::RideCalendar(QWidget *parent) this->addWorkoutCode(QString("gym"), QColor(Qt::lightGray)); }; +struct RideIter +{ + typedef std::random_access_iterator_tag iterator_category; + typedef const RideItem *value_type; + typedef int difference_type; + typedef value_type& reference; + typedef value_type* pointer; + + MainWindow *mainWindow; + int i; + + RideIter() : mainWindow(NULL), i(0) {} + RideIter(MainWindow *mainWindow, int i = 0) : mainWindow(mainWindow), i(i) {} + const RideItem *operator*() { + assert(mainWindow); + assert(i < mainWindow->allRideItems()->childCount()); + return dynamic_cast(mainWindow->allRideItems()->child(i)); + } + RideIter operator++() { /* preincrement */ ++i; return *this; } + RideIter operator++(int) { RideIter result(mainWindow, i); ++i; return result; } + const RideIter &operator+=(int j) { i += j; return *this; } +}; + +bool operator==(const RideIter &a, const RideIter &b) { return a.i == b.i; } +bool operator!=(const RideIter &a, const RideIter &b) { return a.i != b.i; } +int operator-(const RideIter &a, const RideIter &b) { return a.i - b.i; } + +struct RideItemDateLessThan +{ + bool operator()(const RideItem *a, const RideItem *b) { return a->dateTime < b->dateTime; } +}; + +struct RideItemDateGreaterThan +{ + bool operator()(const RideItem *a, const RideItem *b) { return a->dateTime > b->dateTime; } +}; + void RideCalendar::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const { - if (!_rides.contains(date)) { + QMultiMap ridesToday; + RideIter begin(mainWindow, 0); + RideIter end(mainWindow, mainWindow->allRideItems()->childCount()); + + boost::shared_ptr settings = GetApplicationSettings(); + bool ascending = settings->value(GC_ALLRIDES_ASCENDING, Qt::Checked).toInt() > 0; + RideIter i; + if (ascending) { + RideItemDateLessThan comp; + RideItem search(0, "", "", QDateTime(date), NULL, ""); + i = std::lower_bound(begin, end, &search, comp); + } + else { + RideItemDateGreaterThan comp; + RideItem search(0, "", "", QDateTime(date.addDays(1)), NULL, ""); + i = std::upper_bound(begin, end, &search, comp); + } + + while (i != end) { + const RideItem *ride = *i; + if (ride->dateTime.date() != date) + break; + ridesToday.insert(ride->dateTime, ride); + ++i; + } + + if (ridesToday.isEmpty()) { QCalendarWidget::paintCell(painter, rect, date); return; } @@ -33,29 +100,16 @@ void RideCalendar::paintCell(QPainter *painter, const QRect &rect, const QDate & * Fill in the text and color to some default */ QRect textRect; - int number = _rides.count(date); + int number = ridesToday.size(); float count = 1; int startY, stopY; QPen pen(Qt::SolidLine); painter->setPen(pen); pen.setCapStyle(Qt::SquareCap); - QMultiMap ridesToday; - RideItem * ride; QFont font = painter->font(); font.setPointSize(font.pointSize() - 2); painter->setFont(font); - /* - * Loop over all the matching rides, and record the time. - * That way, the entries are sorted earliest -> latest. - */ - QMultiMap::const_iterator j = _rides.find(date); - while (j != _rides.end() && j.key() == date) { - ride = j.value(); - ridesToday.insert(ride->dateTime, ride); - ++j; - } - /* * Loop over all the rides for today, and record colour and text. */ @@ -136,25 +190,6 @@ void RideCalendar::addWorkoutCode(QString string, QColor color) workoutCodes[string] = color; } -/* - * Add a ride, to a specific date. - */ -void RideCalendar::addRide(RideItem* ride) -{ - _rides.insert(ride->dateTime.date(), ride); - update(); -} - -/* - * Remove the info for a current ride. - */ -void RideCalendar::removeRide(RideItem* ride) -{ - QDate date = ride->dateTime.date(); - _rides.erase(_rides.find(date, ride)); - update(); -} - /* * We extend QT's QCalendarWidget's sizeHint() so we claim a little bit of * extra space. diff --git a/src/RideCalendar.h b/src/RideCalendar.h index f42c6d18b..17f3dd651 100644 --- a/src/RideCalendar.h +++ b/src/RideCalendar.h @@ -4,15 +4,14 @@ #include #include #include "RideItem.h" +class MainWindow; class RideCalendar : public QCalendarWidget { Q_OBJECT public: - RideCalendar(QWidget *parent = 0); - void removeRide(RideItem*); - void addRide(RideItem*); + RideCalendar(MainWindow *parent); QSize sizeHint() const; void setHome(const QDir&); void addWorkoutCode(QString, QColor); @@ -21,9 +20,9 @@ protected: void paintCell(QPainter *, const QRect &, const QDate &) const; private: - QMultiMap _rides; QMap workoutCodes; QDir home; + MainWindow *mainWindow; }; #endif