~ubuntu-branches/ubuntu/intrepid/digikam/intrepid

« back to all changes in this revision

Viewing changes to digikam/digikam/timelinewidget.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mark Purcell
  • Date: 2008-07-17 20:25:39 UTC
  • mfrom: (1.2.15 upstream) (3.1.2 lenny)
  • Revision ID: james.westby@ubuntu.com-20080717202539-6n7dtirbkoo7qvhd
Tags: 2:0.9.4-1
* New upstream release
  - digiKam 0.9.4 Release Plan (KDE3) ~ 13 July 08 (Closes: #490144)
* DEB_CONFIGURE_EXTRA_FLAGS := --without-included-sqlite3
* Debhelper compatibility level V7
* Install pixmaps in debian/*.install
* Add debian/digikam.lintian-overrides

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ============================================================
 
2
 *
 
3
 * This file is a part of digiKam project
 
4
 * http://www.digikam.org
 
5
 *
 
6
 * Date        : 2007-12-08
 
7
 * Description : a widget to display date and time statistics of pictures
 
8
 *
 
9
 * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
 
10
 *
 
11
 * This program is free software; you can redistribute it
 
12
 * and/or modify it under the terms of the GNU General
 
13
 * Public License as published by the Free Software Foundation;
 
14
 * either version 2, or (at your option)
 
15
 * any later version.
 
16
 * 
 
17
 * This program is distributed in the hope that it will be useful,
 
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
20
 * GNU General Public License for more details.
 
21
 * 
 
22
 * ============================================================ */
 
23
 
 
24
// C++ includes.
 
25
 
 
26
#include <cmath>
 
27
 
 
28
// Qt includes.
 
29
 
 
30
#include <qpainter.h>
 
31
#include <qpixmap.h>
 
32
#include <qpen.h>
 
33
 
 
34
// KDE include.
 
35
 
 
36
#include <kcursor.h>
 
37
#include <klocale.h>
 
38
#include <kglobal.h>
 
39
#include <kcalendarsystem.h>
 
40
#include <kglobalsettings.h>
 
41
 
 
42
// Local includes.
 
43
 
 
44
#include "ddebug.h"
 
45
#include "themeengine.h"
 
46
#include "timelinewidget.h"
 
47
#include "timelinewidget.moc"
 
48
 
 
49
namespace Digikam
 
50
{
 
51
 
 
52
class TimeLineWidgetPriv
 
53
{
 
54
 
 
55
public :
 
56
 
 
57
    typedef QPair<int, int> YearRefPair;                         // Year + a reference association (Month or week or day)
 
58
    typedef QPair<int, TimeLineWidget::SelectionMode> StatPair;  // Statistic value + selection status.
 
59
 
 
60
public:
 
61
 
 
62
    TimeLineWidgetPriv()
 
63
    {
 
64
        validMouseEvent = false;
 
65
        selMouseEvent   = false;
 
66
        maxCountByDay   = 1;
 
67
        maxCountByWeek  = 1;
 
68
        maxCountByMonth = 1;
 
69
        maxCountByYear  = 1;
 
70
        topMargin       = 3;
 
71
        bottomMargin    = 20;
 
72
        barWidth        = 20;
 
73
        startPos        = 96;
 
74
        nbItems         = 10;
 
75
        timeUnit        = TimeLineWidget::Month;
 
76
        scaleMode       = TimeLineWidget::LinScale;
 
77
        calendar        = KGlobal::locale()->calendar();
 
78
    }
 
79
 
 
80
    bool                         validMouseEvent;   // Current mouse enter event is valid to set cursor position or selection.
 
81
    bool                         selMouseEvent;     // Current mouse enter event is about to make a selection.
 
82
 
 
83
    int                          maxCountByDay;
 
84
    int                          maxCountByWeek;
 
85
    int                          maxCountByMonth;
 
86
    int                          maxCountByYear;
 
87
    int                          topMargin; 
 
88
    int                          bottomMargin; 
 
89
    int                          barWidth; 
 
90
    int                          nbItems;
 
91
    int                          startPos;
 
92
 
 
93
    QDateTime                    refDateTime;       // Reference date-time used to draw histogram from middle of widget.
 
94
    QDateTime                    cursorDateTime;    // Current date-time used to draw focus cursor.
 
95
    QDateTime                    minDateTime;       // Higher date on histogram.
 
96
    QDateTime                    maxDateTime;       // Lower date on histogram.
 
97
    QDateTime                    selStartDateTime;
 
98
    QDateTime                    selMinDateTime;    // Lower date available on histogram.
 
99
    QDateTime                    selMaxDateTime;    // Higher date available on histogram.
 
100
 
 
101
    QPixmap                      pixmap;            // Used for widget double buffering.
 
102
 
 
103
    QMap<YearRefPair, StatPair>  dayStatMap;        // Store Days count statistics.
 
104
    QMap<YearRefPair, StatPair>  weekStatMap;       // Store Weeks count statistics.
 
105
    QMap<YearRefPair, StatPair>  monthStatMap;      // Store Month count statistics.
 
106
    QMap<int,         StatPair>  yearStatMap;       // Store Years count statistics.
 
107
 
 
108
    const KCalendarSystem       *calendar;
 
109
 
 
110
    TimeLineWidget::TimeUnit     timeUnit;
 
111
    TimeLineWidget::ScaleMode    scaleMode;
 
112
};
 
113
 
 
114
TimeLineWidget::TimeLineWidget(QWidget *parent)
 
115
              : QWidget(parent, 0, Qt::WDestructiveClose)
 
116
{
 
117
    d = new TimeLineWidgetPriv;
 
118
    setBackgroundMode(Qt::NoBackground);
 
119
    setMouseTracking(true);
 
120
    setMinimumWidth(256);
 
121
    setMinimumHeight(192);
 
122
 
 
123
    QDateTime ref = QDateTime::currentDateTime();   
 
124
    setCursorDateTime(ref);   
 
125
    setRefDateTime(ref);
 
126
 
 
127
    connect(ThemeEngine::instance(), SIGNAL(signalThemeChanged()),
 
128
            this, SLOT(slotThemeChanged()));
 
129
}
 
130
 
 
131
TimeLineWidget::~TimeLineWidget()
 
132
{
 
133
    delete d;
 
134
}
 
135
 
 
136
void TimeLineWidget::setTimeUnit(TimeUnit timeUnit)
 
137
{
 
138
    d->timeUnit = timeUnit;
 
139
    setCursorDateTime(cursorDateTime());
 
140
    setRefDateTime(cursorDateTime());
 
141
}
 
142
 
 
143
TimeLineWidget::TimeUnit TimeLineWidget::timeUnit() const
 
144
{
 
145
    return d->timeUnit;
 
146
}
 
147
 
 
148
void TimeLineWidget::setScaleMode(ScaleMode scaleMode)
 
149
{
 
150
    d->scaleMode = scaleMode;
 
151
    updatePixmap();
 
152
    update();
 
153
}
 
154
 
 
155
TimeLineWidget::ScaleMode TimeLineWidget::scaleMode() const
 
156
{
 
157
    return d->scaleMode;
 
158
}
 
159
 
 
160
int TimeLineWidget::totalIndex()
 
161
{
 
162
    if (d->minDateTime.isNull() || d->maxDateTime.isNull())
 
163
        return 0;
 
164
 
 
165
    int        i = 0;
 
166
    QDateTime dt = d->minDateTime;
 
167
 
 
168
    do
 
169
    {
 
170
        dt = nextDateTime(dt);
 
171
        i++;
 
172
    }
 
173
    while(dt < d->maxDateTime);
 
174
 
 
175
    return i;
 
176
}
 
177
 
 
178
int TimeLineWidget::indexForDateTime(const QDateTime& date)
 
179
{
 
180
    if (d->minDateTime.isNull() || d->maxDateTime.isNull() || date.isNull())
 
181
        return 0;
 
182
 
 
183
    int        i = 0;
 
184
    QDateTime dt = d->minDateTime;
 
185
 
 
186
    do
 
187
    {
 
188
        dt = nextDateTime(dt);
 
189
        i++;
 
190
    }
 
191
    while(dt < date);
 
192
 
 
193
    return i;
 
194
}
 
195
 
 
196
int TimeLineWidget::indexForRefDateTime()
 
197
{
 
198
    return (indexForDateTime(d->refDateTime));
 
199
}
 
200
 
 
201
int TimeLineWidget::indexForCursorDateTime()
 
202
{
 
203
    return (indexForDateTime(d->cursorDateTime));
 
204
}
 
205
 
 
206
void TimeLineWidget::setCurrentIndex(int index)
 
207
{
 
208
    if (d->minDateTime.isNull() || d->maxDateTime.isNull())
 
209
        return;
 
210
 
 
211
    int        i = 0;
 
212
    QDateTime dt = d->minDateTime;
 
213
 
 
214
    do
 
215
    {
 
216
        dt = nextDateTime(dt);
 
217
        i++;
 
218
    }
 
219
    while(i <= index);
 
220
 
 
221
    setRefDateTime(dt);
 
222
}
 
223
 
 
224
void TimeLineWidget::setCursorDateTime(const QDateTime& dateTime)
 
225
{
 
226
    QDateTime dt = dateTime;
 
227
    dt.setTime(QTime(0, 0, 0, 0));
 
228
 
 
229
    switch(d->timeUnit)
 
230
    {
 
231
        case Week:
 
232
        {
 
233
            // Go to the first day of week.
 
234
            int weekYear = 0;
 
235
            int weekNb   = d->calendar->weekNumber(dt.date(), &weekYear);
 
236
            dt           = firstDayOfWeek(weekYear, weekNb);
 
237
            break;
 
238
        }
 
239
        case Month:
 
240
        {
 
241
            // Go to the first day of month.
 
242
            dt.setDate(QDate(dt.date().year(), dt.date().month(), 1));
 
243
            break;
 
244
        }
 
245
        case Year:
 
246
        {
 
247
            // Go to the first day of year.
 
248
            dt.setDate(QDate(dt.date().year(), 1, 1));
 
249
            break;
 
250
        }
 
251
        default:
 
252
            break;
 
253
    }
 
254
 
 
255
    if (d->cursorDateTime == dt)
 
256
        return;
 
257
 
 
258
    d->cursorDateTime = dt;
 
259
 
 
260
    emit signalCursorPositionChanged();
 
261
}
 
262
 
 
263
QDateTime TimeLineWidget::cursorDateTime() const
 
264
{
 
265
    return d->cursorDateTime;
 
266
}
 
267
 
 
268
int TimeLineWidget::cursorInfo(QString& infoDate)
 
269
{
 
270
    SelectionMode selected;
 
271
    QDateTime dt = cursorDateTime();
 
272
 
 
273
    switch(d->timeUnit)
 
274
    {
 
275
        case Day:
 
276
        {
 
277
            infoDate = KGlobal::locale()->formatDate(dt.date());
 
278
            break;
 
279
        }
 
280
        case Week:
 
281
        {
 
282
            infoDate = i18n("Week #%1 - %2 %3")
 
283
                       .arg(d->calendar->weekNumber(dt.date()))
 
284
                       .arg(d->calendar->monthName(dt.date()))
 
285
                       .arg(d->calendar->yearString(dt.date(), false));
 
286
            break;
 
287
        }
 
288
        case Month:
 
289
        {
 
290
            infoDate = QString("%1 %2")
 
291
                       .arg(d->calendar->monthName(dt.date()))
 
292
                       .arg(d->calendar->yearString(dt.date(), false));
 
293
            break;
 
294
        }
 
295
        case Year:
 
296
        {
 
297
            infoDate = d->calendar->yearString(dt.date(), false);
 
298
            break;
 
299
        }
 
300
    }
 
301
 
 
302
    return statForDateTime(dt, selected);
 
303
}
 
304
 
 
305
void TimeLineWidget::setRefDateTime(const QDateTime& dateTime)
 
306
{
 
307
    QDateTime dt = dateTime;
 
308
    dt.setTime(QTime(0, 0, 0, 0));
 
309
 
 
310
    switch(d->timeUnit)
 
311
    {
 
312
        case Week:
 
313
        {
 
314
            // Go to the first day of week.
 
315
            int dayWeekOffset = (-1) * (d->calendar->dayOfWeek(dt.date()) - 1);
 
316
            dt = dt.addDays(dayWeekOffset);
 
317
            break;
 
318
        }
 
319
        case Month:
 
320
        {
 
321
            // Go to the first day of month.
 
322
            dt.setDate(QDate(dt.date().year(), dt.date().month(), 1));
 
323
            break;
 
324
        }
 
325
        case Year:
 
326
        {
 
327
            // Go to the first day of year.
 
328
            dt.setDate(QDate(dt.date().year(), 1, 1));
 
329
            break;
 
330
        }
 
331
        default:
 
332
            break;
 
333
    }
 
334
 
 
335
    d->refDateTime = dt;
 
336
    updatePixmap();
 
337
    update();
 
338
    emit signalRefDateTimeChanged();
 
339
}
 
340
 
 
341
void TimeLineWidget::slotResetSelection()
 
342
{
 
343
    resetSelection();
 
344
    updatePixmap();
 
345
    update();
 
346
}
 
347
 
 
348
void TimeLineWidget::resetSelection()
 
349
{
 
350
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
 
351
 
 
352
    for (it = d->dayStatMap.begin() ; it != d->dayStatMap.end(); ++it)
 
353
        it.data().second = Unselected;
 
354
 
 
355
    for (it = d->weekStatMap.begin() ; it != d->weekStatMap.end(); ++it)
 
356
        it.data().second = Unselected;
 
357
 
 
358
    for (it = d->monthStatMap.begin() ; it != d->monthStatMap.end(); ++it)
 
359
        it.data().second = Unselected;
 
360
 
 
361
    QMap<int, TimeLineWidgetPriv::StatPair>::iterator it2;
 
362
 
 
363
    for (it2 = d->yearStatMap.begin() ; it2 != d->yearStatMap.end(); ++it2)
 
364
        it2.data().second = Unselected;
 
365
}
 
366
 
 
367
void TimeLineWidget::setSelectedDateRange(const DateRangeList& list)
 
368
{
 
369
    if (list.isEmpty())
 
370
        return;
 
371
 
 
372
    resetSelection();
 
373
 
 
374
    QDateTime start, end, dt;
 
375
    DateRangeList::const_iterator it;
 
376
 
 
377
    for (it = list.begin() ; it != list.end(); ++it)
 
378
    {
 
379
        start = (*it).first;
 
380
        end   = (*it).second;
 
381
        if (end > start)
 
382
        {
 
383
            dt = start;
 
384
            do
 
385
            {
 
386
                setDateTimeSelected(dt, Selected);
 
387
                dt = dt.addDays(1);
 
388
            }
 
389
            while (dt < end);
 
390
        }
 
391
    }
 
392
 
 
393
    updatePixmap();
 
394
    update();
 
395
}
 
396
 
 
397
DateRangeList TimeLineWidget::selectedDateRange(int& totalCount)
 
398
{
 
399
    // We will parse all selections done on Days stats map.
 
400
 
 
401
    DateRangeList list;
 
402
    totalCount = 0;
 
403
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it3;
 
404
    QDateTime sdt, edt;
 
405
    QDate     date;
 
406
 
 
407
    for (it3 = d->dayStatMap.begin() ; it3 != d->dayStatMap.end(); ++it3)
 
408
    {
 
409
        if (it3.data().second == Selected)
 
410
        {
 
411
            date = QDate(it3.key().first, 1, 1);
 
412
            date = date.addDays(it3.key().second-1);
 
413
            sdt  = QDateTime(date);
 
414
            edt  = sdt.addDays(1); 
 
415
            list.append(DateRange(sdt, edt));
 
416
            totalCount += it3.data().first;
 
417
        }
 
418
    }
 
419
 
 
420
    DateRangeList::iterator it, it2;
 
421
 
 
422
    /*
 
423
    for (it = list.begin() ; it != list.end(); ++it)
 
424
        DDebug() << (*it).first.date().toString(Qt::ISODate) << " :: " 
 
425
                 << (*it).second.date().toString(Qt::ISODate) << endl;
 
426
 
 
427
    DDebug() << "Total Count of Items = " << totalCount << endl;
 
428
    */
 
429
 
 
430
    // Group contiguous date ranges to optimize query on database.
 
431
 
 
432
    DateRangeList list2;
 
433
    QDateTime     first, second, first2, second2;
 
434
 
 
435
    for (it = list.begin() ; it != list.end(); ++it)
 
436
    { 
 
437
        first  = (*it).first;
 
438
        second = (*it).second;
 
439
        it2 = it;
 
440
        do
 
441
        {
 
442
            ++it2;
 
443
            if (it2 != list.end())
 
444
            {
 
445
                first2  = (*it2).first;
 
446
                second2 = (*it2).second;
 
447
                
 
448
                if (first2 == second)
 
449
                {
 
450
                    second = second2;
 
451
                    ++it;
 
452
                }
 
453
                else 
 
454
                    break;
 
455
            }
 
456
        }
 
457
        while(it2 != list.end());
 
458
 
 
459
        list2.append(DateRange(first, second));
 
460
    }
 
461
 
 
462
    /*
 
463
    for (it = list2.begin() ; it != list2.end(); ++it)
 
464
        DDebug() << (*it).first.date().toString(Qt::ISODate) << " :: " 
 
465
                 << (*it).second.date().toString(Qt::ISODate) << endl;
 
466
    */
 
467
 
 
468
    return list2;
 
469
}
 
470
 
 
471
void TimeLineWidget::slotDatesMap(const QMap<QDateTime, int>& datesStatMap)
 
472
{
 
473
    // Clear all counts in all stats maps before to update it. Do not clear selections.
 
474
 
 
475
    QMap<int, TimeLineWidgetPriv::StatPair>::iterator it_iP;
 
476
    for ( it_iP = d->yearStatMap.begin() ; it_iP != d->yearStatMap.end(); ++it_iP )
 
477
        it_iP.data().first = 0;
 
478
 
 
479
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it_YP;
 
480
    for ( it_YP = d->monthStatMap.begin() ; it_YP != d->monthStatMap.end(); ++it_YP )
 
481
        it_YP.data().first = 0;
 
482
 
 
483
    for ( it_YP = d->weekStatMap.begin() ; it_YP != d->weekStatMap.end(); ++it_YP )
 
484
        it_YP.data().first = 0;
 
485
 
 
486
    for ( it_YP = d->dayStatMap.begin() ; it_YP != d->dayStatMap.end(); ++it_YP )
 
487
        it_YP.data().first = 0;
 
488
 
 
489
    // Parse all new Date stamp and store histogram stats relevant in maps.
 
490
 
 
491
    int count;
 
492
    QMap<QDateTime, int>::const_iterator it;
 
493
    if (datesStatMap.isEmpty())
 
494
    {
 
495
        d->minDateTime = QDateTime();
 
496
        d->maxDateTime = QDateTime();
 
497
    }
 
498
    else
 
499
    {
 
500
        d->minDateTime = datesStatMap.begin().key();
 
501
        d->maxDateTime = datesStatMap.begin().key();
 
502
    }
 
503
 
 
504
    for ( it = datesStatMap.begin(); it != datesStatMap.end(); ++it )
 
505
    {
 
506
        if (it.key() > d->maxDateTime)
 
507
            d->maxDateTime = it.key();
 
508
 
 
509
        if (it.key() < d->minDateTime)
 
510
            d->minDateTime = it.key();
 
511
 
 
512
        int year        = it.key().date().year();
 
513
        int month       = it.key().date().month();
 
514
        int day         = d->calendar->dayOfYear(it.key().date());
 
515
        int yearForWeek = year;  // Used with week shared between 2 years decade (Dec/Jan).
 
516
        int week        = d->calendar->weekNumber(it.key().date(), &yearForWeek);
 
517
 
 
518
        // Stats Years values.
 
519
 
 
520
        it_iP = d->yearStatMap.find(year);
 
521
        if ( it_iP == d->yearStatMap.end() )
 
522
        {
 
523
            count = it.data();
 
524
            d->yearStatMap.insert( year, TimeLineWidgetPriv::StatPair(count, Unselected) );
 
525
        }
 
526
        else
 
527
        {
 
528
            count = it_iP.data().first + it.data();
 
529
            d->yearStatMap.replace( year, TimeLineWidgetPriv::StatPair(count, it_iP.data().second) );
 
530
        }
 
531
 
 
532
        if (d->maxCountByYear < count) 
 
533
            d->maxCountByYear = count;
 
534
 
 
535
        // Stats Months values.
 
536
 
 
537
        it_YP = d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month));
 
538
        if ( it_YP == d->monthStatMap.end() )
 
539
        {
 
540
            count = it.data();
 
541
            d->monthStatMap.insert( TimeLineWidgetPriv::YearRefPair(year, month), 
 
542
                                    TimeLineWidgetPriv::StatPair(count, Unselected) );
 
543
        }
 
544
        else
 
545
        {
 
546
            count = it_YP.data().first + it.data();
 
547
            d->monthStatMap.replace( TimeLineWidgetPriv::YearRefPair(year, month), 
 
548
                                     TimeLineWidgetPriv::StatPair(count, it_YP.data().second) );
 
549
        }
 
550
 
 
551
        if (d->maxCountByMonth < count) 
 
552
            d->maxCountByMonth = count;
 
553
 
 
554
        // Stats Weeks values.
 
555
 
 
556
        it_YP = d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week));
 
