calendartable.cpp 42.7 KB
Newer Older
1
/*
Davide Bettio's avatar
Davide Bettio committed
2
 *   Copyright 2008,2010 Davide Bettio <davide.bettio@kdemail.net>
3
 *   Copyright 2009 John Layt <john@layt.net>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "calendartable.h"
22
#include "config-calendartable.h"
23
24
25

//Qt
#include <QtCore/QDate>
26
#include <QtCore/QListIterator>
27
#include <QtCore/QTimer>
28
29
30
31
#include <QtGui/QPainter>
#include <QtGui/QWidget>
#include <QtGui/QGraphicsSceneWheelEvent>
#include <QtGui/QStyleOptionGraphicsItem>
32

33
//KDECore
34
#include <KGlobal>
35
#include <KDateTime>
36
37
38
#include <KDebug>
#include <KConfigDialog>
#include <KConfigGroup>
39
40

//Plasma
41
42
#include <Plasma/Svg>
#include <Plasma/Theme>
43
#include <Plasma/DataEngine>
44
#include <Plasma/DataEngineManager>
45

46
47
48
#ifdef HAVE_KDEPIMLIBS
#include "ui_calendarHolidaysConfig.h"
#else
49
#include "ui_calendarConfig.h"
50
#endif
51

52
53
#include <cmath>

54
55
56
namespace Plasma
{

57
58
static const int DISPLAYED_WEEKS = 6;

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class CalendarCellBorder
{
public:
    CalendarCellBorder(int c, int w, int d, CalendarTable::CellTypes t, QDate dt)
        : cell(c),
          week(w),
          weekDay(d),
          type(t),
          date(dt)
    {
    }

    int cell;
    int week;
    int weekDay;
    CalendarTable::CellTypes type;
    QDate date;
};

class CalendarTablePrivate
{
    public:
81
        enum Populations { NoPendingPopulation = 0, PopulateHolidays = 1, PopulateEvents = 2 };
82

83
        CalendarTablePrivate(CalendarTable *calTable, const QDate &initialDate = QDate::currentDate())
84
85
86
            : q(calTable),
              calendarType("locale"),
              calendar(KGlobal::locale()->calendar()),
87
88
89
              displayEvents(false),
              displayHolidays(false),
              calendarDataEngine(0),
90
              automaticUpdates(true),
91
92
93
              opacity(0.5),
              pendingPopulations(NoPendingPopulation),
              delayedPopulationTimer(new QTimer())
94
        {
95
96
            KGlobal::locale()->insertCatalog("libkholidays");

97
98
99
100
            svg = new Svg();
            svg->setImagePath("widgets/calendar");
            svg->setContainsMultipleImages(true);

101
102
103
104
            delayedPopulationTimer->setInterval(0);
            delayedPopulationTimer->setSingleShot(true);
            QObject::connect(delayedPopulationTimer, SIGNAL(timeout()), q, SLOT(populateCalendar()));

105
            setDate(initialDate);
106
107
108
109
        }

        ~CalendarTablePrivate()
        {
110
            // Delete the old calendar first if it's not the global calendar
111
            if (calendar != KGlobal::locale()->calendar()) {
112
113
114
                delete calendar;
            }

115
116
117
118
            if (calendarDataEngine) {
                Plasma::DataEngineManager::self()->unloadEngine("calendar");
            }

119
            delete svg;
120
            delete delayedPopulationTimer;
121
122
        }

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
        void setCalendar(const KCalendarSystem *newCalendar)
        {
            // If not the global calendar, delete the old calendar first
            if (calendar != KGlobal::locale()->calendar()) {
                delete calendar;
            }

            calendar = newCalendar;

            if (calendar == KGlobal::locale()->calendar()) {
                calendarType = "locale";
            } else {
                calendarType = calendar->calendarType();
            }

            // Force date update to refresh cached date componants then update display
            setDate(selectedDate);
            updateHoveredPainting(QPointF());
141
142
            populateHolidays();
            populateEvents();
143
            q->update();
144
145
        }

146
        void setDate(const QDate &setDate)
147
        {
148
149
150
151
152
153
154
155
156
157
158
159
            selectedDate = setDate;
            selectedMonth = calendar->month(setDate);
            selectedYear = calendar->year(setDate);
            weekDayFirstOfSelectedMonth = weekDayFirstOfMonth(setDate);
            daysInWeek = calendar->daysInWeek(setDate);
            daysInSelectedMonth = calendar->daysInMonth(setDate);
            daysShownInPrevMonth = (weekDayFirstOfSelectedMonth - calendar->weekStartDay() + daysInWeek) % daysInWeek;
            // make sure at least one day of the previous month is visible.
            // 1 = minimum number of days to show, increase if more days should be forced visible:
            if (daysShownInPrevMonth < 1) {
                daysShownInPrevMonth += daysInWeek;
            }
160
161
            viewStartDate = dateFromRowColumn(0, 0);
            viewEndDate = dateFromRowColumn(DISPLAYED_WEEKS - 1, daysInWeek - 1);
162
163
        }

164
165
166
        //Returns the x co-ordinate of a given column to LTR order, column is 0 to (daysInWeek-1)
        //This version does not adjust for RTL, so should not be used directly for drawing
        int columnToX(int column)
167
        {
168
            return q->boundingRect().x() +
169
170
171
172
173
                   centeringSpace +
                   weekBarSpace +
                   cellW +
                   ((cellW + cellSpace) * column);
        }
174

175
176
177
        //Returns the y co-ordinate for given row, row is 0 to (DISPLAYED_WEEKS - 1)
        int rowToY(int row)
        {
178
            return (int) q->boundingRect().y() +
179
180
181
182
                         headerHeight +
                         headerSpace +
                         ((cellH + cellSpace) * row);
        }
183

184
185
186
187
188
        //Returns the absolute LTR column for a given x co-ordinate, -1 if outside table
        int xToColumn(qreal x)
        {
            if (x >= columnToX(0) && x < columnToX(daysInWeek)) {
                return ((x - centeringSpace) / (cellW + cellSpace)) - 1;
189
            }
190
191
            return -1;
        }
192

193
194
195
196
197
        //Returns the absolute row for a given y co-ordinate, -1 if outside table
        int yToRow(qreal y)
        {
            if (y >= rowToY(0) && y < rowToY(DISPLAYED_WEEKS)) {
                return (y - headerHeight - headerSpace) / (cellH + cellSpace);
198
            }
199
            return -1;
200
201
        }

202
203
204
        //Convert between column and weekdayColumn depending on LTR or RTL mode
        //Note the same calculation used in both directions
        int adjustColumn(int column)
205
        {
206
            if (column >= 0 && column < daysInWeek) {
207
                if (q->layoutDirection() == Qt::RightToLeft) {
208
209
210
                    return daysInWeek - column - 1;
                } else {
                    return column;
211
212
                }
            }
213
            return -1;
214
215
        }

216
217
218
        //Given an x y point in the table return the cell date.
        //Note can be an invalid date in the calendar system
        QDate dateFromPoint(QPointF point)
219
        {
220
221
222
            if (point.isNull()) {
                return QDate();
            }
223

224
225
226
227
228
            int column = xToColumn(point.x());
            int row = yToRow(point.y());

            if (column < 0 || column >= daysInWeek || row < 0 || row >= DISPLAYED_WEEKS) {
                return QDate();
229
            }
230

231
            return dateFromRowColumn(row, adjustColumn(column));
232
        }
233

234
235
236
237
238
239
240
241
242
243
244
245
246
        //Given a date in the currently selected month, return the position in the table as a
        //row and column. Note no direction is assumed
        void rowColumnFromDate(const QDate &cellDate, int &weekRow, int &weekdayColumn)
        {
            int offset = calendar->day(cellDate) + daysShownInPrevMonth - 1;
            weekRow = offset / daysInWeek;
            weekdayColumn = offset % daysInWeek;
        }

        //Given a position in the table as a 0-indexed row and column, return the cell date.  Makes
        //no assumption about direction.  Date returned can be an invalid date in the calendar
        //system, or simply invalid.
        QDate dateFromRowColumn(int weekRow, int weekdayColumn)
247
248
        {
            QDate cellDate;
249

250
251
252
253
254
            //starting from the first of the month, which is known to always be valid, add/subtract
            //number of days to get to the required cell
            if (calendar->setYMD(cellDate, selectedYear, selectedMonth, 1)) {
                cellDate = calendar->addDays(cellDate, (weekRow * daysInWeek) + weekdayColumn - daysShownInPrevMonth);
            }
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
            return cellDate;
        }

        void updateHoveredPainting(const QPointF &hoverPoint)
        {
            QRectF oldHoverRect = hoverRect;
            hoverRect = QRectF();
            hoverWeekdayColumn = -1;
            hoverWeekRow = -1;

            if (!hoverPoint.isNull()) {
                int column = xToColumn(hoverPoint.x());
                int row = yToRow(hoverPoint.y());

                if (column >= 0 && column < daysInWeek && row >= 0 && row < DISPLAYED_WEEKS) {
                    hoverRect = QRectF(columnToX(column) - glowRadius,
                                       rowToY(row) - glowRadius,
                                       cellW + glowRadius * 2,
274
                                       cellH + glowRadius * 2).adjusted(-2,-2,2,2);
275
276
277
                    hoverWeekdayColumn = adjustColumn(column);
                    hoverWeekRow = row;
                }
278
279
            }

280
281
            // now update what is needed, and only what is needed!
            if (hoverRect != oldHoverRect) {
282
283
                //FIXME: update only of a piece seems to paint over the old stuff
                /*if (oldHoverRect.isValid()) {
284
                    q->update(oldHoverRect);
285
286
                }
                if (hoverRect.isValid()) {
287
                    q->update(hoverRect);
288
                }*/
