Eventviews

monthgraphicsitems.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Bruno Virlet <bruno.virlet@gmail.com>
3 SPDX-FileCopyrightText: 2008 Thomas Thrainer <tom_t@gmx.at>
4
5 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
6*/
7
8#include "monthgraphicsitems.h"
9#include "eventview.h"
10#include "helper.h"
11#include "monthitem.h"
12#include "monthscene.h"
13#include "monthview.h"
14#include "prefs.h"
15
16#include <QGraphicsScene>
17#include <QPainter>
18
19using namespace EventViews;
20
21ScrollIndicator::ScrollIndicator(ScrollIndicator::ArrowDirection dir)
22 : mDirection(dir)
23{
24 setZValue(200); // on top of everything
25 hide();
26}
27
28QRectF ScrollIndicator::boundingRect() const
29{
30 return {-mWidth / 2, -mHeight / 2, mWidth, mHeight};
31}
32
33void ScrollIndicator::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
34{
35 Q_UNUSED(option)
36 Q_UNUSED(widget)
37
39
40 QPolygon arrow(3);
41 if (mDirection == ScrollIndicator::UpArrow) {
42 arrow.setPoint(0, 0, -mHeight / 2);
43 arrow.setPoint(1, mWidth / 2, mHeight / 2);
44 arrow.setPoint(2, -mWidth / 2, mHeight / 2);
45 } else if (mDirection == ScrollIndicator::DownArrow) { // down
46 arrow.setPoint(1, mWidth / 2, -mHeight / 2);
47 arrow.setPoint(2, -mWidth / 2, -mHeight / 2);
48 arrow.setPoint(0, 0, mHeight / 2);
49 }
50 QColor color(QPalette().color(QPalette::WindowText));
51 color.setAlpha(155);
52 painter->setBrush(color);
53 painter->setPen(color);
54 painter->drawPolygon(arrow);
55}
56
57//-------------------------------------------------------------
58MonthCell::MonthCell(int id, QDate date, QGraphicsScene *scene)
59 : mId(id)
60 , mDate(date)
61 , mScene(scene)
62{
63 mUpArrow = new ScrollIndicator(ScrollIndicator::UpArrow);
64 mDownArrow = new ScrollIndicator(ScrollIndicator::DownArrow);
65 mScene->addItem(mUpArrow);
66 mScene->addItem(mDownArrow);
67}
68
69MonthCell::~MonthCell()
70{
71 mScene->removeItem(mUpArrow);
72 mScene->removeItem(mDownArrow);
73 delete mUpArrow; // we've taken ownership, so this is safe
74 delete mDownArrow;
75}
76
77bool MonthCell::hasEventBelow(int height)
78{
79 if (mHeightHash.isEmpty()) {
80 return false;
81 }
82
83 for (int i = 0; i < height; ++i) {
84 if (mHeightHash.value(i) != nullptr) {
85 return true;
86 }
87 }
88
89 return false;
90}
91
92int MonthCell::topMargin()
93{
94 return 18;
95}
96
97void MonthCell::addMonthItem(MonthItem *manager, int height)
98{
99 mHeightHash[height] = manager;
100}
101
102int MonthCell::firstFreeSpace()
103{
104 MonthItem *manager = nullptr;
105 int i = 0;
106 while (true) {
107 manager = mHeightHash[i];
108 if (manager == nullptr) {
109 return i;
110 }
111 i++;
112 }
113}
114
115//-------------------------------------------------------------
116// MONTHGRAPHICSITEM
117static const int ft = 1; // frame thickness
118
119MonthGraphicsItem::MonthGraphicsItem(MonthItem *manager)
120 : QGraphicsItem(nullptr)
121 , mMonthItem(manager)
122{
123 manager->monthScene()->addItem(this);
125 transform = transform.translate(0.5, 0.5);
126 setTransform(transform);
127}
128
129MonthGraphicsItem::~MonthGraphicsItem() = default;
130
132{
133 return mMonthItem->isMoving();
134}
135
137{
138 return startDate().addDays(daySpan()) == mMonthItem->endDate();
139}
140
142{
143 return startDate() == mMonthItem->startDate();
144}
145
146QPainterPath MonthGraphicsItem::shape() const
147{
148 // The returned shape must be a closed path,
149 // otherwise MonthScene:itemAt(pos) can have
150 // problems detecting the item
151 return widgetPath(false);
152}
153
154// TODO: remove this method.
155QPainterPath MonthGraphicsItem::widgetPath(bool border) const
156{
157 // If border is set we won't draw all the path. Items spanning on multiple
158 // rows won't have borders on their boundaries.
159 // If this is the mask, we draw it one pixel bigger
160 const int x0 = (!border && !isBeginItem()) ? -1 : 0;
161 const int y0 = 0;
162 const int x1 = static_cast<int>(boundingRect().width());
163 const int y1 = static_cast<int>(boundingRect().height());
164
165 const int beginRound = 2;
166 const int margin = 1;
167
168 QPainterPath path(QPoint(x0 + beginRound, y0));
169 if (isBeginItem()) {
170 path.quadTo(QPoint(x0 + margin, y0), QPoint(x0 + margin, y0 + beginRound));
171 path.lineTo(QPoint(x0 + margin, y1 - beginRound));
172 path.quadTo(QPoint(x0 + margin, y1), QPoint(x0 + beginRound + margin, y1));
173 } else {
174 path.lineTo(x0, y0);
175 if (!border) {
176 path.lineTo(x0, y1);
177 } else {
178 path.moveTo(x0, y1);
179 }
180 path.lineTo(x0 + beginRound, y1);
181 }
182
183 if (isEndItem()) {
184 path.lineTo(x1 - beginRound, y1);
185 path.quadTo(QPoint(x1 - margin, y1), QPoint(x1 - margin, y1 - beginRound));
186 path.lineTo(QPoint(x1 - margin, y0 + beginRound));
187 path.quadTo(QPoint(x1 - margin, y0), QPoint(x1 - margin - beginRound, y0));
188 } else {
189 path.lineTo(x1, y1);
190 if (!border) {
191 path.lineTo(x1, y0);
192 } else {
193 path.moveTo(x1, y0);
194 }
195 }
196
197 // close path
198 path.lineTo(x0 + beginRound, y0);
199
200 return path;
201}
202
204{
205 // width - 2 because of the cell-dividing line with width == 1 at beginning and end
206 return QRectF(0, 0, (daySpan() + 1) * mMonthItem->monthScene()->columnWidth() - 2, mMonthItem->monthScene()->itemHeight());
207}
208
209void MonthGraphicsItem::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
210{
211 if (!mMonthItem->monthScene()->initialized()) {
212 return;
213 }
214
215 MonthScene *scene = mMonthItem->monthScene();
216
217 int textMargin = 7;
218
219 QColor bgColor = mMonthItem->bgColor();
220 bgColor = mMonthItem->selected() ? bgColor.lighter(EventView::BRIGHTNESS_FACTOR) : bgColor;
221 QColor frameColor = mMonthItem->frameColor();
222 frameColor = mMonthItem->selected() ? frameColor.lighter(EventView::BRIGHTNESS_FACTOR) : frameColor;
223 QColor textColor = EventViews::getTextColor(bgColor);
224
225 // make moving or resizing items translucent
226 if (mMonthItem->isMoving() || mMonthItem->isResizing()) {
227 bgColor.setAlphaF(0.75f);
228 }
229
230 // draw the widget without border
232 p->setBrush(bgColor);
233 p->setPen(Qt::NoPen);
234 p->drawPath(widgetPath(false));
235
237 // draw the border without fill
238 const QPen pen(frameColor, ft, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
239 p->setPen(pen);
241 p->drawPath(widgetPath(true));
242
243 // draw text
244 p->setPen(textColor);
245
246 int alignFlag = Qt::AlignVCenter;
247 if (isBeginItem()) {
248 alignFlag |= Qt::AlignLeft;
249 } else if (isEndItem()) {
250 alignFlag |= Qt::AlignRight;
251 } else {
252 alignFlag |= Qt::AlignHCenter;
253 }
254
255 // !isBeginItem() is not always isEndItem()
256 QString text = mMonthItem->text(!isBeginItem());
257 p->setFont(mMonthItem->monthScene()->monthView()->preferences()->monthViewFont());
258
259 // Every item should set its own LayoutDirection, or eliding fails miserably
261
262 QRect textRect = QRect(textMargin, 0, static_cast<int>(boundingRect().width() - 2 * textMargin), scene->itemHeight());
263
264 if (mMonthItem->monthScene()->monthView()->preferences()->enableMonthItemIcons()) {
265 const QList<QPixmap> icons = mMonthItem->icons();
266 int iconWidths = 0;
267
268 for (const QPixmap &icon : icons) {
269 iconWidths += icon.width();
270 }
271
272 if (!icons.isEmpty()) {
273 // add some margin between the icons and the text
274 iconWidths += textMargin / 2;
275 }
276
277 int textWidth = p->fontMetrics().size(0, text).width();
278 if (textWidth + iconWidths > textRect.width()) {
279 textWidth = textRect.width() - iconWidths;
280 text = p->fontMetrics().elidedText(text, Qt::ElideRight, textWidth);
281 }
282
283 int curXPos = textRect.left();
284 if (alignFlag & Qt::AlignRight) {
285 curXPos += textRect.width() - textWidth - iconWidths;
286 } else if (alignFlag & Qt::AlignHCenter) {
287 curXPos += (textRect.width() - textWidth - iconWidths) / 2;
288 }
289 alignFlag &= ~(Qt::AlignRight | Qt::AlignCenter);
290 alignFlag |= Qt::AlignLeft;
291
292 // update the rect, where the text will be displayed
293 textRect.setLeft(curXPos + iconWidths);
294
295 // assume that all pixmaps have the same height
296 int pixYPos = icons.isEmpty() ? 0 : (textRect.height() - icons.at(0).height()) / 2;
297 for (const QPixmap &icon : std::as_const(icons)) {
298 p->drawPixmap(curXPos, pixYPos, icon);
299 curXPos += icon.width();
300 }
301
302 p->drawText(textRect, alignFlag | Qt::AlignVCenter, text);
303 } else {
304 text = p->fontMetrics().elidedText(text, Qt::ElideRight, textRect.width());
305 p->drawText(textRect, alignFlag, text);
306 }
307}
308
309void MonthGraphicsItem::setStartDate(QDate date)
310{
311 mStartDate = date;
312}
313
315{
316 return startDate().addDays(daySpan());
317}
318
320{
321 return mStartDate;
322}
323
324void MonthGraphicsItem::setDaySpan(int span)
325{
326 mDaySpan = span;
327}
328
330{
331 return mDaySpan;
332}
333
335{
336 MonthCell *cell = mMonthItem->monthScene()->mMonthCellMap.value(startDate());
337
338 // If the item is moving and this one is moved outside the view,
339 // cell will be null
340 if (mMonthItem->isMoving() && !cell) {
341 hide();
342 return;
343 }
344
345 Q_ASSERT(cell);
346
348
349 int beginX = 1 + mMonthItem->monthScene()->cellHorizontalPos(cell);
350 int beginY = 1 + cell->topMargin() + mMonthItem->monthScene()->cellVerticalPos(cell);
351
352 beginY += mMonthItem->position() * mMonthItem->monthScene()->itemHeightIncludingSpacing()
353 - mMonthItem->monthScene()->startHeight() * mMonthItem->monthScene()->itemHeightIncludingSpacing(); // scrolling
354
355 setPos(beginX, beginY);
356
357 if (mMonthItem->position() < mMonthItem->monthScene()->startHeight()
358 || mMonthItem->position() - mMonthItem->monthScene()->startHeight() >= mMonthItem->monthScene()->maxRowCount()) {
359 hide();
360 } else {
361 show();
362 update();
363 }
364}
365
366QString MonthGraphicsItem::getToolTip() const
367{
368 return mMonthItem->toolTipText(mStartDate);
369}
370
371#include "moc_monthgraphicsitems.cpp"
Keeps information about a month cell.
QDate startDate() const
Returns the starting date of this item.
QDate endDate() const
Computed from startDate() and daySpan().
bool isMoving() const
Returns true if this item is currently being moved (ie.
void updateGeometry()
Change QGraphicsItem pos and boundingRect in the scene according to the incidence start and end date.
QRectF boundingRect() const override
Reimplemented from QGraphicsItem.
bool isBeginItem() const
Returns true if this MonthGraphicsItem is the first one of the MonthItem ones.
bool isEndItem() const
Returns true if this MonthGraphicsItem is the last one of the MonthItem ones.
int daySpan() const
Returns the number of day this item spans on minus one to be compatible with QDate::addDays().
A month item manages different MonthGraphicsItems.
Definition monthitem.h:27
int position() const
Returns the position of the item ( > 0 ).
Definition monthitem.h:113
bool isMoving() const
Returns true if the item is being moved.
Definition monthitem.h:167
virtual QColor frameColor() const =0
Returns the frame color of the item.
MonthScene * monthScene() const
Returns the associated month scene to this item.
Definition monthitem.h:121
QDate endDate() const
The end date of the incidence, generally realEndDate.
virtual QColor bgColor() const =0
Returns the background color of the item.
virtual QString toolTipText(const QDate &date) const =0
Returns the text for the tooltip of the item.
bool selected() const
Returns true if this item is selected.
Definition monthitem.h:105
virtual QList< QPixmap > icons() const =0
Returns a list of pixmaps to draw next to the items.
bool isResizing() const
Returns true if the item is being resized.
Definition monthitem.h:175
QDate startDate() const
The start date of the incidence, generally realStartDate.
virtual QString text(bool end) const =0
Returns the text to draw in an item.
Graphics items which indicates that the view can be scrolled to display more events.
Namespace EventViews provides facilities for displaying incidences, including events,...
Definition agenda.h:33
QColor getTextColor(const QColor &c)
Returns a nice QColor for text, give the input color &c.
Definition helper.cpp:26
KDOCTOOLS_EXPORT QString transform(const QString &file, const QString &stylesheet, const QList< const char * > &params=QList< const char * >())
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
QColor lighter(int factor) const const
void setAlphaF(float alpha)
QDate addDays(qint64 ndays) const const
QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags) const const
QSize size(int flags, const QString &text, int tabStops, int *tabArray) const const
void prepareGeometryChange()
QGraphicsScene * scene() const const
void setPos(const QPointF &pos)
void update(const QRectF &rect)
void removeItem(QGraphicsItem *item)
void drawPath(const QPainterPath &path)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void drawText(const QPoint &position, const QString &text)
QFontMetrics fontMetrics() const const
void setBrush(Qt::BrushStyle style)
void setFont(const QFont &font)
void setLayoutDirection(Qt::LayoutDirection direction)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
int height() const const
int left() const const
void setLeft(int x)
int width() const const
qreal height() const const
qreal width() const const
int width() const const
bool isRightToLeft() const const
AlignVCenter
RightToLeft
RoundCap
RoundJoin
ElideRight
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:57:55 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.