557
        if ( it_YP == d->weekStatMap.end() )
 
558
        {
 
559
            count = it.data();
 
560
            d->weekStatMap.insert( TimeLineWidgetPriv::YearRefPair(yearForWeek, week), 
 
561
                                   TimeLineWidgetPriv::StatPair(count, Unselected) );
 
562
        }
 
563
        else
 
564
        {
 
565
            count = it_YP.data().first + it.data();
 
566
            d->weekStatMap.replace( TimeLineWidgetPriv::YearRefPair(yearForWeek, week), 
 
567
                                    TimeLineWidgetPriv::StatPair(count, it_YP.data().second) );
 
568
        }
 
569
 
 
570
        if (d->maxCountByWeek < count) 
 
571
            d->maxCountByWeek = count;
 
572
 
 
573
        // Stats Days values.
 
574
 
 
575
        it_YP = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day));
 
576
        if ( it_YP == d->dayStatMap.end() )
 
577
        {
 
578
            count = it.data();
 
579
            d->dayStatMap.insert( TimeLineWidgetPriv::YearRefPair(year, day), 
 
580
                                  TimeLineWidgetPriv::StatPair(count, Unselected) );
 
581
        }
 
582
        else
 
583
        {
 
584
            count = it_YP.data().first + it.data();
 
585
            d->dayStatMap.replace( TimeLineWidgetPriv::YearRefPair(year, day), 
 
586
                                   TimeLineWidgetPriv::StatPair(count, it_YP.data().second) );
 
587
        }
 