289
290
                emit q->dateHovered(dateFromRowColumn(hoverWeekRow, hoverWeekdayColumn));
                q->update();
291
            }
292
293
        }

294
        // calculate weekday number of first day of this month, this is the anchor for all calculations
295
296
        int weekDayFirstOfMonth(const QDate &cellDate)
        {
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
307
        QString defaultHolidaysRegion()
        {
308
309
            //FIXME: get rid of the query; turn it into a proper async request
            return calendarEngine()->query("holidaysDefaultRegion").value("holidaysDefaultRegion").toString();
310
311
        }

312
313
        bool isValidHolidaysRegion(const QString &holidayRegion)
        {
314
315
316
            //FIXME: get rid of the query; turn it into a proper async request
            QString queryString = "holidaysIsValidRegion" + QString(':') + holidayRegion;
            return calendarEngine()->query(queryString).value(queryString).toBool();
317
318
        }

319
        bool addHolidaysRegion(const QString &holidayRegion, bool daysOff)
320
        {
Aaron J. Seigo's avatar
hush    
Aaron J. Seigo committed
321
            //kDebug() << holidayRegion << holidaysRegions.contains(holidayRegion) << isValidHolidaysRegion(holidayRegion);
322
            if (!holidaysRegions.contains(holidayRegion) && isValidHolidaysRegion(holidayRegion)) {
323
                QString queryString = "holidaysRegion" + QString(':') + holidayRegion;
324
325
326
327
328
                Plasma::DataEngine::Data regions = calendarEngine()->query(queryString);
                Plasma::DataEngine::DataIterator it(regions);
                while (it.hasNext()) {
                    it.next();
                    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
340
        bool holidayIsDayOff(Plasma::DataEngine::Data holidayData)
        {
341
342
            return (holidayData.value("ObservanceType").toString() == "PublicHoliday" &&
                    holidaysRegions.value(holidayData.value("RegionCode").toString()).value("UseForDaysOff").toBool());
343
344
345
346
347
        }


        void insertPimOccurence(const QString &type, const QDate &date, Plasma::DataEngine::Data occurrence)
        {
348
349
350
351
352
353
354
355
356
357
358
359
            if (date >= viewStartDate && date <= viewEndDate) {
                int julian = date.toJulianDay();
                if (type == "Event" && !events.contains(julian, occurrence)) {
                    events.insert(julian, occurrence);
                } else if (type == "Todo" && !todos.contains(julian, occurrence)) {
                    todos.insert(julian, occurrence);
                } else if (type == "Journal" && !journals.contains(julian, occurrence)) {
                    journals.insert(julian, occurrence);
                }
            }
        }

360
361
362
363
364
365
        Plasma::DataEngine *calendarEngine();
        void checkIfCalendarEngineNeeded();
        void populateHolidays();
        void populateEvents();
        void populateCalendar();

366
        CalendarTable *q;
367
        QString calendarType;
368
        const KCalendarSystem *calendar;
369

370
        QDate selectedDate;
371
        QDate currentDate;
372
373
374
375
376
        int selectedMonth;
        int selectedYear;
        int weekDayFirstOfSelectedMonth;
        int daysInWeek;
        int daysInSelectedMonth;
377
        int daysShownInPrevMonth;
378
379
        QDate viewStartDate;
        QDate viewEndDate;
Aaron J. Seigo's avatar
Aaron J. Seigo committed
380

381
        bool displayEvents;
382
        bool displayHolidays;
383
        Plasma::DataEngine *calendarDataEngine;
384
385
386
387
388
389
390
391
392
        // List of Holiday Regions to display
        // Hash key: QString = Holiday Region Code, Data = details of Holiday Region
        QHash<QString, Plasma::DataEngine::Data> holidaysRegions;
        // Index to Holidays that occur on a given date.
        // Hash key: int = Julian Day number of holiday observance, int = key of holiday event
        QMultiHash<int, int> holidays;
        // Holiday details.  A holiday may be observed on multiple dates.
        // Hash key: int = key of holiday event, Data = details of holiday
        QHash<int, Plasma::DataEngine::Data> holidayEvents;
393
394
        // Index to Events/Todos/Journals that occur on a given date.
        // Hash key: int = Julian Day number of event/todo occurrence, Data = occurence details including start date, end date and Akonadi incidence UID
395
396
        QMultiHash<int, Plasma::DataEngine::Data> events;
        QMultiHash<int, Plasma::DataEngine::Data> todos;
397
398
        QMultiHash<int, Plasma::DataEngine::Data> journals;
        // 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
404
        bool automaticUpdates;

405
406
        QPointF lastSeenMousePos;

407
408
409
#ifdef HAVE_KDEPIMLIBS
        Ui::calendarHolidaysConfig calendarConfigUi;
#else
410
        Ui::calendarConfig calendarConfigUi;
411
#endif
412

413
        Plasma::Svg *svg;
414
        float opacity; //transparency for the inactive text
415
        QRectF hoverRect;
416
417
        int hoverWeekRow;
        int hoverWeekdayColumn;
418
419
420
421
422
423
424
425
        int centeringSpace;
        int cellW;
        int cellH;
        int cellSpace;
        int headerHeight;
        int headerSpace;
        int weekBarSpace;
        int glowRadius;
426
427
428

        int pendingPopulations;
        QTimer *delayedPopulationTimer;
429
430
431
432
433
};

CalendarTable::CalendarTable(const QDate &date, QGraphicsWidget *parent)
    : QGraphicsWidget(parent), d(new CalendarTablePrivate(this, date))
{
434
    setAcceptHoverEvents(true);
435
    setCacheMode(QGraphicsItem::DeviceCoordinateCache);
436
437
438
439
440
441
}

CalendarTable::CalendarTable(QGraphicsWidget *parent)
    : QGraphicsWidget(parent), d(new CalendarTablePrivate(this))
{
    setAcceptHoverEvents(true);
442
    setCacheMode(QGraphicsItem::DeviceCoordinateCache);
443
444
445
446
447
448
449
}

CalendarTable::~CalendarTable()
{
    delete d;
}

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
460
461
462
463
464
465
466
        d->setCalendar(KCalendarSystem::create(newCalendarType));
    }

    // Signal out date change so any dependents will update as well
    emit dateChanged(date(), date());
    emit dateChanged(date());
}

467
void CalendarTable::setCalendar(const KCalendarSystem *newCalendar)
468
469
{
    if (newCalendar == d->calendar) {
470
        return;
471
    }
472

473
    d->setCalendar(newCalendar);
474

475
476
477
    // Signal out date change so any dependents will update as well
    emit dateChanged(date(), date());
    emit dateChanged(date());
478
479
}

480
481
482
483
484
const KCalendarSystem *CalendarTable::calendar() const
{
    return d->calendar;
}

485
void CalendarTable::setDate(const QDate &newDate)
486
{
487
488
    // New date must be valid in the current calendar system
    if (!calendar()->isValid(newDate)) {
489
        return;
490
491
    }

492
493
    // If new date is the same as old date don't actually need to do anything
    if (newDate == date()) {
494
        return;
495
496
    }

497
498
499
500
    int oldYear = d->selectedYear;
    int oldMonth = d->selectedMonth;
    QDate oldDate = date();

Christian Loose's avatar
Christian Loose committed
501
    // now change the date
502
    d->setDate(newDate);
Christian Loose's avatar
Christian Loose committed
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
509
        d->populateHolidays();
        d->populateEvents();
510
511
    } else {
        // only update the old and the new areas
512
513
514
        int row, column;
        d->rowColumnFromDate(oldDate, row, column);
        update(cellX(column) - d->glowRadius, cellY(row) - d->glowRadius,
515
516
               d->cellW + d->glowRadius * 2, d->cellH + d->glowRadius * 2);

517
518
        d->rowColumnFromDate(newDate, row, column);
        update(cellX(column) - d->glowRadius, cellY(row) - d->glowRadius,
519
520
521
               d->cellW + d->glowRadius * 2, d->cellH + d->glowRadius * 2);
    }

522
523
    emit dateChanged(newDate, oldDate);
    emit dateChanged(newDate);
524
525
526
527
}

