1 #include "weightgraphview.h"
8 #include <QtGui/QX11Info>
10 #include <X11/Xatom.h>
12 WeightGraphView::WeightGraphView(WeightDataModel *wdm,
13 const QString &id, QWidget *parent) :
14 QWidget(parent), id(id), wdm(wdm),
15 period(Settings::graphSettings(id).defaultTimeInterval)
17 setWindowTitle("WeightGraph");
19 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
21 connect(wdm, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
22 this, SLOT(update()));
23 connect(wdm, SIGNAL(rowsInserted(const QModelIndex&,int,int)),
24 this, SLOT(update()));
25 connect(wdm, SIGNAL(rowsRemoved(const QModelIndex&,int,int)),
26 this, SLOT(update()));
27 connect(Settings::self(), SIGNAL(settingChanged()),
28 this, SLOT(update()));
31 void WeightGraphView::mousePressEvent(QMouseEvent *)
36 QSize WeightGraphView::sizeHint() const
38 return QSize(300, 400);
41 void WeightGraphView::keyPressEvent(QKeyEvent* event)
43 switch (event->key()) {
54 QWidget::keyPressEvent(event);
57 // X-coordinate of QDate / DateWeigth
58 #define DATE_X(d) ((d)*qreal(width())/days)
59 #define DW_X(dw) DATE_X(f.daysTo((dw).date))
61 // Y-coordinate of Weight / DateWeight
62 #define WEIGHT_Y(w) (height()*(maxW-(w))/weightInterval)
63 #define DW_Y(dw) WEIGHT_Y((dw).weight)
65 // Coordinate point of DateWeight
66 #define DW_POINT(dw) QPointF(DW_X(dw), DW_Y(dw))
69 inline double weightIntervalToMult(double wi)
71 if (wi <= 0.2) return 0.01;
72 else if (wi <= 0.5) return 0.025;
73 else if (wi <= 1.0) return 0.05;
74 else if (wi <= 2.0) return 0.1;
75 else if (wi <= 5.0) return 0.25;
76 else if (wi <= 10.0) return 0.5;
77 else if (wi <= 20.0) return 1.0;
78 else if (wi <= 50.0) return 2.5;
79 else if (wi <= 100.0) return 5.0;
80 else if (wi <= 200.0) return 10.0;
81 else if (wi <= 500.0) return 25.0;
82 else if (wi <= 1000.0) return 50.0;
87 void WeightGraphView::paintEvent(QPaintEvent *)
89 // TODO: split this monster function!
90 QPainter painter(this);
91 painter.setRenderHint(QPainter::Antialiasing);
93 GraphSettings gs = Settings::graphSettings(id);
94 const QList<DW> &allWeights = wdm->getWeights();
98 const DW *beforeFirst = NULL;
99 if (allWeights.size() < 2) {
104 bool firstFound = false;
105 min = std::numeric_limits<double>::max();
106 max = std::numeric_limits<double>::min();
107 const QDate &l = allWeights.last().date;
108 for(int i=0; i < allWeights.size(); i++) {
109 if (period == 0 || firstFound || allWeights[i].date.daysTo(l) <= period) {
110 weights.append(allWeights[i]);
111 if (allWeights[i].weight < min)
112 min = allWeights[i].weight;
113 if (allWeights[i].weight > max)
114 max = allWeights[i].weight;
118 beforeFirst = &allWeights[i];
122 if (gs.weightIntervalMode == GraphSettings::AutomaticWithGoalWeight
123 && gs.goalWeightEnabled) {
124 min = qMin(min, Settings::goalWeightMin());
125 max = qMax(max, Settings::goalWeightMax());
127 else if(gs.weightIntervalMode == GraphSettings::Manual) {
128 if (gs.weightIntervalMax > gs.weightIntervalMin) {
129 min = gs.weightIntervalMin;
130 max = gs.weightIntervalMax;
133 // else default is min and max of actual data
135 double margin = (max - min)*0.03;
136 double minW = min - margin;
137 double maxW = max + margin;
138 double weightInterval = maxW-minW;
139 if (maxW-minW < 0.1) {
142 weightInterval = maxW - minW;
146 if (weights.size() < 2) {
147 l = QDate::currentDate();
151 else if (period==0) {
152 f = weights.first().date;
153 l = weights.last().date;
156 l = weights.last().date;
157 f = l.addDays(-period);
161 // Interpolate to fill gap in left part of the graph
162 if (weights.size() >= 2 && weights.first().date != f && beforeFirst != NULL) {
165 dw.weight = (weights.first().weight - beforeFirst->weight)
166 /beforeFirst->date.daysTo(weights.first().date)
167 *beforeFirst->date.daysTo(f)
168 +beforeFirst->weight;
172 painter.setWindow(-50, 0, width()+85, height()+25);
174 if (gs.goalWeightEnabled
175 && ((Settings::goalWeightMin() > minW && Settings::goalWeightMin() < maxW)
176 ||(Settings::goalWeightMax() > minW && Settings::goalWeightMax() < maxW))) {
177 QPen oldPen = painter.pen();
178 QBrush oldBrush = painter.brush();
179 painter.setPen(Qt::NoPen);
180 painter.setBrush(QColor(0,255,0,100));
181 painter.setClipRect(QRectF(0,0,width(),height()));
182 painter.drawRect(QRectF(QPointF(0, WEIGHT_Y(Settings::goalWeightMax())),
183 QPointF(width(), WEIGHT_Y(Settings::goalWeightMin()))));
184 painter.setClipping(false);
185 painter.setPen(oldPen);
186 painter.setBrush(oldBrush);
190 QFont font = painter.font();
191 font.setPixelSize(16);
192 painter.setFont(font);
193 painter.drawLine(QPointF(0.0,0.0), QPointF(0,height()));
194 double mult = weightIntervalToMult(weightInterval);
196 for(double w=ceil(minW/mult)*mult; w < maxW; w += mult, count++) {
197 double len = count%5==0 ? 7.0 : 4.0;
198 painter.drawLine(QPointF(-len,WEIGHT_Y(w)),QPointF(len,WEIGHT_Y(w)));
200 QPen p = painter.pen();
201 painter.setPen(QColor(50,50,50));
202 painter.drawLine(QPointF(len,WEIGHT_Y(w)),QPointF(width(),WEIGHT_Y(w)));
204 QString text = tr("%1").arg(double(w),0,'f', mult <= 0.25 ? 2 : 1);
205 QSize textSize = painter.fontMetrics().size(0, text);
206 painter.drawText(QPointF(-len-3-textSize.width(), WEIGHT_Y(w)+6), text);
211 font.setPixelSize(13);
212 painter.setFont(font);
213 double endOfLastDate = -1e6;
215 painter.drawLine(QPointF(0.0,height()), QPointF(width(),height()));
216 for(int day=0; day <= days; day+=mult) {
217 QString text = f.addDays(day).toString(Qt::ISODate);
218 QSize textSize = painter.fontMetrics().size(0, text);
220 if (DATE_X(day)-textSize.width()/2 > endOfLastDate + 10) {
222 painter.drawText(QPointF(DATE_X(day)-textSize.width()/2,
223 WEIGHT_Y(minW)+18), text);
224 endOfLastDate = DATE_X(day)+textSize.width()/2;
228 painter.drawLine(QPointF(DATE_X(day), WEIGHT_Y(minW)-tickLen),
229 QPointF(DATE_X(day), WEIGHT_Y(minW)+tickLen));
233 QPolygonF linepoints;
234 foreach(DW dw, weights) {
235 linepoints << DW_POINT(dw);
237 painter.drawPolyline(linepoints);
240 void WeightGraphView::show()
243 grabZoomKeysForWindow(winId(), Settings::grabZoomKeys()); //Need to be regrabbed somewhy
244 //Work around a bug: hidden graphs don't update - must wait for the
245 //graph to actually show up, then call update.
246 QTimer *tmp = new QTimer(this);
247 tmp->setSingleShot(true);
248 tmp->setInterval(500);
249 connect(tmp, SIGNAL(timeout()), this, SLOT(update()));
253 void WeightGraphView::update() {
254 grabZoomKeysForWindow(winId(), Settings::grabZoomKeys());
258 void WeightGraphView::grabZoomKeys(bool grab) {
259 WeightGraphView::grabZoomKeysForWindow(winId(), grab);
261 void WeightGraphView::grabZoomKeysForWindow(WId winId, bool grab)
264 qWarning("Can't grab keys unless we have a window id");
268 unsigned long val = (grab) ? 1 : 0;
269 Atom atom = XInternAtom(QX11Info::display(), "_HILDON_ZOOM_KEY_ATOM", False);
271 qWarning("Unable to obtain _HILDON_ZOOM_KEY_ATOM. Is this Maemo 5?");
275 XChangeProperty (QX11Info::display(), winId, atom, XA_INTEGER, 32,
276 PropModeReplace, reinterpret_cast<unsigned char *>(&val), 1);
280 int getNumberOfDaysBetweenFirstAndLastEntry(WeightDataModel *wdm) {
281 QDate first = wdm->getWeights().first().date;
282 QDate last = wdm->getWeights().last().date;
283 return first.daysTo(last);
287 #define PERIOD_CHANGE_FACTOR 0.05
288 void WeightGraphView::incPeriod() {
290 period = getNumberOfDaysBetweenFirstAndLastEntry(wdm) + 1;
292 period += qMax(1, int(period*PERIOD_CHANGE_FACTOR));
295 void WeightGraphView::decPeriod() {
297 period = getNumberOfDaysBetweenFirstAndLastEntry(wdm) - 1;
299 period = qMax(2, qMin(period-1, int(period*(1-PERIOD_CHANGE_FACTOR))));