588
 
 
589
        if (d->maxCountByDay < count) 
 
590
            d->maxCountByDay = count;
 
591
    }
 
592
 
 
593
    if (!datesStatMap.isEmpty())
 
594
    {
 
595
        d->maxDateTime.setTime(QTime(0, 0, 0, 0));
 
596
        d->minDateTime.setTime(QTime(0, 0, 0, 0));
 
597
    }
 
598
    else
 
599
    {
 
600
        d->maxDateTime = d->refDateTime;
 
601
        d->minDateTime = d->refDateTime;
 
602
    }
 
603
 
 
604
    updatePixmap();
 
605
    update();
 
606
    emit signalDateMapChanged();
 
607
}
 
608
 
 
609
void TimeLineWidget::updatePixmap()
 
610
{
 
611
    // Drawing background and image.
 
612
    d->pixmap = QPixmap(size());
 
613
    d->pixmap.fill(palette().active().background());
 
614
 
 
615
    QPainter p(&d->pixmap);
 
616
 
 
617
    d->bottomMargin = (int)(p.fontMetrics().height()*1.5); 
 
618
    d->barWidth     = p.fontMetrics().width("00"); 
 
619
    d->nbItems      = (int)((width() / 2.0) / (float)d->barWidth);
 
620
    d->startPos     = (int)((width() / 2.0) - ((float)(d->barWidth) / 2.0));
 
621
    int dim         = height() - d->bottomMargin - d->topMargin;
 
622
    QDateTime ref   = d->refDateTime;
 
623
    ref.setTime(QTime(0, 0, 0, 0));
 
624
    double        max, logVal;
 
625
    int           val, top;
 
626
    SelectionMode sel;
 
627
    QRect         focusRect, selRect, barRect;
 
628
    QBrush        selBrush;
 
629
    QColor        dateColor, subDateColor;
 
630
 
 
631
    // Date histogram drawing is divided in 2 parts. The current date-time 
 
632
    // is placed on the center of the view and all dates on right are computed,
 
633
    // and in second time, all dates on the left.
 
634
 
 
635
    // Draw all dates on the right of ref. date-time.
 
636
 
 
637
    for (int i = 0 ; i < d->nbItems ; i++)
 
638
    {
 
639
        val = statForDateTime(ref, sel);
 
640
        max = (double)maxCount();
 
641
 
 
642
        if (d->scaleMode == TimeLineWidget::LogScale)
 
643
        {
 
644
            if (max > 0.0) max = log(max);
 
645
            else           max = 1.0;
 
646
 
 
647
            if (val <= 0) logVal = 0;
 
648
            else          logVal = log(val);
 
649
 
 
650
            top = lround(dim + d->topMargin - ((logVal * dim) / max));
 
651
 
 
652
            if (top < 0) val = 0;
 
653
        }
 
654
        else
 
655
        {
 
656
            top = lround(dim + d->topMargin - ((val * dim) / max));
 
657
        }
 
658
 
 
659
        barRect.setTop(top);
 
660
        barRect.setLeft(d->startPos + i*d->barWidth);
 
661
        barRect.setBottom(height() - d->bottomMargin);
 
662
        barRect.setRight(d->startPos + (i+1)*d->barWidth);
 
663
 
 
664
        if (ref == d->cursorDateTime)
 
665
            focusRect = barRect;
 
666
 
 
667
        if (ref > d->maxDateTime)
 
668
            dateColor = palette().active().mid();
 
669
        else 
 
670
            dateColor = palette().active().foreground(); 
 
671
 
 
672
        p.setPen(palette().active().foreground());
 
673
        p.fillRect(barRect, QBrush(ThemeEngine::instance()->textSpecialRegColor()));
 
674
        p.drawRect(barRect);
 
675
        p.drawLine(barRect.right(), barRect.bottom(), barRect.right(), barRect.bottom()+3);
 
676
        p.drawLine(barRect.left(),  barRect.bottom(), barRect.left(),  barRect.bottom()+3);
 
677
 
 
678
        if (val) 
 
679
        {
 
680
            if (sel)
 
681
                subDateColor = palette().active().highlightedText();
 
682
            else
 
683
                subDateColor = palette().active().foreground();
 
684
        }
 
685
        else 
 
686
            subDateColor = palette().active().mid();
 
687
 
 
688
        if (sel == Selected || sel == FuzzySelection)
 
689
        {
 
690
            selBrush.setColor(ThemeEngine::instance()->thumbSelColor());
 
691
            selBrush.setStyle(QBrush::SolidPattern);
 
692
            if (sel == FuzzySelection)
 
693
                selBrush.setStyle(QBrush::Dense4Pattern);
 
694
 
 
695
            selRect.setTop(height() - d->bottomMargin + 1);
 
696
            selRect.setLeft(d->startPos + i*d->barWidth);
 
697
            selRect.setBottom(height() - d->bottomMargin/2);
 
698
            selRect.setRight(d->startPos + (i+1)*d->barWidth);
 
699
            p.fillRect(selRect, selBrush);
 
700
        }
 
701
 
 
702
        switch(d->timeUnit)
 
703
        {
 
704
            case Day:
 
705
            {
 
706
                {
 
707
                    p.save();
 
708
                    QFont fnt = p.font();
 
709
                    fnt.setPointSize(fnt.pointSize()-4);
 
710
                    p.setFont(fnt);
 
711
                    p.setPen(subDateColor);
 
712
                    QString txt = QString(d->calendar->weekDayName(ref.date(), true)[0]);
 
713
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
714
                    p.drawText(barRect.left() + ((barRect.width()-br.width())/2),
 
715
                               barRect.bottom()+br.height(), txt);
 
716
                    p.restore();
 
717
                }
 
718
 
 
719
                if (d->calendar->dayOfWeek(ref.date()) == 1)
 
720
                {
 
721
                    p.setPen(dateColor);
 
722
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
723
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
724
                    QString txt = KGlobal::locale()->formatDate(ref.date(), true);
 
725
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
726
                    p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
727
                }
 
728
                break;
 
729
            }
 
730
            case Week:
 
731
            {
 
732
                int week = d->calendar->weekNumber(ref.date());
 
733
                {
 
734
                    p.save();
 
735
                    QFont fnt = p.font();
 
736
                    fnt.setPointSize(fnt.pointSize()-4);
 
737
                    p.setFont(fnt);
 
738
                    p.setPen(subDateColor);
 
739
                    QString txt = QString::number(week);
 
740
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
741
                    p.drawText(barRect.left() + ((barRect.width()-br.width())/2),
 
742
                               barRect.bottom()+br.height(), txt);
 
743
                    p.restore();
 
744
                }
 
745
 
 
746
                p.setPen(dateColor);
 
747
                if (week == 1 || week == 10 || week == 20 || week == 30 || week == 40 || week == 50)
 
748
                {
 
749
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
750
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
751
                    QString txt = KGlobal::locale()->formatDate(ref.date(), true);
 
752
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
753
                    if (week != 50)
 
754
                        p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
755
                }
 
756
                else if (week == 6 || week == 16 || week == 26 || week == 36 || week == 46)
 
757
                {
 
758
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
759
                               barRect.left(), barRect.bottom()+d->bottomMargin/4);
 
760
                }
 
761
                break;
 
762
            }
 
763
            case Month:
 
764
            {
 
765
                {
 
766
                    p.save();
 
767
                    QFont fnt = p.font();
 
768
                    fnt.setPointSize(fnt.pointSize()-4);
 
769
                    p.setFont(fnt);
 
770
                    p.setPen(subDateColor) ;
 
771
                    QString txt = QString(d->calendar->monthName(ref.date(), true)[0]);
 
772
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
773
                    p.drawText(barRect.left() + ((barRect.width()-br.width())/2),
 
774
                               barRect.bottom()+br.height(), txt);
 
775
                    p.restore();
 
776
                }
 
777
 
 
778
                p.setPen(dateColor);
 
779
                if (ref.date().month() == 1)
 
780
                {
 
781
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
782
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
783
                    QString txt = QString::number(ref.date().year());
 
784
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
785
                    p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
786
                }
 
787
                else if (ref.date().month() == 7)
 
788
                {
 
789
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
790
                               barRect.left(), barRect.bottom()+d->bottomMargin/4);
 
791
                }
 
792
                break;
 
793
            }
 
794
            case Year:
 
795
            {
 
796
                p.setPen(dateColor);
 
797
                if (ref.date().year() % 10 == 0)
 
798
                {
 
799
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
800
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
801
                    QString txt = QString::number(ref.date().year());
 
802
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
803
                    p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
804
                }
 
805
                else if (ref.date().year() % 5 == 0)
 
806
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
807
                               barRect.left(), barRect.bottom()+d->bottomMargin/4);
 
808
                break;
 
809
            }
 