const QDate& CalendarTable::date() const
{
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
539
540
541
542
        QMutableHashIterator<QString, Plasma::DataEngine::Data> it(d->holidaysRegions);
        while (it.hasNext()) {
            it.next();
            if (!d->isValidHolidaysRegion(it.key())) {
                it.remove();
543
            }
544
        }
545
546
547
    } else {
        clearHolidays();
        d->checkIfCalendarEngineNeeded();
548
549
550
551
    }

    if (d->displayHolidays != showHolidays) {
        d->displayHolidays = showHolidays;
552
        d->populateHolidays();
553
554
555
556
557
    }
}

bool CalendarTable::displayHolidays()
{
558
    return d->displayHolidays && !d->holidaysRegions.isEmpty();
559
560
}

561
bool CalendarTable::displayEvents()
562
{
563
564
565
566
567
568
569
570
571
572
573
    return d->displayEvents;
}

void CalendarTable::setDisplayEvents(bool display)
{
    if (d->displayEvents == display) {
        return;
    }

    d->displayEvents = display;
    if (display) {
574
        d->populateEvents();
575
    } else {
576
577
        if (d->calendarDataEngine) {
            d->calendarDataEngine->disconnectSource(d->eventsQuery, this);
578
        }
579
        d->events.clear();
580
        d->todos.clear();
581
582
        d->journals.clear();
        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
596
    if (d->displayHolidays && d->addHolidaysRegion(region, daysOff)) {
        d->populateHolidays();
597
598
599
    }
}

600
QStringList CalendarTable::holidaysRegions() const
601
{
602
    return d->holidaysRegions.keys();
603
604
}

605
606
607
608
609
610
611
612
613
614
615
616
617
QStringList CalendarTable::holidaysRegionsDaysOff() const
{
    QStringList regions;
    QHashIterator<QString, Plasma::DataEngine::Data> it(d->holidaysRegions);
    while (it.hasNext()) {
        it.next();
        if (it.value().value("UseForDaysOff").toBool()) {
            regions.append(it.key());
        }
    }
    return regions;
}

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
628
629
630
631
632
633
    QDate holidayStartDate = QDate::fromString(holidayData.value("ObservanceStartDate").toString(), Qt::ISODate);
    int holidayDuration = holidayData.value("ObservanceDuration").toInt();
    int uid = d->holidayEvents.count();
    d->holidayEvents.insert(uid, holidayData);
    for (int i = 0; i < holidayDuration; ++i) {
        d->holidays.insertMulti(holidayStartDate.addDays(i).toJulianDay(), uid);
    }
634
635
}

