1 /*
2 SPDX-FileCopyrightText: 2008 Joris Guisson <joris.guisson@gmail.com>
3 SPDX-FileCopyrightText: 2008 Ivan Vasic <ivasic@gmail.com>
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "schedulegraphicsitem.h"
8
9 #include <cmath>
10 #include <cstdlib>
11
12 #include <QBrush>
13 #include <QCursor>
14 #include <QFontMetricsF>
15 #include <QGraphicsSceneMouseEvent>
16 #include <QGraphicsTextItem>
17 #include <QPen>
18 #include <QRectF>
19
20 #include <KCursor>
21 #include <KLocalizedString>
22
23 #include "bwschedulerpluginsettings.h"
24 #include "schedule.h"
25 #include "weekscene.h"
26 #include <util/functions.h>
27 #include <util/log.h>
28
29 using namespace bt;
30
31 namespace kt
32 {
33 const Uint32 Top = 1;
34 const Uint32 Bottom = 2;
35 const Uint32 Left = 4;
36 const Uint32 Right = 8;
37
38 const Uint32 TopRight = Top | Right;
39 const Uint32 TopLeft = Top | Left;
40 const Uint32 BottomRight = Bottom | Right;
41 const Uint32 BottomLeft = Bottom | Left;
42
ScheduleGraphicsItem(ScheduleItem * item,const QRectF & r,const QRectF & constraints,WeekScene * ws)43 ScheduleGraphicsItem::ScheduleGraphicsItem(ScheduleItem *item, const QRectF &r, const QRectF &constraints, WeekScene *ws)
44 : QGraphicsRectItem(r)
45 , item(item)
46 , constraints(constraints)
47 , ws(ws)
48 , text_item(nullptr)
49 , resize_edge(0)
50 , ready_to_resize(false)
51 , resizing(false)
52 {
53 setAcceptHoverEvents(true);
54 setPen(QPen(Qt::black));
55 setZValue(3);
56 setHandlesChildEvents(true);
57
58 setBrush(QBrush(item->suspended ? SchedulerPluginSettings::suspendedColor() : SchedulerPluginSettings::itemColor()));
59 setFlag(QGraphicsItem::ItemIsSelectable, true);
60 setFlag(QGraphicsItem::ItemIsMovable, true);
61 }
62
~ScheduleGraphicsItem()63 ScheduleGraphicsItem::~ScheduleGraphicsItem()
64 {
65 }
66
update(const QRectF & r)67 void ScheduleGraphicsItem::update(const QRectF &r)
68 {
69 setRect(r);
70 setPos(QPointF(0, 0));
71 QString text;
72 if (item->suspended) {
73 setBrush(QBrush(SchedulerPluginSettings::suspendedColor()));
74 text = i18n("Suspended");
75 } else {
76 setBrush(QBrush(SchedulerPluginSettings::itemColor()));
77 QString ds = item->download_limit == 0 ? i18n("Unlimited") : BytesPerSecToString(item->download_limit * 1024);
78 QString us = item->upload_limit == 0 ? i18n("Unlimited") : BytesPerSecToString(item->upload_limit * 1024);
79 text = i18n("%1 Down\n%2 Up", ds, us);
80 }
81
82 if (text_item == nullptr)
83 text_item = scene()->addText(text);
84 else
85 text_item->setPlainText(text);
86
87 QFontMetricsF fm(text_item->font());
88 text_item->setPos(QPointF(r.x(), r.y()));
89 text_item->setZValue(4);
90 text_item->setTextWidth(r.width());
91 text_item->setParentItem(this);
92 setToolTip(text);
93
94 if (text_item->boundingRect().height() > r.height()) {
95 // Text is to big for rect
96 delete text_item;
97 text_item = nullptr;
98 }
99 }
100
itemChange(GraphicsItemChange change,const QVariant & value)101 QVariant ScheduleGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value)
102 {
103 if (change == ItemPositionChange && scene()) {
104 QPointF new_pos = value.toPointF();
105 if (!constraints.contains(new_pos)) {
106 qreal x = constraints.x() - boundingRect().x();
107 if (new_pos.x() < x)
108 new_pos.setX(x);
109 else if (new_pos.x() + rect().width() > x + constraints.width())
110 new_pos.setX(x + constraints.width() - rect().width());
111
112 qreal y = constraints.y() - boundingRect().y();
113 if (new_pos.y() < y)
114 new_pos.setY(y);
115 else if (new_pos.y() + rect().height() > y + constraints.height())
116 new_pos.setY(y + constraints.height() - rect().height());
117
118 return new_pos;
119 }
120 }
121
122 return QGraphicsItem::itemChange(change, value);
123 }
124
resize(QPointF scene_pos)125 QRectF ScheduleGraphicsItem::resize(QPointF scene_pos)
126 {
127 qreal x = scene_pos.x();
128 qreal y = scene_pos.y();
129
130 QRectF cur = rect();
131 if (resize_edge & Top) {
132 if (y >= cur.y() + cur.height()) { // rect becomes flipped
133 qreal yn = cur.y() + cur.height();
134 if (yn < constraints.y())
135 yn = constraints.y();
136
137 qreal h = y - yn;
138 cur.setY(yn);
139 cur.setHeight(h);
140 resize_edge |= kt::Bottom;
141 resize_edge &= ~kt::Top;
142 } else {
143 qreal yn = y < constraints.y() ? constraints.y() : y;
144 qreal h = cur.height() + (cur.y() - yn);
145 cur.setY(yn);
146 cur.setHeight(h);
147 }
148 } else if (resize_edge & Bottom) {
149 if (y < cur.y()) { // rect becomes flipped
150 qreal yn = y;
151 if (yn < constraints.y())
152 yn = constraints.y();
153
154 qreal h = cur.y() - yn;
155 cur.setY(yn);
156 cur.setHeight(h);
157 resize_edge |= kt::Top;
158 resize_edge &= ~kt::Bottom;
159 } else {
160 cur.setHeight(y - cur.y());
161 if (cur.y() + cur.height() >= constraints.y() + constraints.height())
162 cur.setHeight(constraints.y() + constraints.height() - cur.y());
163 }
164 }
165
166 if (resize_edge & Left) {
167 if (x >= cur.x() + cur.width()) { // rect becomes flipped
168 qreal xn = cur.x() + cur.x();
169 if (xn < constraints.x())
170 xn = constraints.x();
171
172 qreal w = x - xn;
173 cur.setX(xn);
174 cur.setWidth(w);
175 resize_edge |= kt::Right;
176 resize_edge &= ~kt::Left;
177 } else {
178 qreal xn = x < constraints.x() ? constraints.x() : x;
179 qreal w = cur.width() + (cur.x() - xn);
180 cur.setX(xn);
181 cur.setWidth(w);
182 }
183 } else if (resize_edge & Right) {
184 if (x < cur.x()) { // rect becomes flipped
185 qreal xn = x;
186 if (xn < constraints.x())
187 xn = constraints.x();
188
189 qreal w = cur.x() - xn;
190 cur.setX(xn);
191 cur.setWidth(w);
192 resize_edge |= kt::Left;
193 resize_edge &= ~kt::Right;
194 } else {
195 cur.setWidth(x - cur.x());
196 if (cur.x() + cur.width() >= constraints.x() + constraints.width())
197 cur.setWidth(constraints.x() + constraints.width() - cur.x());
198 }
199 }
200
201 return cur;
202 }
203
mouseMoveEvent(QGraphicsSceneMouseEvent * event)204 void ScheduleGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
205 {
206 if (!resizing) {
207 QGraphicsItem::mouseMoveEvent(event);
208 ws->setShowGuidanceLines(true);
209 QPointF sp = pos() + rect().topLeft();
210 ws->updateGuidanceLines(sp.y(), sp.y() + rect().height());
211
212 setCursor(ws->validMove(item, sp) ? Qt::DragMoveCursor : Qt::ForbiddenCursor);
213 } else {
214 QRectF cur = resize(event->scenePos());
215 setRect(cur);
216 if (text_item)
217 text_item->setPos(cur.x(), cur.y());
218
219 ws->updateGuidanceLines(cur.y(), cur.y() + cur.height());
220 }
221 }
222
mousePressEvent(QGraphicsSceneMouseEvent * event)223 void ScheduleGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
224 {
225 if (!ready_to_resize || !(event->button() & Qt::LeftButton)) {
226 QGraphicsRectItem::mousePressEvent(event);
227 // keep track of original position before the item is dragged
228 original_pos = pos();
229 } else {
230 resizing = true;
231 ws->setShowGuidanceLines(true);
232 ws->updateGuidanceLines(rect().y(), rect().y() + rect().height());
233 }
234
235 setZValue(4);
236 }
237
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)238 void ScheduleGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
239 {
240 if (resizing) {
241 resizing = false;
242 ws->setShowGuidanceLines(false);
243 ws->itemResized(item, rect());
244 } else {
245 QGraphicsRectItem::mouseReleaseEvent(event);
246
247 if (event->button() & Qt::LeftButton) {
248 if (original_pos != pos()) {
249 QPointF sp = pos() + rect().topLeft();
250 ws->itemMoved(item, sp);
251 }
252 }
253 ws->setShowGuidanceLines(false);
254 }
255
256 setZValue(3);
257 setCursor(Qt::ArrowCursor);
258 }
259
hoverEnterEvent(QGraphicsSceneHoverEvent * event)260 void ScheduleGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
261 {
262 ready_to_resize = true;
263 resize_edge = nearEdge(event->scenePos());
264 updateCursor();
265 }
266
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)267 void ScheduleGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
268 {
269 Q_UNUSED(event);
270 setCursor(Qt::ArrowCursor);
271 ready_to_resize = false;
272 }
273
hoverMoveEvent(QGraphicsSceneHoverEvent * event)274 void ScheduleGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
275 {
276 resize_edge = nearEdge(event->scenePos());
277 ready_to_resize = resize_edge != 0;
278 updateCursor();
279 }
280
updateCursor()281 void ScheduleGraphicsItem::updateCursor()
282 {
283 Qt::CursorShape shape = Qt::ArrowCursor;
284 if (resize_edge != 0) {
285 if (resize_edge == kt::TopRight || resize_edge == kt::BottomLeft)
286 shape = Qt::SizeBDiagCursor;
287 else if (resize_edge == kt::BottomRight || resize_edge == kt::TopLeft)
288 shape = Qt::SizeFDiagCursor;
289 else if (resize_edge == kt::Top || resize_edge == kt::Bottom)
290 shape = Qt::SizeVerCursor;
291 else
292 shape = Qt::SizeHorCursor;
293 }
294 setCursor(shape);
295 }
296
nearEdge(QPointF p)297 Uint32 ScheduleGraphicsItem::nearEdge(QPointF p)
298 {
299 qreal y = rect().y();
300 qreal ye = y + rect().height();
301 qreal x = rect().x();
302 qreal xe = x + rect().width();
303 Uint32 ret = 0;
304 if (std::fabs(p.y() - y) < 4)
305 ret |= Top;
306 else if (std::fabs(p.y() - ye) < 4)
307 ret |= Bottom;
308
309 if (std::fabs(p.x() - x) < 4)
310 ret |= Left;
311 else if (std::fabs(p.x() - xe) < 4)
312 ret |= Right;
313
314 return ret;
315 }
316
317 }
318