810
        }
 
811
 
 
812
        ref = nextDateTime(ref);
 
813
    }
 
814
 
 
815
    // Draw all dates on the left of ref. date-time.
 
816
 
 
817
    ref = d->refDateTime;
 
818
    ref.setTime(QTime(0, 0, 0, 0));
 
819
    ref = prevDateTime(ref);
 
820
 
 
821
    for (int i = 0 ; i < d->nbItems-1 ; i++)
 
822
    {
 
823
        val = statForDateTime(ref, sel);
 
824
        max = (double)maxCount();
 
825
 
 
826
        if (d->scaleMode == TimeLineWidget::LogScale)
 
827
        {
 
828
            if (max > 0.0) max = log(max);
 
829
            else           max = 1.0;
 
830
 
 
831
            if (val <= 0) logVal = 0;
 
832
            else          logVal = log(val);
 
833
 
 
834
            top = lround(dim + d->topMargin - ((logVal * dim) / max));
 
835
 
 
836
            if (top < 0) val = 0;
 
837
        }
 
838
        else
 
839
        {
 
840
            top = lround(dim + d->topMargin - ((val * dim) / max));
 
841
        }
 
842
 
 
843
        barRect.setTop(top);
 
844
        barRect.setRight(d->startPos - i*d->barWidth);
 
845
        barRect.setBottom(height() - d->bottomMargin);
 
846
        barRect.setLeft(d->startPos - (i+1)*d->barWidth);
 
847
 
 
848
        if (ref == d->cursorDateTime)
 
849
            focusRect = barRect;
 
850
 
 
851
        if (ref < d->minDateTime)
 
852
            dateColor = palette().active().mid();
 
853
        else 
 
854
            dateColor = palette().active().foreground(); 
 
855
 
 
856
        p.setPen(palette().active().foreground());
 
857
        p.fillRect(barRect, QBrush(ThemeEngine::instance()->textSpecialRegColor()));
 
858
        p.drawRect(barRect);
 
859
        p.drawLine(barRect.right(), barRect.bottom(), barRect.right(), barRect.bottom()+3);
 
860
        p.drawLine(barRect.left(),  barRect.bottom(), barRect.left(),  barRect.bottom()+3);
 
861
 
 
862
        if (val) 
 
863
        {
 
864
            if (sel)
 
865
                subDateColor = palette().active().highlightedText();
 
866
            else
 
867
                subDateColor = palette().active().foreground();
 
868
        }
 
869
        else 
 
870
            subDateColor = palette().active().mid();
 
871
 
 
872
        if (sel == Selected || sel == FuzzySelection)
 
873
        {
 
874
            selBrush.setColor(ThemeEngine::instance()->thumbSelColor());
 
875
            selBrush.setStyle(QBrush::SolidPattern);
 
876
            if (sel == FuzzySelection)
 
877
                selBrush.setStyle(QBrush::Dense4Pattern);
 
878
 
 
879
            selRect.setTop(height() - d->bottomMargin + 1);
 
880
            selRect.setLeft(d->startPos - (i+1)*d->barWidth);
 
881
            selRect.setBottom(height() - d->bottomMargin/2);
 
882
            selRect.setRight(d->startPos - i*d->barWidth);
 
883
            p.fillRect(selRect, selBrush);
 
884
        }
 
885
 
 
886
        switch(d->timeUnit)
 
887
        {
 
888
            case Day:
 
889
            {
 
890
                {
 
891
                    p.save();
 
892
                    QFont fnt = p.font();
 
893
                    fnt.setPointSize(fnt.pointSize()-4);
 
894
                    p.setFont(fnt);
 
895
                    p.setPen(subDateColor) ;
 
896
                    QString txt = QString(d->calendar->weekDayName(ref.date(), true)[0]);
 
897
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
898
                    p.drawText(barRect.left() + ((barRect.width()-br.width())/2),
 
899
                               barRect.bottom()+br.height(), txt);
 
900
                    p.restore();
 
901
                }
 
902
 
 
903
                if (d->calendar->dayOfWeek(ref.date()) == 1)
 
904
                {
 
905
                    p.setPen(dateColor);
 
906
                    p.drawLine(barRect.left(), barRect.bottom(),
 
907
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
908
                    QString txt = KGlobal::locale()->formatDate(ref.date(), true);
 
909
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
910
                    p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
911
                }
 
912
                break;
 
913
            }
 
914
            case Week:
 
915
            {
 
916
                int week = d->calendar->weekNumber(ref.date());
 
917
                {
 
918
                    p.save();
 
919
                    QFont fnt = p.font();
 
920
                    fnt.setPointSize(fnt.pointSize()-4);
 
921
                    p.setFont(fnt);
 
922
                    p.setPen(subDateColor) ;
 
923
                    QString txt = QString::number(week);
 
924
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
925
                    p.drawText(barRect.left() + ((barRect.width()-br.width())/2),
 
926
                               barRect.bottom()+br.height(), txt);
 
927
                    p.restore();
 
928
                }
 
929
 
 
930
                p.setPen(dateColor);
 
931
                if (week == 1 || week == 10 || week == 20 || week == 30 || week == 40 || week == 50)
 
932
                {
 
933
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
934
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
935
                    QString txt = KGlobal::locale()->formatDate(ref.date(), true);
 
936
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
937
                    if (week != 50)
 
938
                        p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
939
                }
 
940
                else if (week == 6 || week == 16 || week == 26 || week == 36 || week == 46)
 
941
                {
 
942
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
943
                               barRect.left(), barRect.bottom()+d->bottomMargin/4);
 
944
                }
 
945
                break;
 
946
            }
 
947
            case Month:
 
948
            {
 
949
                {
 
950
                    p.save();
 
951
                    QFont fnt = p.font();
 
952
                    fnt.setPointSize(fnt.pointSize()-4);
 
953
                    p.setFont(fnt);
 
954
                    p.setPen(subDateColor) ;
 
955
                    QString txt = QString(d->calendar->monthName(ref.date(), true)[0]);
 
956
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
957
                    p.drawText(barRect.left() + ((barRect.width()-br.width())/2),
 
958
                               barRect.bottom()+br.height(), txt);
 
959
                    p.restore();
 
960
                }
 
961
 
 
962
                p.setPen(dateColor);
 
963
                if (ref.date().month() == 1)
 
964
                {
 
965
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
966
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
967
                    QString txt = QString::number(ref.date().year());
 
968
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
969
                    p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
970
                }
 
971
                else if (ref.date().month() == 7)
 
972
                {
 
973
                    p.drawLine(barRect.right(), barRect.bottom(), 
 
974
                               barRect.right(), barRect.bottom()+d->bottomMargin/4);
 
975
                }
 
976
                break;
 
977
            }
 
978
            case Year:
 
979
            {
 
980
                p.setPen(dateColor);
 
981
                if (ref.date().year() % 10 == 0)
 
982
                {
 
983
                    p.drawLine(barRect.left(), barRect.bottom(), 
 
984
                               barRect.left(), barRect.bottom()+d->bottomMargin/2);
 
985
                    QString txt = QString::number(ref.date().year());
 
986
                    QRect br    = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); 
 
987
                    p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
 
988
                }
 
989
                else if (ref.date().year() % 5 == 0)
 
990
                    p.drawLine(barRect.right(), barRect.bottom(), 
 
991
                               barRect.right(), barRect.bottom()+d->bottomMargin/4);
 
992
                break;
 
993
            }
 
994
        }
 
