~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to libs/plasmaclock/calendartable.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *   Copyright 2008,2010 Davide Bettio <davide.bettio@kdemail.net>
 
3
 *   Copyright 2009 John Layt <john@layt.net>
 
4
 *
 
5
 *   This program is free software; you can redistribute it and/or modify
 
6
 *   it under the terms of the GNU Library General Public License as
 
7
 *   published by the Free Software Foundation; either version 2, or
 
8
 *   (at your option) any later version.
 
9
 *
 
10
 *   This program is distributed in the hope that it will be useful,
 
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *   GNU General Public License for more details
 
14
 *
 
15
 *   You should have received a copy of the GNU Library General Public
 
16
 *   License along with this program; if not, write to the
 
17
 *   Free Software Foundation, Inc.,
 
18
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
19
 */
 
20
 
 
21
#include "calendartable.h"
 
22
#include "config-calendartable.h"
 
23
 
 
24
//Qt
 
25
#include <QtCore/QDate>
 
26
#include <QtCore/QListIterator>
 
27
#include <QtCore/QTimer>
 
28
#include <QtGui/QPainter>
 
29
#include <QtGui/QWidget>
 
30
#include <QtGui/QGraphicsSceneWheelEvent>
 
31
#include <QtGui/QStyleOptionGraphicsItem>
 
32
 
 
33
//KDECore
 
34
#include <KGlobal>
 
35
#include <KDateTime>
 
36
#include <KDebug>
 
37
#include <KConfigDialog>
 
38
#include <KConfigGroup>
 
39
 
 
40
//Plasma
 
41
#include <Plasma/Svg>
 
42
#include <Plasma/Theme>
 
43
#include <Plasma/DataEngine>
 
44
#include <Plasma/DataEngineManager>
 
45
 
 
46
#ifdef HAVE_KDEPIMLIBS
 
47
#include "ui_calendarHolidaysConfig.h"
 
48
#else
 
49
#include "ui_calendarConfig.h"
 
50
#endif
 
51
 
 
52
#include <cmath>
 
53
 
 
54
namespace Plasma
 
