2 * Copyright 2008 Davide Bettio <davide.bettio@kdemail.net>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "calendartable.h"
23 #include <QtCore/QDate>
24 #include <QtGui/QPainter>
25 #include <QtGui/QWidget>
26 #include <QtGui/QGraphicsSceneWheelEvent>
27 #include <QtGui/QStyleOptionGraphicsItem>
34 #include <Plasma/Theme>
39 static const int DISPLAYED_WEEKS
= 6;
41 class CalendarCellBorder
44 CalendarCellBorder(int c
, int w
, int d
, CalendarTable::CellTypes t
, QDate dt
)
56 CalendarTable::CellTypes type
;
60 class CalendarTablePrivate
63 CalendarTablePrivate(CalendarTable
*q
, const QDate
&cDate
= QDate::currentDate())
66 svg
->setImagePath("widgets/calendar");
67 svg
->setContainsMultipleImages(true);
69 calendar
= KGlobal::locale()->calendar();
73 QDate currentDate
= QDate::currentDate();
74 month
= calendar
->month(currentDate
);
75 year
= calendar
->year(currentDate
);
77 opacity
= 0.5; //transparency for the inactive text
80 ~CalendarTablePrivate()
85 int firstMonthDayIndex(int y
, int m
)
88 calendar
->setYMD(myDate
, y
, m
, 1);
90 return (((calendar
->dayOfWeek(myDate
) - 1) + (calendar
->daysInWeek(date
) - (calendar
->weekStartDay() - 1))) % calendar
->daysInWeek(date
)) + 1;
93 QRectF
hoveredCellRect(CalendarTable
*q
, const QPointF
&hoverPoint
)
98 if (hoverPoint
.isNull()) {
102 if (hoverPoint
.x() < centeringSpace
+ cellW
+ weekBarSpace
) {
107 int x
= (hoverPoint
.x() - centeringSpace
) / (cellW
+ cellSpace
);
108 int y
= (hoverPoint
.y() - headerHeight
- headerSpace
) / (cellH
+ cellSpace
);
110 if (x
< 1 || x
> 7 || y
< 0 || y
> DISPLAYED_WEEKS
) {
114 //FIXME: this should be a hint or something somewhere
117 //kDebug () << x << y;
118 return QRectF(q
->cellX(x
- 1) - glowRadius
, q
->cellY(y
) - glowRadius
,
119 cellW
+ glowRadius
* 2, cellH
+ glowRadius
* 2);
122 void updateHoveredPainting(CalendarTable
*q
, const QPointF
&hoverPoint
)
124 QRectF newHoverRect
= hoveredCellRect(q
, hoverPoint
);
126 // now update what is needed, and only what is needed!
127 if (newHoverRect
.isValid() && newHoverRect
!= hoverRect
) {
128 if (hoverRect
.isValid()) {
129 q
->update(hoverRect
);
131 q
->update(newHoverRect
);
134 hoverRect
= newHoverRect
;
137 int cell(int week
, int weekDay
, CalendarTable::CellTypes
*type
, QDate
&cellDate
)
141 if ((week
== 0) && (weekDay
< firstMonthDayIndex(year
, month
))){
142 int prevMonth
= (month
== 1) ? 12 : month
- 1;
143 calendar
->setYMD(myDate
, year
, prevMonth
, 1);
146 (*type
) |= CalendarTable::NotInCurrentMonth
;
149 calendar
->setYMD(cellDate
, (prevMonth
== 12) ? year
- 1 : year
, prevMonth
, calendar
->daysInMonth(myDate
) - (firstMonthDayIndex(year
, month
) - 1 - weekDay
));
150 return calendar
->daysInMonth(myDate
) - (firstMonthDayIndex(year
, month
) - 1 - weekDay
);
152 calendar
->setYMD(myDate
, year
, month
, 1);
153 int day
= (week
* calendar
->daysInWeek(date
) + weekDay
) - firstMonthDayIndex(year
, month
) + 1;
155 if (day
<= calendar
->daysInMonth(myDate
)) {
157 (*type
) &= ~CalendarTable::NotInCurrentMonth
;
160 calendar
->setYMD(cellDate
, year
, month
, day
);
164 (*type
) |= CalendarTable::NotInCurrentMonth
;
166 int nextMonth
= (month
== 12) ? 1 : month
+ 1;
167 calendar
->setYMD(cellDate
, (nextMonth
== 1) ? year
+ 1 : year
, nextMonth
, day
- calendar
->daysInMonth(myDate
));
168 return day
- calendar
->daysInMonth(myDate
);
174 const KCalendarSystem
*calendar
;
193 CalendarTable::CalendarTable(const QDate
&date
, QGraphicsWidget
*parent
)
194 : QGraphicsWidget(parent
), d(new CalendarTablePrivate(this, date
))
198 CalendarTable::CalendarTable(QGraphicsWidget
*parent
)
199 : QGraphicsWidget(parent
), d(new CalendarTablePrivate(this))
201 setAcceptHoverEvents(true);
204 CalendarTable::~CalendarTable()
209 const KCalendarSystem
*CalendarTable::calendar() const
214 bool CalendarTable::setCalendar(KCalendarSystem
*calendar
)
216 d
->calendar
= calendar
;
220 bool CalendarTable::setDate(const QDate
&date
)
222 int oldYear
= d
->year
;
223 int oldMonth
= d
->month
;
224 QDate oldDate
= d
->date
;
226 d
->year
= d
->calendar
->year(date
);
227 d
->month
= d
->calendar
->month(date
);
228 bool fullUpdate
= false;
230 if (oldYear
!= d
->year
){
231 emit
displayedYearChanged(d
->year
, d
->month
);
235 if (oldMonth
!= d
->month
){
236 emit
displayedMonthChanged(d
->year
, d
->month
);
240 d
->updateHoveredPainting(this, QPointF());
245 // only update the old and the new areas
246 int offset
= d
->firstMonthDayIndex(d
->year
, d
->month
);
247 int daysInWeek
= d
->calendar
->daysInWeek(d
->date
);
249 int day
= d
->calendar
->day(oldDate
);
250 int x
= ((offset
+ day
- 1) % daysInWeek
);
254 int y
= (offset
+ day
- 2) / daysInWeek
;
255 update(cellX(x
- 1) - d
->glowRadius
, cellY(y
) - d
->glowRadius
,
256 d
->cellW
+ d
->glowRadius
* 2, d
->cellH
+ d
->glowRadius
* 2);
258 day
= d
->calendar
->day(date
);
259 x
= (offset
+ day
- 1) % daysInWeek
;
263 y
= (offset
+ day
- 2) / daysInWeek
;
264 update(cellX(x
- 1) - d
->glowRadius
, cellY(y
) - d
->glowRadius
,
265 d
->cellW
+ d
->glowRadius
* 2, d
->cellH
+ d
->glowRadius
* 2);
271 const QDate
& CalendarTable::date() const
276 int CalendarTable::cellX(int weekDay
)
278 return boundingRect().x() + d
->centeringSpace
+
279 d
->weekBarSpace
+ d
->cellW
+
280 ((d
->cellW
+ d
->cellSpace
) * (weekDay
));
283 int CalendarTable::cellY(int week
)
285 return (int) boundingRect().y() + (d
->cellH
+ d
->cellSpace
) * (week
) + d
->headerHeight
+ d
->headerSpace
;
288 void CalendarTable::wheelEvent(QGraphicsSceneWheelEvent
* event
)
292 if (event
->delta() < 0) {
293 if (d
->month
== 12) {
296 emit
displayedYearChanged(d
->year
, d
->month
);
301 emit
displayedMonthChanged(d
->year
, d
->month
);
302 } else if (event
->delta() > 0) {
306 emit
displayedYearChanged(d
->year
, d
->month
);
311 emit
displayedMonthChanged(d
->year
, d
->month
);
317 void CalendarTable::mousePressEvent(QGraphicsSceneMouseEvent
*event
)
321 if ((event
->pos().x() >= cellX(0)) && (event
->pos().x() <= cellX(d
->calendar
->daysInWeek(d
->date
)) - d
->cellSpace
) &&
322 (event
->pos().y() >= cellY(0)) && (event
->pos().y() <= cellY(DISPLAYED_WEEKS
) - d
->cellSpace
)){
328 for (int i
= 0; i
< d
->calendar
->daysInWeek(d
->date
); i
++) {
329 if ((event
->pos().x() >= cellX(i
)) && (event
->pos().x() <= cellX(i
+ 1) - d
->cellSpace
))
333 for (int i
= 0; i
< DISPLAYED_WEEKS
; i
++) {
334 if ((event
->pos().y() >= cellY(i
)) && (event
->pos().y() <= cellY(i
+ 1) - d
->cellSpace
))
338 if ((week
>= 0) && (weekDay
>= 0)) {
342 QDate oldDate
= d
->date
;
343 d
->cell(week
, weekDay
+ 1, 0, tmpDate
);
345 if (tmpDate
== oldDate
) {
350 emit
dateChanged(tmpDate
, oldDate
);
351 emit
dateChanged(tmpDate
);
356 void CalendarTable::mouseMoveEvent(QGraphicsSceneMouseEvent
*event
)
358 mousePressEvent(event
);
361 void CalendarTable::mouseReleaseEvent(QGraphicsSceneMouseEvent
*event
)
368 void CalendarTable::hoverMoveEvent(QGraphicsSceneHoverEvent
*event
)
370 d
->updateHoveredPainting(this, event
->pos());
373 void CalendarTable::resizeEvent(QGraphicsSceneResizeEvent
* event
)
377 QRectF r
= contentsRect();
378 int rectSizeH
= int(r
.height() / (DISPLAYED_WEEKS
+ 1));
379 int rectSizeW
= int(r
.width() / 8);
381 //Using integers to help to keep things aligned to the grid
382 //kDebug() << r.width() << rectSize;
383 d
->cellSpace
= qMax(1, qMin(4, qMin(rectSizeH
, rectSizeW
) / 20));
384 d
->headerSpace
= d
->cellSpace
* 2;
385 d
->weekBarSpace
= d
->cellSpace
* 2 + 1;
386 d
->cellH
= rectSizeH
- d
->cellSpace
;
387 d
->cellW
= rectSizeW
- d
->cellSpace
;
388 d
->glowRadius
= d
->cellW
* .1;
389 d
->headerHeight
= (int) (d
->cellH
/ 1.5);
390 d
->centeringSpace
= qMax(0, int((r
.width() - (rectSizeW
* 8) - (d
->cellSpace
* 7)) / 2));
393 void CalendarTable::paintCell(QPainter
*p
, int cell
, int week
, int weekDay
, CellTypes type
, const QDate
&cellDate
)
397 QString cellSuffix
= type
& NotInCurrentMonth
? "inactive" : "active";
398 QRectF cellArea
= QRectF(cellX(weekDay
), cellY(week
), d
->cellW
, d
->cellH
);
400 d
->svg
->paint(p
, cellArea
, cellSuffix
); // draw background
402 QColor numberColor
= Theme::defaultTheme()->color(Plasma::Theme::TextColor
);
403 if (type
& NotInCurrentMonth
) {
404 p
->setOpacity(d
->opacity
);
407 p
->setPen(numberColor
);
408 QFont font
= Theme::defaultTheme()->font(Plasma::Theme::DefaultFont
);
410 font
.setPixelSize(cellArea
.height() * 0.7);
412 p
->drawText(cellArea
, Qt::AlignCenter
, QString::number(cell
), &cellArea
); //draw number
416 void CalendarTable::paintBorder(QPainter
*p
, int cell
, int week
, int weekDay
, CellTypes type
, const QDate
&cellDate
)
421 if (type
& Hovered
) {
422 d
->svg
->paint(p
, QRect(cellX(weekDay
), cellY(week
), d
->cellW
, d
->cellH
), "hoverHighlight");
429 } else if (type
& Selected
) {
430 elementId
= "selected";
435 d
->svg
->paint(p
, QRectF(cellX(weekDay
) - 1, cellY(week
) - 1,
436 d
->cellW
+ 1, d
->cellH
+ 2),
440 void CalendarTable::paint(QPainter
*p
, const QStyleOptionGraphicsItem
*option
, QWidget
*widget
)
443 int daysInWeek
= d
->calendar
->daysInWeek(d
->date
);
445 // Draw weeks numbers column and day header
446 QRectF r
= boundingRect();
447 d
->svg
->paint(p
, QRectF(r
.x() + d
->centeringSpace
, cellY(0), d
->cellW
,
448 cellY(DISPLAYED_WEEKS
) - cellY(0) - d
->cellSpace
), "weeksColumn");
449 d
->svg
->paint(p
, QRectF(r
.x() + d
->centeringSpace
, r
.y(),
450 cellX(daysInWeek
) - r
.x() - d
->cellSpace
- d
->centeringSpace
, d
->headerHeight
), "weekDayHeader");
452 QList
<CalendarCellBorder
> borders
;
453 QList
<CalendarCellBorder
> hovers
;
454 QDate currentDate
= QDate::currentDate(); //FIXME: calendar timezone
456 //kDebug() << "exposed: " << option->exposedRect;
457 for (int week
= 0; week
< DISPLAYED_WEEKS
; week
++) {
458 for (int weekDay
= 0; weekDay
< daysInWeek
; weekDay
++) {
459 int x
= cellX(weekDay
);
462 QRectF
cellRect(x
, y
, d
->cellW
, d
->cellH
);
463 if (!cellRect
.intersects(option
->exposedRect
)) {
467 QDate
cellDate(d
->date
.year(), d
->date
.month(), (week
* 7) + (weekDay
+ 1));
468 CalendarTable::CellTypes
type(CalendarTable::NoType
);
470 int cell
= d
->cell(week
, weekDay
+ 1, &type
, cellDate
);
472 // check what kind of cell we are
473 if (cellDate
== currentDate
) {
477 if (cellDate
== d
->date
) {
481 if (type
!= CalendarTable::NoType
&& type
!= CalendarTable::NotInCurrentMonth
) {
482 borders
.append(CalendarCellBorder(cell
, week
, weekDay
, type
, cellDate
));
485 if (week
== d
->hoverWeek
&& weekDay
== d
->hoverDay
) {
487 hovers
.append(CalendarCellBorder(cell
, week
, weekDay
, type
, cellDate
));
490 paintCell(p
, cell
, week
, weekDay
, type
, cellDate
);
493 QRectF
cellRect(r
.x() + d
->centeringSpace
, y
, d
->cellW
, d
->cellH
);
494 p
->setPen(Theme::defaultTheme()->color(Plasma::Theme::TextColor
));
495 QFont font
= Theme::defaultTheme()->font(Plasma::Theme::DefaultFont
);
496 font
.setPixelSize(cellRect
.height() * 0.7);
498 p
->setOpacity(d
->opacity
);
499 p
->drawText(cellRect
, Qt::AlignCenter
, QString::number(d
->calendar
->weekNumber(cellDate
))); //draw number
506 if (option
->exposedRect
.intersects(QRect(r
.x(), r
.y(), r
.width(), d
->headerHeight
))) {
507 p
->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor
));
508 int weekStartDay
= d
->calendar
->weekStartDay();
509 for (int i
= 0; i
< daysInWeek
; i
++){
510 int weekDay
= ((i
+ weekStartDay
- 1) % daysInWeek
) + 1;
511 QString dayName
= d
->calendar
->weekDayName(weekDay
, KCalendarSystem::ShortDayName
);
512 QFont font
= Theme::defaultTheme()->font(Plasma::Theme::DefaultFont
);
513 font
.setPixelSize(d
->headerHeight
* 0.9);
515 p
->drawText(QRectF(cellX(i
), r
.y(), d
->cellW
, d
->headerHeight
),
516 Qt::AlignCenter
| Qt::AlignVCenter
, dayName
);
521 foreach (const CalendarCellBorder
&border
, hovers
) {
523 paintBorder(p
, border
.cell
, border
.week
, border
.weekDay
, border
.type
, border
.date
);
528 foreach (const CalendarCellBorder
&border
, borders
) {
530 paintBorder(p
, border
.cell
, border
.week
, border
.weekDay
, border
.type
, border
.date
);
537 p->drawRect(option->exposedRect.adjusted(1, 1, -2, -2));
544 #include "calendartable.moc"