995
 
 
996
        ref = prevDateTime(ref);
 
997
    }
 
998
 
 
999
    // Draw cursor rectangle over current date-time.
 
1000
    if (focusRect.isValid())
 
1001
    {
 
1002
        focusRect.setTop(d->topMargin);
 
1003
        QPoint p1(focusRect.left(), height() - d->bottomMargin);
 
1004
        QPoint p2(focusRect.right(), height() - d->bottomMargin);
 
1005
        focusRect.setBottom(focusRect.bottom() + d->bottomMargin/2);
 
1006
 
 
1007
        p.setPen(palette().active().shadow());
 
1008
        p.drawLine(p1.x(), p1.y()+1, p2.x(), p2.y()+1);
 
1009
        p.drawRect(focusRect);
 
1010
 
 
1011
        focusRect.addCoords(-1,-1, 1, 1);
 
1012
        p.setPen(ThemeEngine::instance()->textSelColor());
 
1013
        p.drawRect(focusRect);
 
1014
        p.drawLine(p1.x()-1, p1.y(), p2.x()+1, p2.y());
 
1015
 
 
1016
        focusRect.addCoords(-1,-1, 1, 1);
 
1017
        p.drawRect(focusRect);
 
1018
        p.drawLine(p1.x()-1, p1.y()-1, p2.x()+1, p2.y()-1);
 
1019
 
 
1020
        focusRect.addCoords(-1,-1, 1, 1);
 
1021
        p.setPen(palette().active().shadow());
 
1022
        p.drawRect(focusRect);
 
1023
        p.drawLine(p1.x(), p1.y()-2, p2.x(), p2.y()-2);
 
1024
    }
 
1025
    p.end();
 
1026
}
 
1027
 
 
1028
QDateTime TimeLineWidget::prevDateTime(const QDateTime& dt)
 
1029
{
 
1030
    QDateTime prev;
 
1031
    switch(d->timeUnit)
 
1032
    {
 
1033
        case Day:
 
1034
        {
 
1035
            prev = dt.addDays(-1);
 
1036
            break;
 
1037
        }
 
1038
        case Week:
 
1039
        {
 
1040
            prev = dt.addDays(-7);
 
1041
            break;
 
1042
        }
 
1043
        case Month:
 
1044
        {
 
1045
            prev = dt.addMonths(-1);
 
1046
            break;
 
1047
        }
 
1048
        case Year:
 
1049
        {
 
1050
            prev = dt.addYears(-1);
 
1051
            break;
 
1052
        }
 
1053
    }
 
1054
    return prev;
 
1055
}
 
