not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / libs / plasmaclock / calendartable.cpp
blob9adb19f4634f91b7c1a43730c12509c1e8ea905d
1 /*
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"
22 //Qt
23 #include <QtCore/QDate>
24 #include <QtGui/QPainter>
25 #include <QtGui/QWidget>
26 #include <QtGui/QGraphicsSceneWheelEvent>
27 #include <QtGui/QStyleOptionGraphicsItem>
28 //KDECore
29 #include <kglobal.h>
30 #include <kdebug.h>
32 //Plasma
33 #include <Plasma/Svg>
34 #include <Plasma/Theme>
36 namespace Plasma
39 static const int DISPLAYED_WEEKS = 6;
41 class CalendarCellBorder
43 public:
44 CalendarCellBorder(int c, int w, int d, CalendarTable::CellTypes t, QDate dt)
45 : cell(c),
46 week(w),
47 weekDay(d),
48 type(t),
49 date(dt)
53 int cell;
54 int week;
55 int weekDay;
56 CalendarTable::CellTypes type;
57 QDate date;
60 class CalendarTablePrivate
62 public:
63 CalendarTablePrivate(CalendarTable *q, const QDate &cDate = QDate::currentDate())
65 svg = new Svg();
66 svg->setImagePath("widgets/calendar");
67 svg->setContainsMultipleImages(true);
69 calendar = KGlobal::locale()->calendar();
71 date = cDate;
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()
82 delete svg;
85 int firstMonthDayIndex(int y, int m)
87 QDate myDate;
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)
95 hoverDay = -1;
96 hoverWeek = -1;
98 if (hoverPoint.isNull()) {
99 return QRectF();
102 if (hoverPoint.x() < centeringSpace + cellW + weekBarSpace) {
103 // skip the weekbar
104 return QRectF();
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) {
111 return QRectF();
114 //FIXME: this should be a hint or something somewhere
115 hoverDay = x - 1;
116 hoverWeek = y;
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)
139 QDate myDate;
141 if ((week == 0) && (weekDay < firstMonthDayIndex(year, month))){
142 int prevMonth = (month == 1) ? 12 : month - 1;
143 calendar->setYMD(myDate, year, prevMonth, 1);
145 if (type) {
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);
151 } else {
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)) {
156 if (type) {
157 (*type) &= ~CalendarTable::NotInCurrentMonth;
160 calendar->setYMD(cellDate, year, month, day);
161 return day;
162 } else {
163 if (type) {
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);
173 Plasma::Svg *svg;
174 const KCalendarSystem *calendar;
175 QDate date;
176 QRectF hoverRect;
177 int month;
178 int year;
180 float opacity;
181 int hoverWeek;
182 int hoverDay;
183 int centeringSpace;
184 int cellW;
185 int cellH;
186 int cellSpace;
187 int headerHeight;
188 int headerSpace;
189 int weekBarSpace;
190 int glowRadius;
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()
206 delete d;
209 const KCalendarSystem *CalendarTable::calendar() const
211 return d->calendar;
214 bool CalendarTable::setCalendar(KCalendarSystem *calendar)
216 d->calendar = calendar;
217 return false;
220 bool CalendarTable::setDate(const QDate &date)
222 int oldYear = d->year;
223 int oldMonth = d->month;
224 QDate oldDate = d->date;
225 d->date = 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);
232 fullUpdate = true;
235 if (oldMonth != d->month){
236 emit displayedMonthChanged(d->year, d->month);
237 fullUpdate = true;
240 d->updateHoveredPainting(this, QPointF());
242 if (fullUpdate) {
243 update();
244 } else {
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);
251 if (x == 0) {
252 x = 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;
260 if (x == 0) {
261 x = 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);
268 return false;
271 const QDate& CalendarTable::date() const
273 return d->date;
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)
290 Q_UNUSED(event);
292 if (event->delta() < 0) {
293 if (d->month == 12) {
294 d->month = 1;
295 d->year++;
296 emit displayedYearChanged(d->year, d->month);
297 } else {
298 d->month++;
301 emit displayedMonthChanged(d->year, d->month);
302 } else if (event->delta() > 0) {
303 if (d->month == 1) {
304 d->month = 12;
305 d->year--;
306 emit displayedYearChanged(d->year, d->month);
307 } else {
308 d->month--;
311 emit displayedMonthChanged(d->year, d->month);
314 update();
317 void CalendarTable::mousePressEvent(QGraphicsSceneMouseEvent *event)
319 event->accept();
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)){
324 int week = -1;
325 int weekDay = -1;
326 QDate cellDate;
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))
330 weekDay = i;
333 for (int i = 0; i < DISPLAYED_WEEKS; i++) {
334 if ((event->pos().y() >= cellY(i)) && (event->pos().y() <= cellY(i + 1) - d->cellSpace))
335 week = i;
338 if ((week >= 0) && (weekDay >= 0)) {
339 d->hoverDay = -1;
340 d->hoverWeek = -1;
341 QDate tmpDate;
342 QDate oldDate = d->date;
343 d->cell(week, weekDay + 1, 0, tmpDate);
345 if (tmpDate == oldDate) {
346 return;
349 setDate(tmpDate);
350 emit dateChanged(tmpDate, oldDate);
351 emit dateChanged(tmpDate);
356 void CalendarTable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
358 mousePressEvent(event);
361 void CalendarTable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
363 Q_UNUSED(event);
365 emit tableClicked();
368 void CalendarTable::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
370 d->updateHoveredPainting(this, event->pos());
373 void CalendarTable::resizeEvent(QGraphicsSceneResizeEvent * event)
375 Q_UNUSED(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)
395 Q_UNUSED(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);
409 font.setBold(true);
410 font.setPixelSize(cellArea.height() * 0.7);
411 p->setFont(font);
412 p->drawText(cellArea, Qt::AlignCenter, QString::number(cell), &cellArea); //draw number
413 p->setOpacity(1.0);
416 void CalendarTable::paintBorder(QPainter *p, int cell, int week, int weekDay, CellTypes type, const QDate &cellDate)
418 Q_UNUSED(cell);
419 Q_UNUSED(cellDate);
421 if (type & Hovered) {
422 d->svg->paint(p, QRect(cellX(weekDay), cellY(week), d->cellW, d->cellH), "hoverHighlight");
425 QString elementId;
427 if (type & Today) {
428 elementId = "today";
429 } else if (type & Selected) {
430 elementId = "selected";
431 } else {
432 return;
435 d->svg->paint(p, QRectF(cellX(weekDay) - 1, cellY(week) - 1,
436 d->cellW + 1, d->cellH + 2),
437 elementId);
440 void CalendarTable::paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget)
442 Q_UNUSED(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);
460 int y = cellY(week);
462 QRectF cellRect(x, y, d->cellW, d->cellH);
463 if (!cellRect.intersects(option->exposedRect)) {
464 continue;
467 QDate cellDate(d->date.year(), d->date.month(), (week * 7) + (weekDay + 1));
468 CalendarTable::CellTypes type(CalendarTable::NoType);
469 // get cell info
470 int cell = d->cell(week, weekDay + 1, &type, cellDate);
472 // check what kind of cell we are
473 if (cellDate == currentDate) {
474 type |= Today;
477 if (cellDate == d->date) {
478 type |= Selected;
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) {
486 type |= Hovered;
487 hovers.append(CalendarCellBorder(cell, week, weekDay, type, cellDate));
490 paintCell(p, cell, week, weekDay, type, cellDate);
492 if (weekDay == 0) {
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);
497 p->setFont(font);
498 p->setOpacity(d->opacity);
499 p->drawText(cellRect, Qt::AlignCenter, QString::number(d->calendar->weekNumber(cellDate))); //draw number
500 p->setOpacity(1.0);
505 // Draw days
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);
514 p->setFont(font);
515 p->drawText(QRectF(cellX(i), r.y(), d->cellW, d->headerHeight),
516 Qt::AlignCenter | Qt::AlignVCenter, dayName);
520 // Draw hovers
521 foreach (const CalendarCellBorder &border, hovers) {
522 p->save();
523 paintBorder(p, border.cell, border.week, border.weekDay, border.type, border.date);
524 p->restore();
527 // Draw borders
528 foreach (const CalendarCellBorder &border, borders) {
529 p->save();
530 paintBorder(p, border.cell, border.week, border.weekDay, border.type, border.date);
531 p->restore();
535 p->save();
536 p->setPen(Qt::red);
537 p->drawRect(option->exposedRect.adjusted(1, 1, -2, -2));
538 p->restore();
542 } //namespace Plasma
544 #include "calendartable.moc"