1 /*
2     SPDX-FileCopyrightText: 2007 Joris Guisson <joris.guisson@gmail.com>
3     SPDX-FileCopyrightText: 2007 Ivan Vasic <ivasic@gmail.com>
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "weekscene.h"
8 
9 #include <cmath>
10 
11 #include <KFormat>
12 #include <KLocalizedString>
13 
14 #include <QFontMetricsF>
15 #include <QGraphicsLineItem>
16 #include <QGraphicsRectItem>
17 #include <QGraphicsSceneMouseEvent>
18 #include <QGraphicsTextItem>
19 #include <QPalette>
20 
21 #include "bwschedulerpluginsettings.h"
22 #include "guidanceline.h"
23 #include "schedule.h"
24 #include "schedulegraphicsitem.h"
25 #include <util/functions.h>
26 #include <util/log.h>
27 
28 using namespace bt;
29 
30 namespace kt
31 {
WeekScene(QObject * parent)32 WeekScene::WeekScene(QObject *parent)
33     : QGraphicsScene(parent)
34     , schedule(nullptr)
35 {
36     addCalendar();
37 }
38 
~WeekScene()39 WeekScene::~WeekScene()
40 {
41 }
42 
LongestDayWidth(const QFontMetricsF & fm)43 qreal LongestDayWidth(const QFontMetricsF &fm)
44 {
45     qreal wd = 0;
46     for (int i = 1; i <= 7; i++) {
47         qreal w = fm.width(QLocale::system().dayName(i));
48         if (w > wd)
49             wd = w;
50     }
51     return wd;
52 }
53 
updateStatusText(int up,int down,bool suspended,bool enabled)54 void WeekScene::updateStatusText(int up, int down, bool suspended, bool enabled)
55 {
56     static KFormat format;
57     QString msg;
58     if (suspended)
59         msg = i18n("Current schedule: suspended");
60     else if (up > 0 && down > 0)
61         msg = i18n("Current schedule: %1/s download, %2/s upload", format.formatByteSize(down * 1024), format.formatByteSize(up * 1024));
62     else if (up > 0)
63         msg = i18n("Current schedule: unlimited download, %1/s upload", format.formatByteSize(up * 1024));
64     else if (down > 0)
65         msg = i18n("Current schedule: %1/s download, unlimited upload", format.formatByteSize(down * 1024));
66     else
67         msg = i18n("Current schedule: unlimited upload and download");
68 
69     if (!enabled)
70         msg += i18n(" (scheduler disabled)");
71 
72     status->setPlainText(msg);
73 }
74 
addCalendar()75 void WeekScene::addCalendar()
76 {
77     QGraphicsTextItem *tmp = addText(QStringLiteral("Dinges"));
78     QFontMetricsF fm(tmp->font());
79     removeItem(tmp);
80     delete tmp;
81 
82     // first add 7 rectangles for each day of the week
83     xoff = fm.width(QStringLiteral("00:00")) + 10;
84     yoff = 2 * fm.height() + 10;
85     day_width = LongestDayWidth(fm) * 1.5;
86     hour_height = fm.height() * 1.5;
87 
88     status = addText(i18n("Current schedule:"));
89     status->setPos(QPointF(0, 0));
90     status->setZValue(2);
91 
92     QPen pen(SchedulerPluginSettings::scheduleLineColor());
93     QBrush brush(SchedulerPluginSettings::scheduleBackgroundColor());
94 
95     for (int i = 0; i < 7; i++) {
96         QGraphicsRectItem *item = addRect(xoff + day_width * i, yoff, day_width, 24 * hour_height, pen, brush);
97         item->setZValue(1);
98 
99         QString day = QLocale::system().dayName(i + 1);
100 
101         // make sure day is centered in the middle of the column
102         qreal dlen = fm.width(day);
103         qreal mid = xoff + day_width * (i + 0.5);
104         qreal start = mid - dlen * 0.5;
105 
106         QGraphicsTextItem *t = addText(day);
107         t->setPos(QPointF(start, fm.height() + 5));
108         t->setZValue(2);
109 
110         rects.append(item);
111     }
112 
113     // draw hour lines
114     for (int i = 0; i <= 24; i++) {
115         QGraphicsLineItem *item = addLine(0, yoff + i * hour_height, xoff + 7 * day_width, yoff + i * hour_height, pen);
116         item->setZValue(2);
117 
118         if (i < 24) {
119             QGraphicsTextItem *t = addText(QStringLiteral("%1:00").arg(i));
120             t->setPos(QPointF(0, yoff + i * hour_height));
121             t->setZValue(2);
122         }
123         lines.append(item);
124     }
125 
126     ;
127     gline[0] = new GuidanceLine(xoff, yoff, xoff + 7 * day_width + 10);
128     gline[0]->setVisible(false);
129     gline[1] = new GuidanceLine(xoff, yoff, xoff + 7 * day_width + 10);
130     gline[1]->setVisible(false);
131     addItem(gline[0]);
132     addItem(gline[1]);
133 
134     QRectF r = sceneRect();
135     r.setHeight(r.height() + 10);
136     setSceneRect(r);
137 }
138 
addScheduleItem(ScheduleItem * item)139 QGraphicsItem *WeekScene::addScheduleItem(ScheduleItem *item)
140 {
141     QTime midnight(0, 0, 0, 0);
142     qreal x = xoff + (item->start_day - 1) * day_width;
143     //      qreal min_h = hour_height / 60.0;
144     qreal y = timeToY(item->start);
145     qreal ye = timeToY(item->end);
146 
147     QRectF rect(x, y, day_width * (item->end_day - item->start_day + 1), ye - y);
148     QRectF cst(xoff, yoff, 7 * day_width, 24 * hour_height);
149     ScheduleGraphicsItem *gi = new ScheduleGraphicsItem(item, rect, cst, this);
150     addItem(gi);
151     gi->update(rect);
152     return gi;
153 }
154 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * ev)155 void WeekScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *ev)
156 {
157     const QList<QGraphicsItem *> gis = items(ev->scenePos());
158     for (QGraphicsItem *gi : gis) {
159         if (gi->zValue() == 3) {
160             itemDoubleClicked(gi);
161             break;
162         }
163     }
164 }
165 
mousePressEvent(QGraphicsSceneMouseEvent * ev)166 void WeekScene::mousePressEvent(QGraphicsSceneMouseEvent *ev)
167 {
168     if (ev->button() == Qt::RightButton) {
169         const QList<QGraphicsItem *> gis = items(ev->scenePos());
170         for (QGraphicsItem *gi : gis) {
171             if (gi->zValue() == 3) {
172                 clearSelection();
173                 gi->setSelected(true);
174                 break;
175             }
176         }
177     } else
178         QGraphicsScene::mousePressEvent(ev);
179 }
180 
timeToY(const QTime & time)181 qreal WeekScene::timeToY(const QTime &time)
182 {
183     QTime midnight(0, 0, 0, 0);
184     qreal min_h = hour_height / 60.0;
185     return (midnight.secsTo(time) / 60.0) * min_h + yoff;
186 }
187 
yToTime(qreal y)188 QTime WeekScene::yToTime(qreal y)
189 {
190     y = y - yoff; // get rid of offset
191     qreal min_h = hour_height / 60.0;
192     return QTime(0, 0, 0, 0).addSecs((y / min_h) * 60);
193 }
194 
itemMoved(ScheduleItem * item,const QPointF & np)195 void WeekScene::itemMoved(ScheduleItem *item, const QPointF &np)
196 {
197     QTime start = yToTime(np.y());
198     int d = item->start.secsTo(item->end); // duration in seconds
199     QTime end = start.addSecs(d);
200 
201     int start_day = 1 + floor((np.x() + day_width * 0.5 - xoff) / day_width);
202     if (start_day < 1)
203         start_day = 1;
204     else if (start_day > 7)
205         start_day = 7;
206 
207     int end_day = start_day + (item->end_day - item->start_day);
208     if (end_day < 1)
209         end_day = 1;
210     else if (end_day > 7)
211         end_day = 7;
212     itemMoved(item, start, end, start_day, end_day);
213 }
214 
validMove(ScheduleItem * item,const QPointF & np)215 bool WeekScene::validMove(ScheduleItem *item, const QPointF &np)
216 {
217     if (!schedule)
218         return true;
219 
220     QTime start = yToTime(np.y());
221     int d = item->start.secsTo(item->end); // duration in seconds
222     QTime end = start.addSecs(d);
223 
224     int start_day = 1 + floor((np.x() + day_width * 0.5 - xoff) / day_width);
225     int end_day = start_day + (item->end_day - item->start_day);
226     if (end_day > 7)
227         end_day = 7;
228     return schedule->validModify(item, start, end, start_day, end_day);
229 }
230 
itemResized(ScheduleItem * item,const QRectF & r)231 void WeekScene::itemResized(ScheduleItem *item, const QRectF &r)
232 {
233     QTime start = yToTime(r.y());
234     QTime end = yToTime(r.y() + r.height());
235 
236     int start_day = 1 + floor((r.x() + day_width * 0.5 - xoff) / day_width);
237     int end_day = 1 + floor((r.x() + r.width() - day_width * 0.5 - xoff) / day_width);
238     if (start_day < 1)
239         start_day = 1;
240     else if (start_day > 7)
241         start_day = 7;
242 
243     if (end_day < 1)
244         end_day = 1;
245     else if (end_day > 7)
246         end_day = 7;
247 
248     itemMoved(item, start, end, start_day, end_day);
249 }
250 
validResize(ScheduleItem * item,const QRectF & r)251 bool WeekScene::validResize(ScheduleItem *item, const QRectF &r)
252 {
253     QTime start = yToTime(r.y());
254     QTime end = yToTime(r.y() + r.height());
255     return schedule->validModify(item, start, end, item->start_day, item->end_day);
256 }
257 
itemChanged(ScheduleItem * item,QGraphicsItem * gi)258 void WeekScene::itemChanged(ScheduleItem *item, QGraphicsItem *gi)
259 {
260     ScheduleGraphicsItem *sgi = (ScheduleGraphicsItem *)gi;
261     qreal x = xoff + (item->start_day - 1) * day_width;
262     qreal y = timeToY(item->start);
263     qreal ye = timeToY(item->end);
264     sgi->update(QRectF(x, y, day_width * (item->end_day - item->start_day + 1), ye - y));
265 }
266 
colorsChanged()267 void WeekScene::colorsChanged()
268 {
269     QPen pen(SchedulerPluginSettings::scheduleLineColor());
270     QBrush brush(SchedulerPluginSettings::scheduleBackgroundColor());
271 
272     for (QGraphicsLineItem *line : qAsConst(lines))
273         line->setPen(pen);
274 
275     for (QGraphicsRectItem *rect : qAsConst(rects)) {
276         rect->setPen(pen);
277         rect->setBrush(brush);
278     }
279 
280     pen.setStyle(Qt::DashLine);
281     gline[0]->setPen(pen);
282     gline[1]->setPen(pen);
283 }
284 
setShowGuidanceLines(bool on)285 void WeekScene::setShowGuidanceLines(bool on)
286 {
287     gline[0]->setVisible(on);
288     gline[1]->setVisible(on);
289 }
290 
updateGuidanceLines(qreal y1,qreal y2)291 void WeekScene::updateGuidanceLines(qreal y1, qreal y2)
292 {
293     const QString FORMAT = QStringLiteral("hh:mm");
294     gline[0]->update(xoff, y1, yToTime(y1).toString(FORMAT));
295     gline[1]->update(xoff, y2, yToTime(y2).toString(FORMAT));
296 }
297 }
298