1056
 
 
1057
QDateTime TimeLineWidget::nextDateTime(const QDateTime& dt)
 
1058
{
 
1059
    QDateTime next;
 
1060
    switch(d->timeUnit)
 
1061
    {
 
1062
        case Day:
 
1063
        {
 
1064
            next = dt.addDays(1);
 
1065
            break;
 
1066
        }
 
1067
        case Week:
 
1068
        {
 
1069
            next = dt.addDays(7);
 
1070
            break;
 
1071
        }
 
1072
        case Month:
 
1073
        {
 
1074
            next = dt.addMonths(1);
 
1075
            break;
 
1076
        }
 
1077
        case Year:
 
1078
        {
 
1079
            next = dt.addYears(1);
 
1080
            break;
 
1081
        }
 
1082
    }
 
1083
    return next;
 
1084
}
 
1085
 
 
1086
int TimeLineWidget::maxCount()
 
1087
{
 
1088
    int max = 1;
 
1089
    switch(d->timeUnit)
 
1090
    {
 
1091
        case Day:
 
1092
        {
 
1093
            max = d->maxCountByDay;
 
1094
            break;
 
1095
        }
 
1096
        case Week:
 
1097
        {
 
1098
            max = d->maxCountByWeek;
 
1099
            break;
 
1100
        }
 
1101
        case Month:
 
1102
        {
 
1103
            max = d->maxCountByMonth;
 
1104
            break;
 
1105
        }
 
1106
        case Year:
 
1107
        {
 
1108
            max = d->maxCountByYear;
 
1109
            break;
 
1110
        }
 
1111
    }
 
1112
    return max;
 
1113
}
 
1114
 
 
1115
int TimeLineWidget::statForDateTime(const QDateTime& dt, SelectionMode& selected)
 
1116
{
 
1117
    int count = 0;
 
1118
    int year  = dt.date().year();
 
1119
    int month = dt.date().month();
 
1120
    int day   = d->calendar->dayOfYear(dt.date());
 
1121
    int yearForWeek;  // Used with week shared between 2 years decade (Dec/Jan).
 
1122
    int week  = d->calendar->weekNumber(dt.date(), &yearForWeek);
 
1123
    selected  = Unselected;
 
1124
 
 
1125
    switch(d->timeUnit)
 
1126
    {
 
1127
        case Day:
 
1128
        {
 
1129
            QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it =
 
1130
                d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day));
 
1131
            if ( it != d->dayStatMap.end() )
 
1132
            {
 
1133
                count    = it.data().first;
 
1134
                selected = it.data().second;
 
1135
            }
 
1136
            break;
 
1137
        }
 
1138
        case Week:
 
1139
        {
 
1140
            QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it =
 
1141
                d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week));
 
1142
            if ( it != d->weekStatMap.end() )
 
1143
            {
 
1144
                count    = it.data().first;
 
1145
                selected = it.data().second;
 
1146
            }
 
1147
            break;
 
1148
        }
 
1149
        case Month:
 
1150
        {
 
1151
            QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it =
 
1152
                d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month));
 
1153
            if ( it != d->monthStatMap.end() )
 
1154
            {
 
1155
                count    = it.data().first;
 
1156
                selected = it.data().second;
 
1157
            }
 
1158
            break;
 
1159
        }
 
1160
        case Year:
 
1161
        {
 
1162
            QMap<int, TimeLineWidgetPriv::StatPair>::iterator it = d->yearStatMap.find(year);
 
1163
            if ( it != d->yearStatMap.end() )
 
1164
            {
 
1165
                count    = it.data().first;
 
1166
                selected = it.data().second;
 
1167
            }
 
1168
            break;
 
1169
        }
 
1170
    }
 
1171
 
 
1172
    return count;
 
1173
}
 
1174
 
 
1175
void TimeLineWidget::setDateTimeSelected(const QDateTime& dt, SelectionMode selected)
 
1176
{
 
1177
    int year  = dt.date().year();
 
1178
    int month = dt.date().month();
 
1179
    int yearForWeek;  // Used with week shared between 2 years decade (Dec/Jan).
 
1180
    int week  = d->calendar->weekNumber(dt.date(), &yearForWeek);
 
1181
 
 
1182
    QDateTime dts, dte;
 
1183
 
 
1184
    switch(d->timeUnit)
 
1185
    {
 
1186
        case Day:
 
1187
        {
 
1188
            dts = dt;
 
1189
            dte = dts.addDays(1);
 
1190
            setDaysRangeSelection(dts, dte, selected);
 
1191
            break;
 
1192
        }
 
1193
        case Week:
 
1194
        {
 
1195
            dts = firstDayOfWeek(yearForWeek, week);
 
1196
            dte = dts.addDays(7);
 
1197
            setDaysRangeSelection(dts, dte, selected);
 
1198
            updateWeekSelection(dts, dte);
 
1199
            break;
 
1200
        }
 
1201
        case Month:
 
1202
        {
 
1203
            dts = QDateTime(QDate(year, month, 1));
 
1204
            dte = dts.addDays(d->calendar->daysInMonth(dts.date()));
 
1205
            setDaysRangeSelection(dts, dte, selected);
 
1206
            updateMonthSelection(dts, dte);
 
1207
            break;
 
1208
        }
 
1209
        case Year:
 
1210
        {
 
1211
            dts = QDateTime(QDate(year, 1, 1));
 
1212
            dte = dts.addDays(d->calendar->daysInYear(dts.date()));
 
1213
            setDaysRangeSelection(dts, dte, selected);
 
1214
            updateYearSelection(dts, dte);
 
1215
            break;
 
1216
        }
 
1217
    }
 
1218
}
 
1219
 
 
1220
void TimeLineWidget::updateWeekSelection(const QDateTime dts, const QDateTime dte)
 
1221
{
 
1222
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
 
1223
    QDateTime dtsWeek, dteWeek, dt;
 
1224
    int week;
 
1225
    int yearForWeek;  // Used with week shared between 2 years decade (Dec/Jan).
 
1226
    dt = dts;
 
1227
    do
 
1228
    {
 
1229
        yearForWeek = dt.date().year();
 
1230
        week = d->calendar->weekNumber(dt.date(), &yearForWeek);
 
1231
 
 
1232
        dtsWeek = firstDayOfWeek(yearForWeek, week);
 
1233
        dteWeek = dtsWeek.addDays(7);
 
1234
        it  = d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week));
 
1235
        if ( it != d->weekStatMap.end() )
 
1236
            it.data().second = checkSelectionForDaysRange(dtsWeek, dteWeek);
 
1237
 
 
1238
        dt = dt.addDays(7);
 
1239
    }
 
1240
    while (dt <= dte);
 
1241
}
 
1242
 
 
1243
void TimeLineWidget::updateMonthSelection(const QDateTime dts, const QDateTime dte)
 
1244
{
 
1245
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
 
1246
    QDateTime dtsMonth, dteMonth, dt;
 
1247
    int       year, month;
 
1248
    dt = dts;
 
1249
    do
 
1250
    {
 
1251
        year  = dt.date().year();
 
1252
        month = dt.date().month();
 
1253
 
 
1254
        dtsMonth = QDateTime(QDate(year, month, 1));
 
1255
        dteMonth = dtsMonth.addDays(d->calendar->daysInMonth(dtsMonth.date()));
 
1256
        it  = d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month));
 
1257
        if ( it != d->monthStatMap.end() )
 
1258
            it.data().second = checkSelectionForDaysRange(dtsMonth, dteMonth);
 
1259
 
 
1260
        dt = dteMonth;
 
1261
    }
 
1262
    while (dt <= dte);
 
1263
}
 
1264
 
 
1265
void TimeLineWidget::updateYearSelection(const QDateTime dts, const QDateTime dte)
 
1266
{
 
1267
    QMap<int, TimeLineWidgetPriv::StatPair>::iterator it;
 
1268
    QDateTime dtsYear, dteYear, dt;
 
1269
    int       year;
 
1270
    dt = dts;
 
1271
    do
 
1272
    {
 
1273
        year = dt.date().year();
 
1274
 
 
1275
        dtsYear = QDateTime(QDate(year, 1, 1));
 
1276
        dteYear = dtsYear.addDays(d->calendar->daysInYear(dtsYear.date()));
 
1277
        it = d->yearStatMap.find(year);
 
1278
        if ( it != d->yearStatMap.end() )
 
1279
            it.data().second = checkSelectionForDaysRange(dtsYear, dteYear);
 
1280
 
 
1281
        dt = dteYear;
 
1282
    }
 
1283
    while (dt <= dte);
 
1284
}
 