55
{
 
56
 
 
57
static const int DISPLAYED_WEEKS = 6;
 
58
 
 
59
class CalendarCellBorder
 
60
{
 
61
public:
 
62
    CalendarCellBorder(int c, int w, int d, CalendarTable::CellTypes t, QDate dt)
 
63
        : cell(c),
 
64
          week(w),
 
65
          weekDay(d),
 
66
          type(t),
 
67
          date(dt)
 
68
    {
 
69
    }
 
70
 
 
71
    int cell;
 
72
    int week;
 
73
    int weekDay;
 
74
    CalendarTable::CellTypes type;
 
75
    QDate date;
 
76
};
 
77
 
 
78
class CalendarTablePrivate
 
79
{
 
80
    public:
 
81
        enum Populations { NoPendingPopulation = 0, PopulateHolidays = 1, PopulateEvents = 2 };
 
82
 
 
83
        CalendarTablePrivate(CalendarTable *calTable, const QDate &initialDate = QDate::currentDate())
 
84
            : q(calTable),
 
85
              calendarType("locale"),
 
86
              calendar(KGlobal::locale()->calendar()),
 
87
              displayEvents(false),
 
88
              displayHolidays(false),
 
89
              calendarDataEngine(0),
 
90
              automaticUpdates(true),
 
91
              opacity(0.5),
 
92
              pendingPopulations(NoPendingPopulation),
 
93
              delayedPopulationTimer(new QTimer())
 
94
        {
 
95
            KGlobal::locale()->insertCatalog("libkholidays");
 
96
 
 
97
            svg = new Svg();
 
98
            svg->setImagePath("widgets/calendar");
 
99
            svg->setContainsMultipleImages(true);
 
100
 
 
101
            delayedPopulationTimer->setInterval(0);
 
102
            delayedPopulationTimer->setSingleShot(true);
 
103
            QObject::connect(delayedPopulationTimer, SIGNAL(timeout()), q, SLOT(populateCalendar()));
 
104
 
 
105
            setDate(initialDate);
 
106
        }
 
107
 
 
108
        ~CalendarTablePrivate()
 
109
        {
 
110
            // Delete the old calendar first if it's not the global calendar
 
111
            if (calendar != KGlobal::locale()->calendar()) {
 
112
                delete calendar;
 
113
            }
 
114
 
 
115
            if (calendarDataEngine) {
 
116
                Plasma::DataEngineManager::self()->unloadEngine("calendar");
 
117
            }
 
118
 
 
119
            delete svg;
 
120
            delete delayedPopulationTimer;
 
121
        }
 
122
 
 
123
        void setCalendar(const KCalendarSystem *newCalendar)
 
124
        {
 
125
            // If not the global calendar, delete the old calendar first
 
126
            if (calendar != KGlobal::locale()->calendar()) {
 
127
                delete calendar;
 
128
            }
 
129
 
 
130
            calendar = newCalendar;
 
131
 
 
132
            if (calendar == KGlobal::locale()->calendar()) {
 
133
                calendarType = "locale";
 
134
            } else {
 
135
                calendarType = calendar->calendarType();
 
136
            }
 
137
 
 
138
            // Force date update to refresh cached date componants then update display
 
139
            setDate(selectedDate);
 
140
            updateHoveredPainting(QPointF());
 
141
            populateHolidays();
 
142
            populateEvents();
 
143
            q->update();
 
144
        }
 
145
 
 
146
        void setDate(const QDate &setDate)
 
147
        {
 
148
            selectedDate = setDate;
 
149
            selectedMonth = calendar->month(setDate);
 
150
            selectedYear = calendar->year(setDate);
 
151
            weekDayFirstOfSelectedMonth = weekDayFirstOfMonth(setDate);
 
152
            daysInWeek = calendar->daysInWeek(setDate);
 
153
            daysInSelectedMonth = calendar->daysInMonth(setDate);
 
154
            daysShownInPrevMonth = (weekDayFirstOfSelectedMonth - calendar->weekStartDay() + daysInWeek) % daysInWeek;
 
155
            // make sure at least one day of the previous month is visible.
 
156
            // 1 = minimum number of days to show, increase if more days should be forced visible:
 
157
            if (daysShownInPrevMonth < 1) {
 
158
                daysShownInPrevMonth += daysInWeek;
 
159
            }
 
160
            viewStartDate = dateFromRowColumn(0, 0);
 
161
            viewEndDate = dateFromRowColumn(DISPLAYED_WEEKS - 1, daysInWeek - 1);
 
162
        }
 
163
 
 
164
        //Returns the x co-ordinate of a given column to LTR order, column is 0 to (daysInWeek-1)
 
165
        //This version does not adjust for RTL, so should not be used directly for drawing
 
166
        int columnToX(int column)
 
167
        {
 
168
            return q->boundingRect().x() +
 
169
                   centeringSpace +
 
170
                   weekBarSpace +
 
171
                   cellW +
 
172
                   ((cellW + cellSpace) * column);
 
173
        }
 
174
 
 
175
        //Returns the y co-ordinate for given row, row is 0 to (DISPLAYED_WEEKS - 1)
 
176
        int rowToY(int row)
 
177
        {
 
178
            return (int) q->boundingRect().y() +
 
179
                         headerHeight +
 
180
                         headerSpace +
 
181
                         ((cellH + cellSpace) * row);
 
182
        }
 
183
 
 
184
        //Returns the absolute LTR column for a given x co-ordinate, -1 if outside table
 
185
        int xToColumn(qreal x)
 
186
        {
 
187
            if (x >= columnToX(0) && x < columnToX(daysInWeek)) {
 
188
                return ((x - centeringSpace) / (cellW + cellSpace)) - 1;
 
189
            }
 
190
            return -1;
 
191
        }
 
192
 
 
193
        //Returns the absolute row for a given y co-ordinate, -1 if outside table
 
194
        int yToRow(qreal y)
 
195
        {
 
196
            if (y >= rowToY(0) && y < rowToY(DISPLAYED_WEEKS)) {
 
197
                return (y - headerHeight - headerSpace) / (cellH + cellSpace);
 
198
            }
 
199
            return -1;
 
200
        }
 
201
 
 
202
        //Convert between column and weekdayColumn depending on LTR or RTL mode
 
203
        //Note the same calculation used in both directions
 
204
        int adjustColumn(int column)
 
205
        {
 
206
            if (column >= 0 && column < daysInWeek) {
 
207
                if (q->layoutDirection() == Qt::RightToLeft) {
 
208
                    return daysInWeek - column - 1;
 
209
                } else {
 
210
                    return column;
 
211
                }
 
212
            }
 
213
            return -1;
 
214
        }
 
215
 
 
216
        //Given an x y point in the table return the cell date.
 
217
        //Note can be an invalid date in the calendar system
 
218
        QDate dateFromPoint(QPointF point)
 
219
        {
 
220
            if (point.isNull()) {
 
221
                return QDate();
 
222
            }
 
223
 
 
224
            int column = xToColumn(point.x());
 
225
            int row = yToRow(point.y());
 
226
 
 
227
            if (column < 0 || column >= daysInWeek || row < 0 || row >= DISPLAYED_WEEKS) {
 
228
                return QDate();
 
229
            }
 
230
 
 
231
            return dateFromRowColumn(row, adjustColumn(column));
 
232
        }
 
233
 
 
234
        //Given a date in the currently selected month, return the position in the table as a
 
235
        //row and column. Note no direction is assumed
 
236
        void rowColumnFromDate(const QDate &cellDate, int &weekRow, int &weekdayColumn)
 
237
        {
 
238
            int offset = calendar->day(cellDate) + daysShownInPrevMonth - 1;
 
239
            weekRow = offset / daysInWeek;
 
240
            weekdayColumn = offset % daysInWeek;
 
241
        }
 
242
 
 
243
        //Given a position in the table as a 0-indexed row and column, return the cell date.  Makes
 
244
        //no assumption about direction.  Date returned can be an invalid date in the calendar
 
245
        //system, or simply invalid.
 
246
        QDate dateFromRowColumn(int weekRow, int weekdayColumn)
 
247
        {
 
248
            QDate cellDate;
 
249
 
 
250
            //starting from the first of the month, which is known to always be valid, add/subtract
 
251
            //number of days to get to the required cell
 
252
            if (calendar->setYMD(cellDate, selectedYear, selectedMonth, 1)) {
 
253
                cellDate = calendar->addDays(cellDate, (weekRow * daysInWeek) + weekdayColumn - daysShownInPrevMonth);
 
254
            }
 
255
 
 
256
            return cellDate;
 
257
        }
 
258
 
 
259
        void updateHoveredPainting(const QPointF &hoverPoint)
 
260
        {
 
261
            QRectF oldHoverRect = hoverRect;
 
262
            hoverRect = QRectF();
 
263
            hoverWeekdayColumn = -1;
 
264
            hoverWeekRow = -1;
 
265
 
 
266
            if (!hoverPoint.isNull()) {
 
267
                int column = xToColumn(hoverPoint.x());
 
268
                int row = yToRow(hoverPoint.y());
 
269
 
 
270
                if (column >= 0 && column < daysInWeek && row >= 0 && row < DISPLAYED_WEEKS) {
 
271
                    hoverRect = QRectF(columnToX(column) - glowRadius,
 
272
                                       rowToY(row) - glowRadius,
 
273
                                       cellW + glowRadius * 2,
 
274
                                       cellH + glowRadius * 2).adjusted(-2,-2,2,2);
 
275
                    hoverWeekdayColumn = adjustColumn(column);
 
276
                    hoverWeekRow = row;
 
277
                }
 
278
            }
 
279
 
 
280
            // now update what is needed, and only what is needed!
 
281
            if (hoverRect != oldHoverRect) {
 
282
                //FIXME: update only of a piece seems to paint over the old stuff
 
283
                /*if (oldHoverRect.isValid()) {
 
284
                    q->update(oldHoverRect);
 
285
                }
 
286
                if (hoverRect.isValid()) {
 
287
                    q->update(hoverRect);
 
288
                }*/
 
289
                emit q->dateHovered(dateFromRowColumn(hoverWeekRow, hoverWeekdayColumn));
 
290
                q->update();
 
291
            }
 
292
        }
 
293
 
 
294
        // calculate weekday number of first day of this month, this is the anchor for all calculations
 
295
        int weekDayFirstOfMonth(const QDate &cellDate)
 
296
        {
 
297
            Q_UNUSED(cellDate);
 
298
            QDate firstDayOfMonth;
 
299
            int weekday = -1;
 
300
            if ( calendar->setYMD(firstDayOfMonth, selectedYear, selectedMonth, 1)) {
 
301
                weekday = calendar->dayOfWeek(firstDayOfMonth);
 
302
            }
 
303
            return weekday;
 
304
        }
 
305
 
 
306
        QString defaultHolidaysRegion()
 
307
        {
 
308
            //FIXME: get rid of the query; turn it into a proper async request
 
309
            return calendarEngine()->query("holidaysDefaultRegion").value("holidaysDefaultRegion").toString();
 
310
        }
 
311
 
 
312
        bool isValidHolidaysRegion(const QString &holidayRegion)
 
313
        {
 
314
            //FIXME: get rid of the query; turn it into a proper async request
 
315
            QString queryString = "holidaysIsValidRegion" + QString(':') + holidayRegion;
 
316
            return calendarEngine()->query(queryString).value(queryString).toBool();
 
317
        }
 
318
 
 
319
        bool addHolidaysRegion(const QString &holidayRegion, bool daysOff)
 
320
        {
 
321
            //kDebug() << holidayRegion << holidaysRegions.contains(holidayRegion) << isValidHolidaysRegion(holidayRegion);
 
322
            if (!holidaysRegions.contains(holidayRegion) && isValidHolidaysRegion(holidayRegion)) {
 
323
                QString queryString = "holidaysRegion" + QString(':') + holidayRegion;
 
324
                Plasma::DataEngine::Data regions = calendarEngine()->query(queryString);
 
325
                Plasma::DataEngine::DataIterator it(regions);
 
326
                while (it.hasNext()) {
 
327
                    it.next();
 
328
                    Plasma::DataEngine::Data region = it.value().toHash();
 
329
                    region.insert("UseForDaysOff", daysOff);
 
330
                    holidaysRegions.insert(it.key(), region);
 
331
                }
 
332
 
 
333
                return true;
 
334
            }
 
335
 
 
336
            return false;
 
337
        }
 
338
 
 
339
        bool holidayIsDayOff(Plasma::DataEngine::Data holidayData)
 
340
        {
 
341
            return (holidayData.value("ObservanceType").toString() == "PublicHoliday" &&
 
342
                    holidaysRegions.value(holidayData.value("RegionCode").toString()).value("UseForDaysOff").toBool());
 
343
        }
 
344
 
 
345
 
 
346
        void insertPimOccurence(const QString &type, const QDate &date, Plasma::DataEngine::Data occurrence)
 
347
        {
 
348
            if (date >= viewStartDate && date <= viewEndDate) {
 
349
                int julian = date.toJulianDay();
 
350
                if (type == "Event" && !events.contains(julian, occurrence)) {
 
351
                    events.insert(julian, occurrence);
 
352
                } else if (type == "Todo" && !todos.contains(julian, occurrence)) {
 
353
                    todos.insert(julian, occurrence);
 
354
                } else if (type == "Journal" && !journals.contains(julian, occurrence)) {
 
355
                    journals.insert(julian, occurrence);
 
356
                }
 
357
            }
 
358
        }
 
359
 
 
360
        Plasma::DataEngine *calendarEngine();
 
361
        void checkIfCalendarEngineNeeded();
 
362
        void populateHolidays();
 
363
        void populateEvents();
 
364
        void populateCalendar();
 
365
 
 
366
        CalendarTable *q;
 
367
        QString calendarType;
 
368
        const KCalendarSystem *calendar;
 
369
 
 
370
        QDate selectedDate;
 
371
        QDate currentDate;
 
372
        int selectedMonth;
 
373
        int selectedYear;
 
374
        int weekDayFirstOfSelectedMonth;
 
375
        int daysInWeek;
 
376
        int daysInSelectedMonth;
 
377
        int daysShownInPrevMonth;
 
378
        QDate viewStartDate;
 
379
        QDate viewEndDate;
 
380
 
 
381
        bool displayEvents;
 
382
        bool displayHolidays;
 
383
        Plasma::DataEngine *calendarDataEngine;
 
384
        // List of Holiday Regions to display
 
385
        // Hash key: QString = Holiday Region Code, Data = details of Holiday Region
 
386
        QHash<QString, Plasma::DataEngine::Data> holidaysRegions;
 
387
        // Index to Holidays that occur on a given date.
 
388
        // Hash key: int = Julian Day number of holiday observance, int = key of holiday event
 
389
        QMultiHash<int, int> holidays;
 
390
        // Holiday details.  A holiday may be observed on multiple dates.
 
391
        // Hash key: int = key of holiday event, Data = details of holiday
 
392
        QHash<int, Plasma::DataEngine::Data> holidayEvents;
 
393
        // Index to Events/Todos/Journals that occur on a given date.
 
394
        // Hash key: int = Julian Day number of event/todo occurrence, Data = occurence details including start date, end date and Akonadi incidence UID
 
395
        QMultiHash<int, Plasma::DataEngine::Data> events;
 
396
        QMultiHash<int, Plasma::DataEngine::Data> todos;
 
397
        QMultiHash<int, Plasma::DataEngine::Data> journals;
 
398
        // Event/Todo/Journal details.  An event may recur on multiple dates.
 
399
        // Hash key: QString = Akonadi UID of event/todo/journal, Data = details of event/todo/journal
 
400
        QHash<QString, Plasma::DataEngine::Data> pimEvents;
 
401
        QString eventsQuery;
 
402
 
 
403
        bool automaticUpdates;
 
404
 
 
405
        QPointF lastSeenMousePos;
 
406
 
 
407
#ifdef HAVE_KDEPIMLIBS
 
408
        Ui::calendarHolidaysConfig calendarConfigUi;
 
409
#else
 
410
        Ui::calendarConfig calendarConfigUi;
 
411
#endif
 
412
 
 
413
        Plasma::Svg *svg;
 
414
        float opacity; //transparency for the inactive text
 
415
        QRectF hoverRect;
 
416
        int hoverWeekRow;
 
417
        int hoverWeekdayColumn;
 
418
        int centeringSpace;
 
419
        int cellW;
 
420
        int cellH;
 
421
        int cellSpace;
 
422
        int headerHeight;
 
423
        int headerSpace;
 
424
        int weekBarSpace;
 
425
        int glowRadius;
 
426
 
 
427
        int pendingPopulations;
 
428
        QTimer *delayedPopulationTimer;
 
429
};
 
430
 
 
431
CalendarTable::CalendarTable(const QDate &date, QGraphicsWidget *parent)
 
432
    : QGraphicsWidget(parent), d(new CalendarTablePrivate(this, date))
 
433
{
 
434
    setAcceptHoverEvents(true);
 
435
    setCacheMode(QGraphicsItem::DeviceCoordinateCache);
 
436
}
 
437
 
 
438
CalendarTable::CalendarTable(QGraphicsWidget *parent)
 
439
    : QGraphicsWidget(parent), d(new CalendarTablePrivate(this))
 
440
{
 
441
    setAcceptHoverEvents(true);
 
442
    setCacheMode(QGraphicsItem::DeviceCoordinateCache);
 
443
}
 
444
 
 
445
CalendarTable::~CalendarTable()
 
446
{
 
447
    delete d;
 
448
}
 
449
 
 
450
void CalendarTable::setCalendar(const QString &newCalendarType)
 
451
{
 
452
    if (newCalendarType == d->calendarType) {
 
453
        return;
 
454
    }
 
455
 
 
456
    if (newCalendarType == "locale") {
 
457
        d->setCalendar(KGlobal::locale()->calendar());
 
458
    } else {
 
459
        d->setCalendar(KCalendarSystem::create(newCalendarType));
 
460
    }
 
461
 
 
462
    // Signal out date change so any dependents will update as well
 
463
    emit dateChanged(date(), date());
 
464
    emit dateChanged(date());
 
465
}
 
466
 
 
467
void CalendarTable::setCalendar(const KCalendarSystem *newCalendar)
 
468
{
 
469
    if (newCalendar == d->calendar) {
 
470
        return;
 
471
    }
 
472
 
 
473
    d->setCalendar(newCalendar);
 
474
 
 
475
    // Signal out date change so any dependents will update as well
 
476
    emit dateChanged(date(), date());
 
477
    emit dateChanged(date());
 
478
}
 
479
 
 
480
const KCalendarSystem *CalendarTable::calendar() const
 
481
{
 
482
    return d->calendar;
 
483
}
 
484
 
 
485
void CalendarTable::setDate(const QDate &newDate)
 
486
{
 
487
    // New date must be valid in the current calendar system
 
488
    if (!calendar()->isValid(newDate)) {
 
489
        return;
 
490
    }
 
491
 
 
492
    // If new date is the same as old date don't actually need to do anything
 
493
    if (newDate == date()) {
 
494
        return;
 
495
    }
 
496
 
 
497
    int oldYear = d->selectedYear;
 
498
    int oldMonth = d->selectedMonth;
 
499
    QDate oldDate = date();
 
500
 
 
501
    // now change the date
 
502
    d->setDate(newDate);
 
503
 
 
504
    d->updateHoveredPainting(d->lastSeenMousePos);
 
505
    update(); //FIXME: we shouldn't need this update here, but something in Qt is broken (but with plasmoidviewer everything work)
 
506
 
 
507
    if (oldYear != d->selectedYear || oldMonth != d->selectedMonth) {
 
508
        d->populateHolidays();
 
509
        d->populateEvents();
 
510
    } else {
 
511
        // only update the old and the new areas
 
512
        int row, column;
 
513
        d->rowColumnFromDate(oldDate, row, column);
 
514
        update(cellX(column) - d->glowRadius, cellY(row) - d->glowRadius,
 
515
               d->cellW + d->glowRadius * 2, d->cellH + d->glowRadius * 2);
 
516
 
 
517
        d->rowColumnFromDate(newDate, row, column);
 
518
        update(cellX(column) - d->glowRadius, cellY(row) - d->glowRadius,
 
519
               d->cellW + d->glowRadius * 2, d->cellH + d->glowRadius * 2);
 
520
    }
 
521
 
 
522
    emit dateChanged(newDate, oldDate);
 
523
    emit dateChanged(newDate);
 
524
}
 
525
 
 
526
const QDate& CalendarTable::date() const
 
527
{
 
528
    return d->selectedDate;
 
529
}
 
530
 
 
531
void CalendarTable::setDisplayHolidays(bool showHolidays)
 
532
{
 
533
    if (showHolidays) {
 
534
        if (d->holidaysRegions.isEmpty()) {
 
535
            d->addHolidaysRegion(d->defaultHolidaysRegion(), true);
 
536
        }
 
537
 
 
538
        QMutableHashIterator<QString, Plasma::DataEngine::Data> it(d->holidaysRegions);
 
539
        while (it.hasNext()) {
 
540
            it.next();
 
541
            if (!d->isValidHolidaysRegion(it.key())) {
 
542
                it.remove();
 
543
            }
 
544
        }
 
545
    } else {
 
546
        clearHolidays();
 
547
        d->checkIfCalendarEngineNeeded();
 
548
    }
 
549
 
 
550
    if (d->displayHolidays != showHolidays) {
 
551
        d->displayHolidays = showHolidays;
 
552
        d->populateHolidays();
 
553
    }
 
554
}
 
555
 
 
556
bool CalendarTable::displayHolidays()
 
557
{
 
558
    return d->displayHolidays && !d->holidaysRegions.isEmpty();
 
559
}
 
560
 
 
561
bool CalendarTable::displayEvents()
 
562
{
 
563
    return d->displayEvents;
 
564
}
 
565
 
 
566
void CalendarTable::setDisplayEvents(bool display)
 
567
{
 
568
    if (d->displayEvents == display) {
 
569
        return;
 
570
    }
 
571
 
 
572
    d->displayEvents = display;
 
573
    if (display) {
 
574
        d->populateEvents();
 
575
    } else {
 
576
        if (d->calendarDataEngine) {
 
577
            d->calendarDataEngine->disconnectSource(d->eventsQuery, this);
 
578
        }
 
579
        d->events.clear();
 
580
        d->todos.clear();
 
581
        d->journals.clear();
 
582
        d->pimEvents.clear();
 
583
        d->checkIfCalendarEngineNeeded();
 
584
    }
 
585
}
 
586
 
 
587
void CalendarTable::clearHolidaysRegions()
 
588
{
 
589
    d->holidaysRegions.clear();
 
590
    clearHolidays();
 
591
}
 
592
 
 
593
void CalendarTable::addHolidaysRegion(const QString &region, bool daysOff)
 
594
{
 
595
    if (d->displayHolidays && d->addHolidaysRegion(region, daysOff)) {
 
596
        d->populateHolidays();
 
597
    }
 
598
}
 
599
 
 
600
QStringList CalendarTable::holidaysRegions() const
 
601
{
 
602
    return d->holidaysRegions.keys();
 
603
}
 
604
 
 
605
QStringList CalendarTable::holidaysRegionsDaysOff() const
 
606
{
 
607
    QStringList regions;
 
608
    QHashIterator<QString, Plasma::DataEngine::Data> it(d->holidaysRegions);
 
609
    while (it.hasNext()) {
 
610
        it.next();
 
611
        if (it.value().value("UseForDaysOff").toBool()) {
 
612
            regions.append(it.key());
 
613
        }
 
614
    }
 
615
    return regions;
 
616
}
 
617
 
 
618
void CalendarTable::clearHolidays()
 
619
{
 
620
    d->holidayEvents.clear();
 
621
    d->holidays.clear();
 
622
    update();
 
623
}
 
624
 
 
625
void CalendarTable::addHoliday(Plasma::DataEngine::Data holidayData)
 
626
{
 
627
    QDate holidayStartDate = QDate::fromString(holidayData.value("ObservanceStartDate").toString(), Qt::ISODate);
 
628
    int holidayDuration = holidayData.value("ObservanceDuration").toInt();
 
629
    int uid = d->holidayEvents.count();
 
630
    d->holidayEvents.insert(uid, holidayData);
 
631
    for (int i = 0; i < holidayDuration; ++i) {
 
632
        d->holidays.insertMulti(holidayStartDate.addDays(i).toJulianDay(), uid);
 
633
    }
 
634
}
 
635
 
 
636
bool CalendarTable::dateHasDetails(const QDate &date) const
 
637
{
 
638
    int julian = date.toJulianDay();
 
639
    return d->holidays.contains(julian) ||
 
640
           d->events.contains(julian) ||
 
641
           d->todos.contains(julian) ||
 
642
           d->journals.contains(julian);
 
643
}
 
644
 
 
645
QStringList CalendarTable::dateDetails(const QDate &date) const
 
646
{
 
647
    QStringList details;
 
648
    const int julian = date.toJulianDay();
 
649
 
 
650
    if (d->holidays.contains(julian)) {
 
651
        foreach (int holidayUid, d->holidays.values(julian)) {
 
652
            Plasma::DataEngine::Data holidayData = d->holidayEvents.value(holidayUid);
 
653
            QString region = holidayData.value("RegionCode").toString();
 
654
 
 
655
            if (d->holidayIsDayOff(holidayData)) {
 
656
                details << i18nc("Day off: Holiday name (holiday region)",
 
657
                                 "<i>Holiday</i>: %1 (%2)",
 
658
                                 holidayData.value("Name").toString(),
 
659
                                 d->holidaysRegions.value(region).value("Name").toString());
 
660
            } else {
 
661
                QString region = holidayData.value("RegionCode").toString();
 
662
                details << i18nc("Not day off: Holiday name (holiday region)",
 
663
                                    "%1 (%2)",
 
664
                                    holidayData.value("Name").toString(),
 
665
                                    d->holidaysRegions.value(region).value("Name").toString());
 
666
            }
 
667
        }
 
668
    }
 
669
 
 
670
    if (d->events.contains(julian)) {
 
671
        foreach (const Plasma::DataEngine::Data &occurrence, d->events.values(julian)) {
 
672
            details << i18n("<i>Event</i>: %1", buildOccurrenceDescription(occurrence));
 
673
        }
 
674
    }
 
675
 
 
676
    if (d->todos.contains(julian)) {
 
677
        foreach (const Plasma::DataEngine::Data &occurrence, d->todos.values(julian)) {
 
678
            //TODO add Status and Percentage Complete when strings not frozen
 
679
            details << i18n("<i>Todo</i>: %1", buildOccurrenceDescription(occurrence));
 
680
        }
 
681
    }
 
682
 
 
683
    return details;
 
684
}
 
685
 
 
686
QString CalendarTable::buildOccurrenceDescription(const Plasma::DataEngine::Data &occurrence) const
 
687
{
 
688
    const QString uid = occurrence.value("OccurrenceUid").toString();
 
689
    const KDateTime occStartDate = occurrence.value("OccurrenceStartDate").value<KDateTime>();
 
690
    const KDateTime occEndDate = occurrence.value("OccurrenceEndDate").value<KDateTime>();
 
691
    const Plasma::DataEngine::Data details = d->pimEvents.value(uid);
 
692
 
 
693
    if (details.value("AllDay").toBool()) {
 
694
        return i18nc("All-day calendar event summary", "<br>%1", details.value("Summary").toString());
 
695
    } else if (!occEndDate.isValid() || occStartDate == occEndDate) {
 
696
        return i18nc("Time and summary for a calendarevent", "%1<br>%2",
 
697
                     KGlobal::locale()->formatTime(occStartDate.time()),
 
698
                     details.value("Summary").toString());
 
699
    }
 
700
 
 
701
    return i18nc("Start and end time and summary for a calendar event", "%1 - %2<br>%3",
 
702
                 KGlobal::locale()->formatTime(occStartDate.time()),
 
703
                 KGlobal::locale()->formatTime(occEndDate.time()),
 
704
                 details.value("Summary").toString());
 
705
}
 
706
 
 
707
void CalendarTable::setAutomaticUpdateEnabled(bool enabled)
 
708
{
 
709
    d->automaticUpdates = enabled;
 
710
}
 
711
 
 
712
bool CalendarTable::isAutomaticUpdateEnabled() const
 
713
{
 
714
    return d->automaticUpdates;
 
715
}
 
716
 
 
717
void CalendarTable::setCurrentDate(const QDate &date)
 
718
{
 
719
    d->currentDate = date;
 
720
}
 
721
 
 
722
const QDate& CalendarTable::currentDate() const
 
723
{
 
724
    return d->currentDate;
 
725
}
 
726
 
 
727
QDate CalendarTable::startDate() const
 
728
{
 
729
    return d->viewStartDate;
 
730
}
 
731
 
 
732
QDate CalendarTable::endDate() const
 
733
{
 
734
    return d->viewEndDate;
 
735
}
 
736
 
 
737
Plasma::DataEngine *CalendarTablePrivate::calendarEngine()
 
738
{
 
739
    if (!calendarDataEngine) {
 
740
        calendarDataEngine = Plasma::DataEngineManager::self()->loadEngine("calendar");
 
741
    }
 
742
 
 
743
    return calendarDataEngine;
 
744
}
 
745
 
 
746
void CalendarTablePrivate::checkIfCalendarEngineNeeded()
 
747
{
 
748
    if (calendarDataEngine && !displayHolidays && !displayEvents) {
 
749
        calendarDataEngine = Plasma::DataEngineManager::self()->loadEngine("calendar");
 
750
    }
 
751
}
 
752
 
 
753
void CalendarTablePrivate::populateHolidays()
 
754
{
 
755
    pendingPopulations |= PopulateHolidays;
 
756
    delayedPopulationTimer->start();
 
757
}
 
758
 
 
759
void CalendarTablePrivate::populateCalendar()
 
760
{
 
761
    if (pendingPopulations & PopulateHolidays) {
 
762
        holidayEvents.clear();
 
763
        holidays.clear();
 
764
 
 
765
        if (q->displayHolidays()) {
 
766
            // Just fetch the days displayed in the grid
 
767
            //FIXME: queries are bad, use an async method
 
768
            QString queryString = "holidays" + QString(':') +
 
769
                                  q->holidaysRegions().join(QString(',')) +
 
770
                                  QString(':') + viewStartDate.toString(Qt::ISODate) +
 
771
                                  QString(':') + viewEndDate.toString(Qt::ISODate);
 
772
            QList<QVariant> holidays = calendarEngine()->query(queryString).value(queryString).toList();
 
773
 
 
774
            QMutableListIterator<QVariant> i(holidays);
 
775
            while (i.hasNext()) {
 
776
                q->addHoliday(i.next().toHash());
 
777
            }
 
778
        }
 
779
 
 
780
        q->update();
 
781
        kDebug() << "repainting due to holiday population";
 
782
    }
 
783
 
 
784
    if (pendingPopulations & PopulateEvents) {
 
785
        events.clear();
 
786
        todos.clear();
 
787
        journals.clear();
 
788
        pimEvents.clear();
 
789
 
 
790
        if (calendarDataEngine && !eventsQuery.isEmpty()) {
 
791
            calendarDataEngine->disconnectSource(eventsQuery, q);
 
792
        }
 
793
 
 
794
        if (displayEvents) {
 
795
            // Just fetch the days displayed in the grid
 
796
            eventsQuery = "events" + QString(':') + viewStartDate.toString(Qt::ISODate) +
 
797
                          QString(':') + viewEndDate.toString(Qt::ISODate);
 
798
            kDebug() << "connecting to .. " << eventsQuery;
 
799
            calendarEngine()->connectSource(eventsQuery, q);
 
800
        } else {
 
801
            eventsQuery.clear();
 
802
        }
 
803
 
 
804
        q->update();
 
805
    }
 
806
 
 
807
    pendingPopulations = NoPendingPopulation;
 
808
    emit q->eventsChanged();
 
809
}
 
810
 
 
811
void CalendarTablePrivate::populateEvents()
 
812
{
 
813
    pendingPopulations |= PopulateEvents;
 
814
    delayedPopulationTimer->start();
 
815
}
 
816
 
 
817
void CalendarTable::dataUpdated(const QString &source, const Plasma::DataEngine::Data &data)
 
818
{
 
819
    Q_UNUSED(source)
 
820
    d->events.clear();
 
821
    d->todos.clear();
 
822
    d->journals.clear();
 
823
    d->pimEvents.clear();
 
824
 
 
825
    foreach (const QVariant &v, data) {
 
826
        Plasma::DataEngine::Data pimData = v.toHash();
 
827
        QString type = pimData.value("Type").toString();
 
828
        QString uid = pimData.value("UID").toString();
 
829
        QDate startDate = pimData.value("StartDate").value<KDateTime>().date();
 
830
 
 
831
        d->pimEvents.insert(uid, pimData);
 
832
 
 
833
        QList<QVariant> occurrenceList = pimData.value("Occurrences").toList();
 
834
        foreach (const QVariant &occurrence, occurrenceList) {
 
835
            QDate occStartDate = occurrence.toHash().value("OccurrenceStartDate").value<KDateTime>().date();
 
836
            if (pimData.value("EventMultiDay").toBool() == true) {
 
837
                QDate occEndDate = occurrence.toHash().value("OccurrenceEndDate").value<KDateTime>().date();
 
838
                QDate multiDate = occStartDate;
 
839
                while (multiDate <= occEndDate) {
 
840
                    d->insertPimOccurence(type, multiDate, occurrence.toHash());
 
841
                    multiDate = multiDate.addDays(1);
 
842
                }
 
843
            } else {
 
844
                d->insertPimOccurence(type, occStartDate, occurrence.toHash());
 
845
            }
 
846
        }
 
847
    }
 
848
 
 
849
    emit eventsChanged();
 
850
    update();
 
851
}
 
852
 
 
853
void CalendarTable::applyConfiguration(KConfigGroup cg)
 
854
{
 
855
    setCalendar(cg.readEntry("calendarType", "locale"));
 
856
    const bool holidays = cg.readEntry("displayHolidays", true);
 
857
    clearHolidaysRegions();
 
858
    if (holidays) {
 
859
        QStringList regions = cg.readEntry("holidaysRegions", QStringList(d->defaultHolidaysRegion()));
 
860
        QStringList daysOff = cg.readEntry("holidaysRegionsDaysOff", QStringList(d->defaultHolidaysRegion()));
 
861
        clearHolidaysRegions();
 
862
        foreach (const QString &region, regions) {
 
863
            d->addHolidaysRegion(region, daysOff.contains(region));
 
864
        }
 
865
 
 
866
        d->populateHolidays();
 
867
    }
 
868
 
 
869
    setDisplayHolidays(holidays);
 
870
    setDisplayEvents(cg.readEntry("displayEvents", true));
 
871
}
 
872
 
 
873
void CalendarTable::writeConfiguration(KConfigGroup cg)
 
874
{
 
875
    cg.writeEntry("calendarType", d->calendarType);
 
876
    cg.writeEntry("holidaysRegions", holidaysRegions());
 
877
    cg.writeEntry("holidaysRegionsDaysOff", holidaysRegionsDaysOff());
 
878
    cg.writeEntry("displayHolidays", d->displayHolidays);
 
879
    cg.writeEntry("displayEvents", d->displayEvents);
 
880
}
 
881
 
 
882
void CalendarTable::createConfigurationInterface(KConfigDialog *parent)
 
883
{
 
884
    QWidget *calendarConfigWidget = new QWidget();
 
885
    d->calendarConfigUi.setupUi(calendarConfigWidget);
 
886
    parent->addPage(calendarConfigWidget, i18n("Calendar"), "view-pim-calendar");
 
887
 
 
888
    QStringList calendars = KCalendarSystem::calendarSystems();
 
889
    d->calendarConfigUi.calendarComboBox->addItem( i18n("Local"), QVariant( "locale" ) );
 
890
    foreach ( const QString &cal, calendars ) {
 
891
        d->calendarConfigUi.calendarComboBox->addItem( KCalendarSystem::calendarLabel( cal ), QVariant( cal ) );
 
892
    }
 
893
    d->calendarConfigUi.calendarComboBox->setCurrentIndex( d->calendarConfigUi.calendarComboBox->findData( QVariant( d->calendarType ) ) );
 
894
 
 
895
    d->calendarConfigUi.displayEvents->setChecked(d->displayEvents);
 
896
 
 
897
#ifdef HAVE_KDEPIMLIBS
 
898
    QHashIterator<QString, Plasma::DataEngine::Data> it(d->holidaysRegions);
 
899
    while (it.hasNext()) {
 
900
        it.next();
 
901
        if (it.value().value("UseForDaysOff").toBool()) {
 
902
            d->calendarConfigUi.holidayRegionWidget->setRegionUseFlags(it.key(), KHolidays::HolidayRegionSelector::UseDaysOff);
 
903
        } else {
 
904
            d->calendarConfigUi.holidayRegionWidget->setRegionUseFlags(it.key(), KHolidays::HolidayRegionSelector::UseInformationOnly);
 
905
        }
 
906
    }
 
907
    d->calendarConfigUi.holidayRegionWidget->setDescriptionHidden(true);
 
908
 
 
909
    connect(d->calendarConfigUi.holidayRegionWidget, SIGNAL(selectionChanged()), parent, SLOT(settingsModified()));
 
910
#endif
 
911
 
 
912
    connect(d->calendarConfigUi.calendarComboBox, SIGNAL(activated(int)), parent, SLOT(settingsModified()));
 
913
    connect(d->calendarConfigUi.displayEvents, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
 
914
}
 
915
 
 
916
void CalendarTable::configAccepted(KConfigGroup cg)
 
917
{
 
918
    setCalendar(d->calendarConfigUi.calendarComboBox->itemData(d->calendarConfigUi.calendarComboBox->currentIndex()).toString());
 
919
    setDisplayEvents(d->calendarConfigUi.displayEvents->isChecked());
 
920
 
 
921
#ifdef HAVE_KDEPIMLIBS
 
922
    clearHolidaysRegions();
 
923
    QHash<QString, KHolidays::HolidayRegionSelector::RegionUseFlags> regions = d->calendarConfigUi.holidayRegionWidget->regionUseFlags();
 
924
    QHashIterator<QString, KHolidays::HolidayRegionSelector::RegionUseFlags> it(regions);
 
925
    bool displayHolidays = false;
 
926
    while (it.hasNext()) {
 
927
        it.next();
 
928
        if (it.value() == KHolidays::HolidayRegionSelector::UseDaysOff) {
 
929
            addHolidaysRegion(it.key(), true);
 
930
            displayHolidays = true;
 
931
        } else if (it.value() == KHolidays::HolidayRegionSelector::UseInformationOnly) {
 
932
            addHolidaysRegion(it.key(), false);
 
933
            displayHolidays = true;
 
934
        }
 
935
    }
 
936
    setDisplayHolidays(displayHolidays);
 
937
#endif
 
938
 
 
939
    writeConfiguration(cg);
 
940
}
 
941
 
 
942
//Returns the x co-ordinate for drawing the day cell on the widget given the weekday column
 
943
//Note weekdayColumn is 0 to (daysInWeek -1) and is not a real weekDay number (i.e. NOT Monday=1).
 
944
//Adjusts automatically for RTL mode, so don't use to calculate absolute positions
 
945
int CalendarTable::cellX(int weekdayColumn)
 
946
{
 
947
    return d->columnToX(d->adjustColumn(weekdayColumn));
 
948
}
 
949
 
 
950
//Returns the y co-ordinate for drawing the day cell on the widget given the weekRow
 
951
//weekRow is 0 to (DISPLAYED_WEEKS - 1)
 
952
int CalendarTable::cellY(int weekRow)
 
953
{
 
954
    return d->rowToY(weekRow);
 
955
}
 
956
 
 
957
void CalendarTable::wheelEvent(QGraphicsSceneWheelEvent * event)
 
958
{
 
959
    if (event->delta() < 0) {
 
960
        setDate(calendar()->addMonths(date(), 1));
 
961
    } else if (event->delta() > 0) {
 
962
        setDate(calendar()->addMonths(date(), -1));
 
963
    }
 
964
}
 
965
 
 
966
void CalendarTable::mousePressEvent(QGraphicsSceneMouseEvent *event)
 
967
{
 
968
    d->lastSeenMousePos = event->pos();
 
969
 
 
970
    event->accept();
 
971
    QDate date = d->dateFromPoint(event->pos());
 
972
    setDate(date);
 
973
    emit dateSelected(date);
 
974
}
 
975
 
 
976
void CalendarTable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
 
977
{
 
978
    mousePressEvent(event);
 
979
}
 
980
 
 
981
void CalendarTable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
 
982
{
 
983
    Q_UNUSED(event);
 
984
 
 
985
    d->lastSeenMousePos = event->pos();
 
986
 
 
987
    emit tableClicked();
 
988
}
 
989
 
 
990
void CalendarTable::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
 
991
{
 
992
    d->lastSeenMousePos = event->pos();
 
993
 
 
994
    d->updateHoveredPainting(event->pos());
 
995
}
 
996
 
 
997
void CalendarTable::resizeEvent(QGraphicsSceneResizeEvent * event)
 
998
{
 
999
    Q_UNUSED(event);
 
1000
 
 
1001
    QRectF r = contentsRect();
 
1002
    int numCols = d->daysInWeek + 1;
 
1003
    int rectSizeH = int(r.height() / (DISPLAYED_WEEKS + 1));
 
1004
    int rectSizeW = int(r.width() / numCols);
 
1005
 
 
1006
    //Using integers to help to keep things aligned to the grid
 
1007
    //kDebug() << r.width() << rectSize;
 
1008
    d->cellSpace = qMax(1, qMin(4, qMin(rectSizeH, rectSizeW) / 20));
 
1009
    d->headerSpace = d->cellSpace * 2;
 
1010
    d->weekBarSpace = d->cellSpace * 2 + 1;
 
1011
    d->cellH = rectSizeH - d->cellSpace;
 
1012
    d->cellW = rectSizeW - d->cellSpace;
 
1013
    d->glowRadius = d->cellW * .1;
 
1014
    d->headerHeight = (int) (d->cellH / 1.5);
 
1015
    d->centeringSpace = qMax(0, int((r.width() - (rectSizeW * numCols) - (d->cellSpace * (numCols -1))) / 2));
 
1016
}
 
1017
 
 
1018
void CalendarTable::paintCell(QPainter *p, int cell, int weekRow, int weekdayColumn, CellTypes type, const QDate &cellDate)
 
1019
{
 
1020
    Q_UNUSED(cell);
 
1021
 
 
1022
    QString cellSuffix = type & NotInCurrentMonth ? "inactive" : "active";
 
1023
    QRectF cellArea = QRectF(cellX(weekdayColumn), cellY(weekRow), d->cellW, d->cellH);
 
1024
 
 
1025
    d->svg->paint(p, cellArea, cellSuffix); // draw background
 
1026
 
 
1027
    QColor numberColor = Theme::defaultTheme()->color(Plasma::Theme::TextColor);
 
1028
    if (type & NotInCurrentMonth || type & InvalidDate) {
 
1029
        p->setOpacity(d->opacity);
 
1030
    }
 
1031
 
 
1032
    p->setPen(numberColor);
 
1033
    QFont font = Theme::defaultTheme()->font(Plasma::Theme::DefaultFont);
 
1034
    if (type & Event) {
 
1035
        font.setBold(true);
 
1036
    }
 
1037
    font.setPixelSize(cellArea.height() * 0.7);
 
1038
    p->setFont(font);
 
1039
    if (!(type & InvalidDate)) {
 
1040
        p->drawText(cellArea, Qt::AlignCenter, calendar()->dayString(cellDate, KCalendarSystem::ShortFormat), &cellArea); //draw number
 
1041
    }
 
1042
    p->setOpacity(1.0);
 
1043
}
 
1044
 
 
1045
void CalendarTable::paintBorder(QPainter *p, int cell, int weekRow, int weekdayColumn, CellTypes type, const QDate &cellDate)
 
1046
{
 
1047
    Q_UNUSED(cell);
 
1048
    Q_UNUSED(cellDate);
 
1049
 
 
1050
    if (type & Hovered) {
 
1051
        d->svg->paint(p, QRect(cellX(weekdayColumn), cellY(weekRow), d->cellW, d->cellH), "hoverHighlight");
 
1052
    }
 
1053
 
 
1054
    QString elementId;
 
1055
 
 
1056
    if (type & Today) {
 
1057
        elementId = "today";
 
1058
    } else if (type & Selected) {
 
1059
        elementId = "selected";
 
1060
    } else if (type & PublicHoliday) {
 
1061
        elementId = "red";
 
1062
    } else if (type & Holiday) {
 
1063
        elementId = "green";
 
1064
    } else {
 
1065
        return;
 
1066
    }
 
1067
 
 
1068
    d->svg->paint(p, QRectF(cellX(weekdayColumn) - 1, cellY(weekRow) - 1, d->cellW + 1, d->cellH + 2), elementId);
 
1069
}
 
1070
 
 
1071
void CalendarTable::paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget)
 
1072
{
 
1073
    Q_UNUSED(widget);
 
1074
 
 
1075
    // Draw weeks numbers column and day header
 
1076
    QRectF r = boundingRect();
 
1077
    d->svg->paint(p, QRectF(r.x() + d->centeringSpace, cellY(0), d->cellW,
 
1078
                  cellY(DISPLAYED_WEEKS) - cellY(0) - d->cellSpace),  "weeksColumn");
 
1079
    d->svg->paint(p, QRectF(r.x() + d->centeringSpace, r.y(),
 
1080
                  cellX(d->daysInWeek) - r.x() - d->cellSpace - d->centeringSpace, d->headerHeight), "weekDayHeader");
 
1081
 
 
1082
    QList<CalendarCellBorder> borders;
 
1083
    QList<CalendarCellBorder> hovers;
 
1084
    if (d->automaticUpdates) {
 
1085
        d->currentDate = QDate::currentDate();
 
1086
    }
 
1087
 
 
1088
    //weekRow and weekDaycolumn of table are 0 indexed and are not equivalent to weekday or week
 
1089
    //numbers.  In LTR mode we count/paint row and column from top-left corner, in RTL mode we
 
1090
    //count/paint from top-right corner, but we don't need to know as cellX() calculates the actual
 
1091
    //painting position for us depending on the mode.
 
1092
    for (int weekRow = 0; weekRow < DISPLAYED_WEEKS; weekRow++) {
 
1093
        for (int weekdayColumn = 0; weekdayColumn < d->daysInWeek; weekdayColumn++) {
 
1094
 
 
1095
            int x = cellX(weekdayColumn);
 
1096
            int y = cellY(weekRow);
 
1097
 
 
1098
            QRectF cellRect(x, y, d->cellW, d->cellH);
 
1099
            if (!cellRect.intersects(option->exposedRect)) {
 
1100
                continue;
 
1101
            }
 
1102
 
 
1103
            QDate cellDate = d->dateFromRowColumn(weekRow, weekdayColumn);
 
1104
            CalendarTable::CellTypes type(CalendarTable::NoType);
 
1105
            // get cell info
 
1106
            const int cellDay = calendar()->day(cellDate);
 
1107
            const int julian = cellDate.toJulianDay();
 
1108
 
 
1109
            // check what kind of cell we are
 
1110
            if (calendar()->month(cellDate) != d->selectedMonth) {
 
1111
                type |= CalendarTable::NotInCurrentMonth;
 
1112
            }
 
1113
 
 
1114
            if (!calendar()->isValid(cellDate)) {
 
1115
                type |= CalendarTable::InvalidDate;
 
1116
            }
 
1117
 
 
1118
            if (cellDate == d->currentDate) {
 
1119
                type |= CalendarTable::Today;
 
1120
            }
 
1121
 
 
1122
            if (cellDate == date()) {
 
1123
                type |= CalendarTable::Selected;
 
1124
            }
 
1125
 
 
1126
            foreach (int holidayUid, d->holidays.values(julian)) {
 
1127
                if (d->holidayIsDayOff(d->holidayEvents.value(holidayUid))) {
 
1128
                    type |= CalendarTable::PublicHoliday;
 
1129
                } else {
 
1130
                    type |= CalendarTable::Holiday;
 
1131
                }
 
1132
            }
 
1133
 
 
1134
            if (d->events.contains(julian) || d->todos.contains(julian)) {
 
1135
                type |= CalendarTable::Event;
 
1136
            }
 
1137
 
 
1138
            if (type != CalendarTable::NoType && type != CalendarTable::NotInCurrentMonth) {
 
1139
                borders.append(CalendarCellBorder(cellDay, weekRow, weekdayColumn, type, cellDate));
 
1140
            }
 
1141
 
 
1142
            if (weekRow == d->hoverWeekRow && weekdayColumn == d->hoverWeekdayColumn) {
 
1143
                type |= CalendarTable::Hovered;
 
1144
                hovers.append(CalendarCellBorder(cellDay, weekRow, weekdayColumn, type, cellDate));
 
1145
            }
 
1146
 
 
1147
            paintCell(p, cellDay, weekRow, weekdayColumn, type, cellDate);
 
1148
 
 
1149
            // FIXME: modify svg to allow for a wider week number cell
 
1150
            // a temporary workaround is to paint only one week number (weekString) when the cell is small
 
1151
            // and both week numbers (accurateWeekString) when there is enough room
 
1152
            if (weekdayColumn == 0) {
 
1153
                QRectF cellRect(r.x() + d->centeringSpace, y, d->cellW, d->cellH);
 
1154
                p->setPen(Theme::defaultTheme()->color(Plasma::Theme::TextColor));
 
1155
                QFont font = Theme::defaultTheme()->font(Plasma::Theme::DefaultFont);
 
1156
                font.setPixelSize(cellRect.height() * 0.7);
 
1157
                p->setFont(font);
 
1158
                p->setOpacity(d->opacity);
 
1159
                QString weekString;
 
1160
                QString accurateWeekString;
 
1161
                if (calendar()->isValid(cellDate)) {
 
1162
                    weekString = calendar()->weekNumberString(cellDate);
 
1163
                    accurateWeekString = weekString;
 
1164
                    if (calendar()->dayOfWeek(cellDate) != Qt::Monday) {
 
1165
                        QDate nextWeekDate = calendar()->addDays(cellDate, d->daysInWeek);
 
1166
                        if (calendar()->isValid(nextWeekDate)) {
 
1167
                            if (layoutDirection() == Qt::RightToLeft) {
 
1168
                                accurateWeekString.prepend("/").prepend(calendar()->weekNumberString(nextWeekDate));
 
1169
                            } else {
 
1170
                                accurateWeekString.append("/").append(calendar()->weekNumberString(nextWeekDate));
 
1171
                            }
 
1172
                        }
 
1173
                        // ensure that weekString is the week number that has the most amout of days in the row
 
1174
                        QDate middleWeekDate = calendar()->addDays(cellDate, floor(static_cast<float>(d->daysInWeek / 2)));
 
1175
                        if (calendar()->isValid(middleWeekDate)) {
 
1176
                            QString middleWeekString = calendar()->weekNumberString(middleWeekDate);
 
1177
                            if (weekString != middleWeekString) {
 
1178
                                weekString = middleWeekString;
 
1179
                            }
 
1180
                        }
 
1181
                    }
 
1182
                }
 
1183
                QFontMetrics fontMetrics(font);
 
1184
                if (fontMetrics.width(accurateWeekString) > d->cellW) {
 
1185
                    p->drawText(cellRect, Qt::AlignCenter, weekString); //draw number
 
1186
                } else {
 
1187
                    p->drawText(cellRect, Qt::AlignCenter, accurateWeekString); //draw number
 
1188
                }
 
1189
                p->setOpacity(1.0);
 
1190
            }
 
1191
        }
 
1192
    }
 
1193
 
 
1194
    // Draw days
 
1195
    if (option->exposedRect.intersects(QRect(r.x(), r.y(), r.width(), d->headerHeight))) {
 
1196
        p->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
 
1197
        int weekStartDay = calendar()->weekStartDay();
 
1198
        for (int i = 0; i < d->daysInWeek; i++){
 
1199
            int weekDay = ((i + weekStartDay - 1) % d->daysInWeek) + 1;
 
1200
            QString dayName = calendar()->weekDayName(weekDay, KCalendarSystem::ShortDayName);
 
1201
            QFont font = Theme::defaultTheme()->font(Plasma::Theme::DefaultFont);
 
1202
            font.setPixelSize(d->headerHeight * 0.9);
 
1203
            p->setFont(font);
 
1204
            p->drawText(QRectF(cellX(i), r.y(), d->cellW, d->headerHeight),
 
1205
                        Qt::AlignCenter | Qt::AlignVCenter, dayName);
 
1206
        }
 
1207
    }
 
1208
 
 
1209
    // Draw hovers
 
1210
    foreach (const CalendarCellBorder &border, hovers) {
 
1211
        p->save();
 
1212
        paintBorder(p, border.cell, border.week, border.weekDay, border.type, border.date);
 
1213
        p->restore();
 
1214
    }
 
1215
 
 
1216
    // Draw borders
 
1217
    foreach (const CalendarCellBorder &border, borders) {
 
1218
        p->save();
 
1219
        paintBorder(p, border.cell, border.week, border.weekDay, border.type, border.date);
 
1220
        p->restore();
 
1221
    }
 
1222
}
 
1223
 
 
1224
} //namespace Plasma
 
1225
 
 
1226
#include "calendartable.moc"