mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
.. move the fluff that has collected into the src directory to somewhere more appropriate.
237 lines
7.1 KiB
C++
237 lines
7.1 KiB
C++
#include <QCalendarWidget>
|
|
#include <QMultiMap>
|
|
#include <QPainter>
|
|
#include <QObject>
|
|
#include <QDate>
|
|
#include <QAbstractItemView>
|
|
#include <QSize>
|
|
#include <QTextCharFormat>
|
|
#include <QPen>
|
|
|
|
#include "RideItem.h"
|
|
#include "RideMetadata.h"
|
|
#include "RideCalendar.h"
|
|
#include "MainWindow.h"
|
|
#include "Settings.h"
|
|
|
|
#include <algorithm>
|
|
|
|
RideCalendar::RideCalendar(MainWindow *parent) :
|
|
QCalendarWidget(parent), mainWindow(parent)
|
|
{
|
|
defaultColor = Qt::white;
|
|
// make font 2 point sizes smaller that the default
|
|
QFont font;
|
|
font.setPointSize(font.pointSize()-2);
|
|
setFont(font);
|
|
this->setFirstDayOfWeek(Qt::Monday);
|
|
|
|
// setup workout codes
|
|
configUpdate();
|
|
|
|
connect(mainWindow, SIGNAL(configChanged()), this, SLOT(configUpdate()));
|
|
};
|
|
|
|
void
|
|
RideCalendar::configUpdate()
|
|
{
|
|
// clear existing
|
|
workoutCodes.clear();
|
|
|
|
// setup the keyword/color combinations from config settings
|
|
foreach (KeywordDefinition keyword, mainWindow->rideMetadata()->getKeywords()) {
|
|
if (keyword.name == "Default")
|
|
defaultColor = keyword.color;
|
|
else {
|
|
addWorkoutCode(keyword.name, keyword.color);
|
|
|
|
// alternative texts in notes
|
|
foreach (QString token, keyword.tokens) {
|
|
addWorkoutCode(token, keyword.color);
|
|
}
|
|
}
|
|
}
|
|
|
|
// force a repaint -- NASTY HACK TO WORKAROUND BUG
|
|
QAbstractItemView *view = qFindChild<QAbstractItemView*>(this);
|
|
if (view) view->viewport()->update();
|
|
else update(); // fallback
|
|
}
|
|
|
|
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<const RideItem*>(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
|
|
{
|
|
QMultiMap<QDateTime, const RideItem*> ridesToday;
|
|
RideIter begin(mainWindow, 0);
|
|
RideIter end(mainWindow, mainWindow->allRideItems()->childCount());
|
|
|
|
bool ascending = appsettings->value(this, GC_ALLRIDES_ASCENDING, Qt::Checked).toInt() > 0;
|
|
RideIter i;
|
|
if (ascending) {
|
|
RideItemDateLessThan comp;
|
|
RideItem search(0, "", "", QDateTime(date), NULL, NULL, "", NULL);
|
|
i = std::lower_bound(begin, end, &search, comp);
|
|
}
|
|
else {
|
|
RideItemDateGreaterThan comp;
|
|
RideItem search(0, "", "", QDateTime(date.addDays(1)), NULL, NULL, "", 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;
|
|
}
|
|
painter->save();
|
|
|
|
/*
|
|
* Fill in the text and color to some default
|
|
*/
|
|
QRect textRect;
|
|
int number = ridesToday.size();
|
|
float count = 1;
|
|
int startY, stopY;
|
|
QPen pen(Qt::SolidLine);
|
|
painter->setPen(pen);
|
|
pen.setCapStyle(Qt::SquareCap);
|
|
QFont font = painter->font();
|
|
font.setPointSize(font.pointSize() - 2);
|
|
painter->setFont(font);
|
|
|
|
/*
|
|
* Loop over all the rides for today, and record colour and text.
|
|
*/
|
|
foreach (const RideItem *ride, ridesToday.values()) {
|
|
QString notesPath = home.absolutePath() + "/" + ride->notesFileName;
|
|
QFile notesFile(notesPath);
|
|
QColor color = defaultColor;
|
|
QString line("Ride");
|
|
QString code;
|
|
if (notesFile.exists()) {
|
|
if (notesFile.open(QFile::ReadOnly | QFile::Text)) {
|
|
QTextStream in(¬esFile);
|
|
line = in.readLine();
|
|
notesFile.close();
|
|
foreach(code, workoutCodes.keys()) {
|
|
if (line.contains(code, Qt::CaseInsensitive)) {
|
|
color = workoutCodes[code];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
startY = rect.y() + ((count - 1) * rect.height()) / number;
|
|
stopY = rect.height() / number;
|
|
/* fillRect() doesn't need width-1, height-1. drawRect() does */
|
|
painter->fillRect(rect.x(), startY,
|
|
rect.width(), stopY,
|
|
color);
|
|
textRect = QRect(rect.x(), startY, rect.width(), stopY);
|
|
pen.setColor(Qt::black);
|
|
pen.setStyle(Qt::SolidLine);
|
|
painter->setPen(pen);
|
|
painter->drawText(textRect, Qt::AlignHCenter | Qt::TextWordWrap, line);
|
|
pen.setColor(QColor(0, 0, 0, 63));
|
|
pen.setStyle(Qt::SolidLine);
|
|
painter->setPen(pen);
|
|
painter->drawRoundRect(textRect, 10, 10);
|
|
++count;
|
|
}
|
|
|
|
/*
|
|
* Draw a rectangle in the color specified. If this is the
|
|
* currently selected date, draw a black outline.
|
|
*/
|
|
if (date == selectedDate()) {
|
|
pen.setColor(Qt::black);
|
|
pen.setStyle(Qt::SolidLine);
|
|
pen.setWidth(2);
|
|
painter->setPen(pen);
|
|
/* drawRect() draws an outline, so needs width-1, height-1 */
|
|
/* We set +1, +1, -2, -2 because the width is 2. */
|
|
painter->drawRect(rect.x() + 1, rect.y() + 1,
|
|
rect.width() - 2, rect.height() - 2);
|
|
}
|
|
|
|
/*
|
|
* Display the date.
|
|
*/
|
|
pen.setColor(QColor(0, 0, 0, 63));
|
|
pen.setStyle(Qt::SolidLine);
|
|
pen.setWidth(1);
|
|
painter->setPen(pen);
|
|
QString textDate = QString::number(date.day());
|
|
painter->drawText(rect,
|
|
Qt::AlignHCenter | Qt::TextWordWrap | Qt::AlignVCenter,
|
|
textDate);
|
|
|
|
/* We're done, restore */
|
|
painter->restore();
|
|
}
|
|
|
|
void RideCalendar::setHome(const QDir &homeDir)
|
|
{
|
|
home = homeDir;
|
|
}
|
|
|
|
void RideCalendar::addWorkoutCode(QString string, QColor color)
|
|
{
|
|
workoutCodes[string] = color;
|
|
}
|
|
|
|
/*
|
|
* We extend QT's QCalendarWidget's sizeHint() so we claim a little bit of
|
|
* extra space.
|
|
*/
|
|
QSize RideCalendar::sizeHint() const
|
|
{
|
|
QSize hint = QCalendarWidget::sizeHint();
|
|
hint.setHeight(hint.height() * 2);
|
|
hint.setWidth(hint.width() * 2);
|
|
return hint;
|
|
}
|
|
|