1285
 
 
1286
void TimeLineWidget::updateAllSelection()
 
1287
{
 
1288
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
 
1289
    QDateTime dts, dte;
 
1290
    QDate     date;
 
1291
 
 
1292
    for (it = d->dayStatMap.begin() ; it != d->dayStatMap.end(); ++it)
 
1293
    {
 
1294
        if (it.data().second == Selected)
 
1295
        {
 
1296
            date = QDate(it.key().first, 1, 1);
 
1297
            date = date.addDays(it.key().second-1);
 
1298
            dts  = QDateTime(date);
 
1299
            dte  = dts.addDays(1); 
 
1300
            updateWeekSelection(dts, dte);
 
1301
            updateMonthSelection(dts, dte);
 
1302
            updateYearSelection(dts, dte);
 
1303
        }
 
1304
    }
 
1305
}
 
1306
 
 
1307
void TimeLineWidget::setDaysRangeSelection(const QDateTime dts, const QDateTime dte, SelectionMode selected)
 
1308
{
 
1309
    int year, day;
 
1310
    QDateTime dt = dts;
 
1311
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
 
1312
 
 
1313
    do
 
1314
    {
 
1315
        year = dt.date().year();
 
1316
        day  = d->calendar->dayOfYear(dt.date());
 
1317
        it = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day));
 
1318
        if ( it != d->dayStatMap.end() )
 
1319
            it.data().second = selected;
 
1320
 
 
1321
        dt = dt.addDays(1);
 
1322
    }
 
1323
    while(dt < dte);
 
1324
}
 
1325
 
 
1326
TimeLineWidget::SelectionMode TimeLineWidget::checkSelectionForDaysRange(const QDateTime dts, const QDateTime dte)
 
1327
{
 
1328
    int year, day;
 
1329
    int items         = 0;
 
1330
    int itemsFuz      = 0;
 
1331
    int itemsSel      = 0;
 
1332
    QDateTime dt      = dts;
 
1333
    QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
 
1334
 
 
1335
    do
 
1336
    {
 
1337
        year = dt.date().year();
 
1338
        day  = d->calendar->dayOfYear(dt.date());
 
1339
 
 
1340
        it = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day));
 
1341
        if ( it != d->dayStatMap.end() )
 
1342
        {
 
1343
            items++;
 
1344
 
 
1345
            if (it.data().second != Unselected)
 
1346
            {
 
1347
                if (it.data().second == FuzzySelection)
 
1348
                    itemsFuz++;
 
1349
                else
 
1350
                    itemsSel++;
 
1351
            }
 
1352
        }
 
1353
        dt = dt.addDays(1);
 
1354
    }
 
1355
    while (dt < dte);
 
1356
 
 
1357
    if (items == 0)
 
1358
        return Unselected;
 
1359
 
 
1360
    if (itemsFuz == 0 && itemsSel == 0)
 
1361
        return Unselected;
 
1362
 
 
1363
    if (itemsFuz > 0)
 
1364
        return FuzzySelection;
 
1365
 
 
1366
    if (items > itemsSel)
 
1367
        return FuzzySelection;
 
1368
 
 
1369
    return Selected;
 
1370
}
 
1371
 
 
1372
void TimeLineWidget::paintEvent(QPaintEvent*)
 
1373
{
 
1374
    bitBlt(this, 0, 0, &d->pixmap);
 
1375
}
 
1376
 
 
1377
void TimeLineWidget::resizeEvent(QResizeEvent*)
 
1378
{
 
1379
    updatePixmap();
 
1380
}
 
1381
 
 
1382
void TimeLineWidget::slotBackward()
 
1383
{
 
1384
    QDateTime ref = d->refDateTime;
 
1385
 
 
1386
    switch(d->timeUnit)
 
1387
    {
 
1388
        case Day:
 
1389
        {
 
1390
            for (int i = 0; i < 7; i++)
 
1391
                ref = prevDateTime(ref);
 
1392
            break;
 
1393
        }
 
1394
        case Week:
 
1395
        {
 
1396
            for (int i = 0; i < 4; i++)
 
1397
                ref = prevDateTime(ref);
 
1398
            break;
 
1399
        }
 
1400
        case Month:
 
1401
        {
 
1402
            for (int i = 0; i < 12; i++)
 
1403
                ref = prevDateTime(ref);
 
1404
            break;
 
1405
        }
 
1406
        case Year:
 
1407
        {
 
1408
            for (int i = 0; i < 5; i++)
 
1409
                ref = prevDateTime(ref);
 
1410
            break;
 
1411
        }
 
1412
    }
 
1413
 
 
1414
    if (ref < d->minDateTime)
 
1415
        ref = d->minDateTime;
 
1416
 
 
1417
    setRefDateTime(ref);
 
1418
}
 
1419
 
 
1420
void TimeLineWidget::slotPrevious()
 
1421
{
 
1422
    if (d->refDateTime <= d->minDateTime)
 
1423
        return;
 
1424
    QDateTime ref = prevDateTime(d->refDateTime);
 
1425
    setRefDateTime(ref);
 
1426
}
 
1427
 
 
1428
void TimeLineWidget::slotNext()
 
1429
{
 
1430
    if (d->refDateTime >= d->maxDateTime)
 
1431
        return;
 
1432
    QDateTime ref = nextDateTime(d->refDateTime);
 
1433
    setRefDateTime(ref);
 
1434
}
 
1435
 
 
1436
void TimeLineWidget::slotForward()
 
1437
{
 
1438
    QDateTime ref = d->refDateTime;
 
1439
 
 
1440
    switch(d->timeUnit)
 
1441
    {
 
1442
        case Day:
 
1443
        {
 
1444
            for (int i = 0; i < 7; i++)
 
1445
                ref = nextDateTime(ref);
 
1446
            break;
 
1447
        }
 
1448
        case Week:
 
1449
        {
 
1450
            for (int i = 0; i < 4; i++)
 
1451
                ref = nextDateTime(ref);
 
1452
            break;
 
1453
        }
 
1454
        case Month:
 
1455
        {
 
1456
            for (int i = 0; i < 12; i++)
 
1457
                ref = nextDateTime(ref);
 
1458
            break;
 
1459
        }
 
1460
        case Year:
 
1461
        {
 
1462
            for (int i = 0; i < 5; i++)
 
1463
                ref = nextDateTime(ref);
 
1464
            break;
 
1465
        }
 
1466
    }
 
1467
 
 
1468
    if (ref > d->maxDateTime)
 
1469
        ref = d->maxDateTime;
 
1470
 
 
1471
    setRefDateTime(ref);
 
1472
}
 
1473
 
 
1474
void TimeLineWidget::wheelEvent(QWheelEvent* e)
 
1475
{
 
1476
    if (e->delta() < 0)
 
1477
    {
 
1478
        if (e->state() & Qt::ShiftButton)
 
1479
            slotForward();
 
1480
        else
 
1481
            slotNext();
 
1482
    }
 
1483
 
 
1484
    if (e->delta() > 0)
 
1485
    {
 
1486
        if (e->state() & Qt::ShiftButton)
 
1487
            slotBackward();
 
1488
        else
 
1489
            slotPrevious();
 
1490
    }
 
1491
}
 
1492
 
 
1493
void TimeLineWidget::mousePressEvent(QMouseEvent *e)
 
1494
{
 
1495
    if (e->button() == Qt::LeftButton)
 
1496
    {
 
1497
        QPoint pt(e->x(), e->y());
 
1498
 
 
1499
        bool ctrlPressed    = e->state() & Qt::ControlButton;
 
1500
        QDateTime ref       = dateTimeForPoint(pt, d->selMouseEvent);
 
1501
        d->selStartDateTime = QDateTime();
 
1502
        if (d->selMouseEvent) 
 
1503
        {
 
1504
            if (!ctrlPressed)
 
1505
                resetSelection();
 
1506
 
 
1507
            d->selStartDateTime = ref;
 
1508
            d->selMinDateTime   = ref;
 
1509
            d->selMaxDateTime   = ref;
 
1510
            setDateTimeSelected(ref, Selected);
 
1511
        }
 
1512
 
 
1513
        if (!ref.isNull())
 
1514
            setCursorDateTime(ref);
 
1515
 
 
1516
        d->validMouseEvent = true;
 
1517
        updatePixmap();
 
1518
        update();
 
1519
    }
 
1520
}
 