636
bool CalendarTable::dateHasDetails(const QDate &date) const
637
{
638
639
640
641
642
    int julian = date.toJulianDay();
    return d->holidays.contains(julian) ||
           d->events.contains(julian) ||
           d->todos.contains(julian) ||
           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
652
        foreach (int holidayUid, d->holidays.values(julian)) {
            Plasma::DataEngine::Data holidayData = d->holidayEvents.value(holidayUid);
653
654
            QString region = holidayData.value("RegionCode").toString();

655
            if (d->holidayIsDayOff(holidayData)) {
656
                details << i18nc("Day off: Holiday name (holiday region)",
657
658
659
                                 "<i>Holiday</i>: %1 (%2)",
                                 holidayData.value("Name").toString(),
                                 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
665
                                    holidayData.value("Name").toString(),
                                    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
678
        foreach (const Plasma::DataEngine::Data &occurrence, d->todos.values(julian)) {
            //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
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
QString CalendarTable::buildOccurrenceDescription(const Plasma::DataEngine::Data &occurrence) const
{
    const QString uid = occurrence.value("OccurrenceUid").toString();
    const KDateTime occStartDate = occurrence.value("OccurrenceStartDate").value<KDateTime>();
    const KDateTime occEndDate = occurrence.value("OccurrenceEndDate").value<KDateTime>();
    const Plasma::DataEngine::Data details = d->pimEvents.value(uid);

    if (details.value("AllDay").toBool()) {
        return i18nc("All-day calendar event summary", "<br>%1", details.value("Summary").toString());
    } else if (!occEndDate.isValid() || occStartDate == occEndDate) {
        return i18nc("Time and summary for a calendarevent", "%1<br>%2",
                     KGlobal::locale()->formatTime(occStartDate.time()),
                     details.value("Summary").toString());
    }

    return i18nc("Start and end time and summary for a calendar event", "%1 - %2<br>%3",
                 KGlobal::locale()->formatTime(occStartDate.time()),
                 KGlobal::locale()->formatTime(occEndDate.time()),
                 details.value("Summary").toString());
}

707
708
709
710
711
712
713
714
715
void CalendarTable::setAutomaticUpdateEnabled(bool enabled)
{
    d->automaticUpdates = enabled;
}

bool CalendarTable::isAutomaticUpdateEnabled() const
{
    return d->automaticUpdates;
}
716

717
718
void CalendarTable::setCurrentDate(const QDate &date)
{
719
    d->currentDate = date;
720
721
722
723
724
725
726
}

const QDate& CalendarTable::currentDate() const
{
    return d->currentDate;
}

727
728
729
730
731
732
733
734
735
736
QDate CalendarTable::startDate() const
{
    return d->viewStartDate;
}

QDate CalendarTable::endDate() const
{
    return d->viewEndDate;
}

737
Plasma::DataEngine *CalendarTablePrivate::calendarEngine()
738
{
739
740
    if (!calendarDataEngine) {
        calendarDataEngine = Plasma::DataEngineManager::self()->loadEngine("calendar");
741
742
    }

743
744
    return calendarDataEngine;
}
745

746
747
748
749
void CalendarTablePrivate::checkIfCalendarEngineNeeded()
{
    if (calendarDataEngine && !displayHolidays && !displayEvents) {
        calendarDataEngine = Plasma::DataEngineManager::self()->loadEngine("calendar");
750
751
752
    }
}

753
void CalendarTablePrivate::populateHolidays()
754
{
755
756
757
    pendingPopulations |= PopulateHolidays;
    delayedPopulationTimer->start();
}
758

759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
void CalendarTablePrivate::populateCalendar()
{
    if (pendingPopulations & PopulateHolidays) {
        holidayEvents.clear();
        holidays.clear();

        if (q->displayHolidays()) {
            // Just fetch the days displayed in the grid
            //FIXME: queries are bad, use an async method
            QString queryString = "holidays" + QString(':') +
                                  q->holidaysRegions().join(QString(',')) +
                                  QString(':') + viewStartDate.toString(Qt::ISODate) +
                                  QString(':') + viewEndDate.toString(Qt::ISODate);
            QList<QVariant> holidays = calendarEngine()->query(queryString).value(queryString).toList();

            QMutableListIterator<QVariant> i(holidays);
            while (i.hasNext()) {
                q->addHoliday(i.next().toHash());
            }
        }

        q->update();
        kDebug() << "repainting due to holiday population";
782
783
    }

784
785
786
787
788
789
790
791
792
793
794
795
796
797
    if (pendingPopulations & PopulateEvents) {
        events.clear();
        todos.clear();
        journals.clear();
        pimEvents.clear();

        if (calendarDataEngine && !eventsQuery.isEmpty()) {
            calendarDataEngine->disconnectSource(eventsQuery, q);
        }

        if (displayEvents) {
            // Just fetch the days displayed in the grid
            eventsQuery = "events" + QString(':') + viewStartDate.toString(Qt::ISODate) +
                          QString(':') + viewEndDate.toString(Qt::ISODate);
798
            kDebug() << "connecting to .. " << eventsQuery;
799
800
801
802
803
804
805
806
807
            calendarEngine()->connectSource(eventsQuery, q);
        } else {
            eventsQuery.clear();
        }

        q->update();
    }

    pendingPopulations = NoPendingPopulation;
808
    emit q->eventsChanged();
809
810
811
812
}

void CalendarTablePrivate::populateEvents()
{
813
    pendingPopulations |= PopulateEvents;
814
    delayedPopulationTimer->start();
815
816
817
818
}

void CalendarTable::dataUpdated(const QString &source, const Plasma::DataEngine::Data &data)
{
819
    Q_UNUSED(source)
820
821
    d->events.clear();
    d->todos.clear();
822
823
    d->journals.clear();
    d->pimEvents.clear();
Aaron J. Seigo's avatar
Aaron J. Seigo committed
824

825
    foreach (const QVariant &v, data) {
826
        Plasma::DataEngine::Data pimData = v.toHash();
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
        QString type = pimData.value("Type").toString();
        QString uid = pimData.value("UID").toString();
        QDate startDate = pimData.value("StartDate").value<KDateTime>().date();

        d->pimEvents.insert(uid, pimData);

        QList<QVariant> occurrenceList = pimData.value("Occurrences").toList();
        foreach (QVariant occurrence, occurrenceList) {
            QDate occStartDate = occurrence.toHash().value("OccurrenceStartDate").value<KDateTime>().date();
            if (pimData.value("EventMultiDay").toBool() == true) {
                QDate occEndDate = occurrence.toHash().value("OccurrenceEndDate").value<KDateTime>().date();
                QDate multiDate = occStartDate;
                while (multiDate <= occEndDate) {
                    d->insertPimOccurence(type, multiDate, occurrence.toHash());
                    multiDate = multiDate.addDays(1);
                }
            } else {
                d->insertPimOccurence(type, occStartDate, occurrence.toHash());
            }
        }
847
    }
848
849

    emit eventsChanged();
850
    update();
851
852
}

853
854
855
void CalendarTable::applyConfiguration(KConfigGroup cg)
{
    setCalendar(cg.readEntry("calendarType", "locale"));
856
    const bool holidays = cg.readEntry("displayHolidays", true);
857
    clearHolidaysRegions();
858
859
860
861
862
863
864
865
866
    if (holidays) {
        QStringList regions = cg.readEntry("holidaysRegions", QStringList(d->defaultHolidaysRegion()));
        QStringList daysOff = cg.readEntry("holidaysRegionsDaysOff", QStringList(d->defaultHolidaysRegion()));
        clearHolidaysRegions();
        foreach (const QString &region, regions) {
            d->addHolidaysRegion(region, daysOff.contains(region));
        }

        d->populateHolidays();
867
    }
868

Aaron J. Seigo's avatar
Aaron J. Seigo committed
869
    setDisplayHolidays(holidays);
870
    setDisplayEvents(cg.readEntry("displayEvents", true));
871
872
873
874
875
}

void CalendarTable::writeConfiguration(KConfigGroup cg)
{
    cg.writeEntry("calendarType", d->calendarType);
876
877
    cg.writeEntry("holidaysRegions", holidaysRegions());
    cg.writeEntry("holidaysRegionsDaysOff", holidaysRegionsDaysOff());
878
    cg.writeEntry("displayHolidays", d->displayHolidays);
Aaron J. Seigo's avatar
Aaron J. Seigo committed
879
    cg.writeEntry("displayEvents", d->displayEvents);
880
881
882
883
884
885
886
887
888
889
}

void CalendarTable::createConfigurationInterface(KConfigDialog *parent)
{
    QWidget *calendarConfigWidget = new QWidget();
    d->calendarConfigUi.setupUi(calendarConfigWidget);
    parent->addPage(calendarConfigWidget, i18n("Calendar"), "view-pim-calendar");

    QStringList calendars = KCalendarSystem::calendarSystems();
    d->calendarConfigUi.calendarComboBox->addItem( i18n("Local"), QVariant( "locale" ) );
890
    foreach ( const QString &cal, calendars ) {
891
892
893
894
        d->calendarConfigUi.calendarComboBox->addItem( KCalendarSystem::calendarLabel( cal ), QVariant( cal ) );
    }
    d->calendarConfigUi.calendarComboBox->setCurrentIndex( d->calendarConfigUi.calendarComboBox->findData( QVariant( d->calendarType ) ) );

Aaron J. Seigo's avatar
Aaron J. Seigo committed
895
896
    d->calendarConfigUi.displayEvents->setChecked(d->displayEvents);

897
#ifdef HAVE_KDEPIMLIBS
898
899
900
901
    QHashIterator<QString, Plasma::DataEngine::Data> it(d->holidaysRegions);
    while (it.hasNext()) {
        it.next();
        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
913

    connect(d->calendarConfigUi.calendarComboBox, SIGNAL(activated(int)), parent, SLOT(settingsModified()));
    connect(d->calendarConfigUi.displayEvents, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
914
915
916
917
918
}

void CalendarTable::applyConfigurationInterface()
{
    setCalendar(d->calendarConfigUi.calendarComboBox->itemData(d->calendarConfigUi.calendarComboBox->currentIndex()).toString());
Aaron J. Seigo's avatar
Aaron J. Seigo committed
919
    setDisplayEvents(d->calendarConfigUi.displayEvents->isChecked());
920
921

#ifdef HAVE_KDEPIMLIBS
922
    clearHolidaysRegions();
923
924
    QHash<QString, KHolidays::HolidayRegionSelector::RegionUseFlags> regions = d->calendarConfigUi.holidayRegionWidget->regionUseFlags();
    QHashIterator<QString, KHolidays::HolidayRegionSelector::RegionUseFlags> it(regions);
925
926
927
    bool displayHolidays = false;
    while (it.hasNext()) {
        it.next();
928
        if (it.value() == KHolidays::HolidayRegionSelector::UseDaysOff) {
929
930
            addHolidaysRegion(it.key(), true);
            displayHolidays = true;
931
        } else if (it.value() == KHolidays::HolidayRegionSelector::UseInformationOnly) {
932
933
934
935
936
            addHolidaysRegion(it.key(), false);
            displayHolidays = true;
        }
    }
    setDisplayHolidays(displayHolidays);
937
#endif
938
939
940
941
942
943
944
945
}

void CalendarTable::configAccepted(KConfigGroup cg)
{
    applyConfigurationInterface();
    writeConfiguration(cg);
}

946
947
948
949
//Returns the x co-ordinate for drawing the day cell on the widget given the weekday column
//Note weekdayColumn is 0 to (daysInWeek -1) and is not a real weekDay number (i.e. NOT Monday=1).
//Adjusts automatically for RTL mode, so don't use to calculate absolute positions
int CalendarTable::cellX(int weekdayColumn)
950
{
951
    return d->columnToX(d->adjustColumn(weekdayColumn));
952
953
}

954
//Returns the y co-ordinate for drawing the day cell on the widget given the weekRow
955
//weekRow is 0 to (DISPLAYED_WEEKS - 1)
956
int CalendarTable::cellY(int weekRow)
957
{
958
    return d->rowToY(weekRow);
959
960
961
962
963
}

void CalendarTable::wheelEvent(QGraphicsSceneWheelEvent * event)
{
    if (event->delta() < 0) {
964
        setDate(calendar()->addMonths(date(), 1));
965
    } else if (event->delta() > 0) {
966
        setDate(calendar()->addMonths(date(), -1));
967
968
969
970
971
    }
}

void CalendarTable::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
972
    d->lastSeenMousePos = event->pos();
973

974
    event->accept();
975
976
977
    QDate date = d->dateFromPoint(event->pos());
    setDate(date);
    emit dateSelected(date);
978
979
980
981
982
983
984
985
986
987
988
}

void CalendarTable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    mousePressEvent(event);
}

void CalendarTable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);

989
    d->lastSeenMousePos = event->pos();
990

991
992
993
994
995
    emit tableClicked();
}

void CalendarTable::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
996
    d->lastSeenMousePos = event->pos();
997

998
    d->updateHoveredPainting(event->pos());
999
1000
}

For faster browsing, not all history is shown. View entire blame