1521
 
 
1522
void TimeLineWidget::mouseMoveEvent(QMouseEvent *e)
 
1523
{
 
1524
    if (d->validMouseEvent == true)
 
1525
    {
 
1526
        QPoint pt(e->x(), e->y());
 
1527
 
 
1528
        bool sel;
 
1529
        QDateTime selEndDateTime = dateTimeForPoint(pt, sel);
 
1530
        setCursorDateTime(selEndDateTime);
 
1531
 
 
1532
        // Clamp start and end date-time of current contiguous selection.
 
1533
 
 
1534
        if (!selEndDateTime.isNull() && !d->selStartDateTime.isNull())
 
1535
        {
 
1536
            if (selEndDateTime > d->selStartDateTime && 
 
1537
                selEndDateTime > d->selMaxDateTime)
 
1538
            {
 
1539
                d->selMaxDateTime = selEndDateTime;
 
1540
            }
 
1541
            else if (selEndDateTime < d->selStartDateTime && 
 
1542
                     selEndDateTime < d->selMinDateTime)
 
1543
            {
 
1544
                d->selMinDateTime = selEndDateTime;
 
1545
            }
 
1546
 
 
1547
            QDateTime dt = d->selMinDateTime;
 
1548
            do
 
1549
            {
 
1550
                setDateTimeSelected(dt, Unselected);
 
1551
                dt = nextDateTime(dt);
 
1552
            }
 
1553
            while(dt <= d->selMaxDateTime);
 
1554
        }
 
1555
 
 
1556
        // Now perform selections on Date Maps.
 
1557
 
 
1558
        if (d->selMouseEvent)
 
1559
        {
 
1560
            if (!d->selStartDateTime.isNull() && !selEndDateTime.isNull())
 
1561
            {
 
1562
                QDateTime dt = d->selStartDateTime;
 
1563
                if (selEndDateTime > d->selStartDateTime)
 
1564
                {
 
1565
                    do
 
1566
                    {
 
1567
                        setDateTimeSelected(dt, Selected);
 
1568
                        dt = nextDateTime(dt);
 
1569
                    }
 
1570
                    while(dt <= selEndDateTime);
 
1571
                }
 
1572
                else
 
1573
                {
 
1574
                    do
 
1575
                    {
 
1576
                        setDateTimeSelected(dt, Selected);
 
1577
                        dt = prevDateTime(dt);
 
1578
                    }
 
1579
                    while(dt >= selEndDateTime);
 
1580
                }
 
1581
            }
 
1582
        }
 
1583
 
 
1584
        updatePixmap();
 
1585
        update();
 
1586
    }
 
1587
}
 
1588
 
 
1589
void TimeLineWidget::mouseReleaseEvent(QMouseEvent*)
 
1590
{
 
1591
    d->validMouseEvent = false;
 
1592
 
 
1593
    // Only dispatch changes about selection when user release mouse selection
 
1594
    // to prevent multiple queries on database.
 
1595
    if (d->selMouseEvent)
 
1596
    {
 
1597
        updateAllSelection();
 
1598
        emit signalSelectionChanged();
 
1599
    }
 
1600
 
 
1601
    d->selMouseEvent = false;
 
1602
}
 
1603
 
 
1604
QDateTime TimeLineWidget::dateTimeForPoint(const QPoint& pt, bool &isOnSelectionArea)
 
1605
{
 
1606
    QRect barRect, selRect;
 
1607
    isOnSelectionArea = false;
 
1608
 
 
1609
    // Check on the right of reference date.
 
1610
 
 
1611
    QDateTime ref = d->refDateTime;
 
1612
    ref.setTime(QTime(0, 0, 0, 0));
 
1613
 
 
1614
    QRect deskRect = KGlobalSettings::desktopGeometry(this);
 
1615
    int items = deskRect.width() / d->barWidth;
 
1616
 
 
1617
    for (int i = 0 ; i < items ; i++)
 
1618
    {
 
1619
        barRect.setTop(0);
 
1620
        barRect.setLeft(d->startPos + i*d->barWidth);
 
1621
        barRect.setBottom(height() - d->bottomMargin + 1);
 
1622
        barRect.setRight(d->startPos + (i+1)*d->barWidth);
 
1623
 
 
1624
        selRect.setTop(height() - d->bottomMargin + 1);
 
1625
        selRect.setLeft(d->startPos + i*d->barWidth);
 
1626
        selRect.setBottom(height());
 
1627
        selRect.setRight(d->startPos + (i+1)*d->barWidth);
 
1628
 
 
1629
        if (selRect.contains(pt))
 
1630
            isOnSelectionArea = true;
 
1631
 
 
1632
        if (barRect.contains(pt) || selRect.contains(pt))
 
1633
        {
 
1634
            if (i >= d->nbItems)
 
1635
            {
 
1636
                // Point is outside visible widget area. We scrolling widget contents.
 
1637
                slotNext();
 
1638
            }
 
1639
 
 
1640
            return ref;
 
1641
        }
 
1642
 
 
1643
        ref = nextDateTime(ref);
 
1644
    }
 
1645
 
 
1646
    // Check on the left of reference date.
 
1647
 
 
1648
    ref = d->refDateTime;
 
1649
    ref.setTime(QTime(0, 0, 0, 0));
 
1650
    ref = prevDateTime(ref);
 
1651
 
 
1652
    for (int i = 0 ; i < items ; i++)
 
1653
    {
 
1654
        barRect.setTop(0);
 
1655
        barRect.setRight(d->startPos - i*d->barWidth);
 
1656
        barRect.setBottom(height() - d->bottomMargin + 1);
 
1657
        barRect.setLeft(d->startPos - (i+1)*d->barWidth);
 
1658
 
 
1659
        selRect.setTop(height() - d->bottomMargin + 1);
 
1660
        selRect.setLeft(d->startPos - (i+1)*d->barWidth);
 
1661
        selRect.setBottom(height());
 
1662
        selRect.setRight(d->startPos - i*d->barWidth);
 
1663
 
 
1664
        if (selRect.contains(pt))
 
1665
            isOnSelectionArea = true;
 
1666
 
 
1667
        if (barRect.contains(pt) || selRect.contains(pt))
 
1668
        {
 
1669
            if (i >= d->nbItems-1)
 
1670
            {
 
1671
                // Point is outside visible widget area. We scrolling widget contents.
 
1672
                slotPrevious();
 
1673
            }
 
1674
 
 
1675
            return ref;
 
1676
        }
 
1677
 
 
1678
        ref = prevDateTime(ref);
 
1679
    }
 
1680
 
 
1681
    return QDateTime();
 
1682
}
 
1683
 
 
1684
QDateTime TimeLineWidget::firstDayOfWeek(int year, int weekNumber)
 
1685
{
 
1686
    // Search the first day of first week of year.
 
1687
    // We start to scan from 1st december of year-1 because 
 
1688
    // first week of year OR last week of year-1 can be shared 
 
1689
    // between year-1 and year.
 
1690
    QDateTime d1(QDate(year-1, 12, 1));
 
1691
    QDateTime dt = d1;
 
1692
    int weekYear = 0;
 
1693
    int weekNum  = 0;
 
1694
 
 
1695
    do
 
1696
    {
 
1697
        dt      = dt.addDays(1);
 
1698
        weekNum = d->calendar->weekNumber(dt.date(), &weekYear);
 
1699
    }
 
1700
    while(weekNum != 1 && weekYear != year);
 
1701
 
 
1702
    dt = dt.addDays((weekNumber-1)*7);
 
1703
 
 
1704
/*
 
1705
    DDebug() << "Year= " << year << " Week= " << weekNumber 
 
1706
             << " 1st day= " << dt << endl;
 
1707
*/
 
1708
 
 
1709
    return dt;
 
1710
}
 
1711
 
 
1712
void TimeLineWidget::slotThemeChanged()
 
1713
{
 
1714
    updatePixmap();
 
1715
    update();
 
1716
}
 
1717
 
 
1718
}  // NameSpace Digikam