1
/* ============================================================
3
* This file is a part of digiKam project
4
* http://www.digikam.org
7
* Description : a widget to display date and time statistics of pictures
9
* Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
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)
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.
22
* ============================================================ */
39
#include <kcalendarsystem.h>
40
#include <kglobalsettings.h>
45
#include "themeengine.h"
46
#include "timelinewidget.h"
47
#include "timelinewidget.moc"
52
class TimeLineWidgetPriv
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.
64
validMouseEvent = false;
65
selMouseEvent = false;
75
timeUnit = TimeLineWidget::Month;
76
scaleMode = TimeLineWidget::LinScale;
77
calendar = KGlobal::locale()->calendar();
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.
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.
101
QPixmap pixmap; // Used for widget double buffering.
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.
108
const KCalendarSystem *calendar;
110
TimeLineWidget::TimeUnit timeUnit;
111
TimeLineWidget::ScaleMode scaleMode;
114
TimeLineWidget::TimeLineWidget(QWidget *parent)
115
: QWidget(parent, 0, Qt::WDestructiveClose)
117
d = new TimeLineWidgetPriv;
118
setBackgroundMode(Qt::NoBackground);
119
setMouseTracking(true);
120
setMinimumWidth(256);
121
setMinimumHeight(192);
123
QDateTime ref = QDateTime::currentDateTime();
124
setCursorDateTime(ref);
127
connect(ThemeEngine::instance(), SIGNAL(signalThemeChanged()),
128
this, SLOT(slotThemeChanged()));
131
TimeLineWidget::~TimeLineWidget()
136
void TimeLineWidget::setTimeUnit(TimeUnit timeUnit)
138
d->timeUnit = timeUnit;
139
setCursorDateTime(cursorDateTime());
140
setRefDateTime(cursorDateTime());
143
TimeLineWidget::TimeUnit TimeLineWidget::timeUnit() const
148
void TimeLineWidget::setScaleMode(ScaleMode scaleMode)
150
d->scaleMode = scaleMode;
155
TimeLineWidget::ScaleMode TimeLineWidget::scaleMode() const
160
int TimeLineWidget::totalIndex()
162
if (d->minDateTime.isNull() || d->maxDateTime.isNull())
166
QDateTime dt = d->minDateTime;
170
dt = nextDateTime(dt);
173
while(dt < d->maxDateTime);
178
int TimeLineWidget::indexForDateTime(const QDateTime& date)
180
if (d->minDateTime.isNull() || d->maxDateTime.isNull() || date.isNull())
184
QDateTime dt = d->minDateTime;
188
dt = nextDateTime(dt);
196
int TimeLineWidget::indexForRefDateTime()
198
return (indexForDateTime(d->refDateTime));
201
int TimeLineWidget::indexForCursorDateTime()
203
return (indexForDateTime(d->cursorDateTime));
206
void TimeLineWidget::setCurrentIndex(int index)
208
if (d->minDateTime.isNull() || d->maxDateTime.isNull())
212
QDateTime dt = d->minDateTime;
216
dt = nextDateTime(dt);
224
void TimeLineWidget::setCursorDateTime(const QDateTime& dateTime)
226
QDateTime dt = dateTime;
227
dt.setTime(QTime(0, 0, 0, 0));
233
// Go to the first day of week.
235
int weekNb = d->calendar->weekNumber(dt.date(), &weekYear);
236
dt = firstDayOfWeek(weekYear, weekNb);
241
// Go to the first day of month.
242
dt.setDate(QDate(dt.date().year(), dt.date().month(), 1));
247
// Go to the first day of year.
248
dt.setDate(QDate(dt.date().year(), 1, 1));
255
if (d->cursorDateTime == dt)
258
d->cursorDateTime = dt;
260
emit signalCursorPositionChanged();
263
QDateTime TimeLineWidget::cursorDateTime() const
265
return d->cursorDateTime;
268
int TimeLineWidget::cursorInfo(QString& infoDate)
270
SelectionMode selected;
271
QDateTime dt = cursorDateTime();
277
infoDate = KGlobal::locale()->formatDate(dt.date());
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));
290
infoDate = QString("%1 %2")
291
.arg(d->calendar->monthName(dt.date()))
292
.arg(d->calendar->yearString(dt.date(), false));
297
infoDate = d->calendar->yearString(dt.date(), false);
302
return statForDateTime(dt, selected);
305
void TimeLineWidget::setRefDateTime(const QDateTime& dateTime)
307
QDateTime dt = dateTime;
308
dt.setTime(QTime(0, 0, 0, 0));
314
// Go to the first day of week.
315
int dayWeekOffset = (-1) * (d->calendar->dayOfWeek(dt.date()) - 1);
316
dt = dt.addDays(dayWeekOffset);
321
// Go to the first day of month.
322
dt.setDate(QDate(dt.date().year(), dt.date().month(), 1));
327
// Go to the first day of year.
328
dt.setDate(QDate(dt.date().year(), 1, 1));
338
emit signalRefDateTimeChanged();
341
void TimeLineWidget::slotResetSelection()
348
void TimeLineWidget::resetSelection()
350
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
352
for (it = d->dayStatMap.begin() ; it != d->dayStatMap.end(); ++it)
353
it.data().second = Unselected;
355
for (it = d->weekStatMap.begin() ; it != d->weekStatMap.end(); ++it)
356
it.data().second = Unselected;
358
for (it = d->monthStatMap.begin() ; it != d->monthStatMap.end(); ++it)
359
it.data().second = Unselected;
361
QMap<int, TimeLineWidgetPriv::StatPair>::iterator it2;
363
for (it2 = d->yearStatMap.begin() ; it2 != d->yearStatMap.end(); ++it2)
364
it2.data().second = Unselected;
367
void TimeLineWidget::setSelectedDateRange(const DateRangeList& list)
374
QDateTime start, end, dt;
375
DateRangeList::const_iterator it;
377
for (it = list.begin() ; it != list.end(); ++it)
386
setDateTimeSelected(dt, Selected);
397
DateRangeList TimeLineWidget::selectedDateRange(int& totalCount)
399
// We will parse all selections done on Days stats map.
403
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it3;
407
for (it3 = d->dayStatMap.begin() ; it3 != d->dayStatMap.end(); ++it3)
409
if (it3.data().second == Selected)
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;
420
DateRangeList::iterator it, it2;
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;
427
DDebug() << "Total Count of Items = " << totalCount << endl;
430
// Group contiguous date ranges to optimize query on database.
433
QDateTime first, second, first2, second2;
435
for (it = list.begin() ; it != list.end(); ++it)
438
second = (*it).second;
443
if (it2 != list.end())
445
first2 = (*it2).first;
446
second2 = (*it2).second;
448
if (first2 == second)
457
while(it2 != list.end());
459
list2.append(DateRange(first, second));
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;
471
void TimeLineWidget::slotDatesMap(const QMap<QDateTime, int>& datesStatMap)
473
// Clear all counts in all stats maps before to update it. Do not clear selections.
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;
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;
483
for ( it_YP = d->weekStatMap.begin() ; it_YP != d->weekStatMap.end(); ++it_YP )
484
it_YP.data().first = 0;
486
for ( it_YP = d->dayStatMap.begin() ; it_YP != d->dayStatMap.end(); ++it_YP )
487
it_YP.data().first = 0;
489
// Parse all new Date stamp and store histogram stats relevant in maps.
492
QMap<QDateTime, int>::const_iterator it;
493
if (datesStatMap.isEmpty())
495
d->minDateTime = QDateTime();
496
d->maxDateTime = QDateTime();
500
d->minDateTime = datesStatMap.begin().key();
501
d->maxDateTime = datesStatMap.begin().key();
504
for ( it = datesStatMap.begin(); it != datesStatMap.end(); ++it )
506
if (it.key() > d->maxDateTime)
507
d->maxDateTime = it.key();
509
if (it.key() < d->minDateTime)
510
d->minDateTime = it.key();
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);
518
// Stats Years values.
520
it_iP = d->yearStatMap.find(year);
521
if ( it_iP == d->yearStatMap.end() )
524
d->yearStatMap.insert( year, TimeLineWidgetPriv::StatPair(count, Unselected) );
528
count = it_iP.data().first + it.data();
529
d->yearStatMap.replace( year, TimeLineWidgetPriv::StatPair(count, it_iP.data().second) );
532
if (d->maxCountByYear < count)
533
d->maxCountByYear = count;
535
// Stats Months values.
537
it_YP = d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month));
538
if ( it_YP == d->monthStatMap.end() )
541
d->monthStatMap.insert( TimeLineWidgetPriv::YearRefPair(year, month),
542
TimeLineWidgetPriv::StatPair(count, Unselected) );
546
count = it_YP.data().first + it.data();
547
d->monthStatMap.replace( TimeLineWidgetPriv::YearRefPair(year, month),
548
TimeLineWidgetPriv::StatPair(count, it_YP.data().second) );
551
if (d->maxCountByMonth < count)
552
d->maxCountByMonth = count;
554
// Stats Weeks values.
556
it_YP = d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week));
557
if ( it_YP == d->weekStatMap.end() )
560
d->weekStatMap.insert( TimeLineWidgetPriv::YearRefPair(yearForWeek, week),
561
TimeLineWidgetPriv::StatPair(count, Unselected) );
565
count = it_YP.data().first + it.data();
566
d->weekStatMap.replace( TimeLineWidgetPriv::YearRefPair(yearForWeek, week),
567
TimeLineWidgetPriv::StatPair(count, it_YP.data().second) );
570
if (d->maxCountByWeek < count)
571
d->maxCountByWeek = count;
573
// Stats Days values.
575
it_YP = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day));
576
if ( it_YP == d->dayStatMap.end() )
579
d->dayStatMap.insert( TimeLineWidgetPriv::YearRefPair(year, day),
580
TimeLineWidgetPriv::StatPair(count, Unselected) );
584
count = it_YP.data().first + it.data();
585
d->dayStatMap.replace( TimeLineWidgetPriv::YearRefPair(year, day),
586
TimeLineWidgetPriv::StatPair(count, it_YP.data().second) );
589
if (d->maxCountByDay < count)
590
d->maxCountByDay = count;
593
if (!datesStatMap.isEmpty())
595
d->maxDateTime.setTime(QTime(0, 0, 0, 0));
596
d->minDateTime.setTime(QTime(0, 0, 0, 0));
600
d->maxDateTime = d->refDateTime;
601
d->minDateTime = d->refDateTime;
606
emit signalDateMapChanged();
609
void TimeLineWidget::updatePixmap()
611
// Drawing background and image.
612
d->pixmap = QPixmap(size());
613
d->pixmap.fill(palette().active().background());
615
QPainter p(&d->pixmap);
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));
627
QRect focusRect, selRect, barRect;
629
QColor dateColor, subDateColor;
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.
635
// Draw all dates on the right of ref. date-time.
637
for (int i = 0 ; i < d->nbItems ; i++)
639
val = statForDateTime(ref, sel);
640
max = (double)maxCount();
642
if (d->scaleMode == TimeLineWidget::LogScale)
644
if (max > 0.0) max = log(max);
647
if (val <= 0) logVal = 0;
648
else logVal = log(val);
650
top = lround(dim + d->topMargin - ((logVal * dim) / max));
652
if (top < 0) val = 0;
656
top = lround(dim + d->topMargin - ((val * dim) / max));
660
barRect.setLeft(d->startPos + i*d->barWidth);
661
barRect.setBottom(height() - d->bottomMargin);
662
barRect.setRight(d->startPos + (i+1)*d->barWidth);
664
if (ref == d->cursorDateTime)
667
if (ref > d->maxDateTime)
668
dateColor = palette().active().mid();
670
dateColor = palette().active().foreground();
672
p.setPen(palette().active().foreground());
673
p.fillRect(barRect, QBrush(ThemeEngine::instance()->textSpecialRegColor()));
675
p.drawLine(barRect.right(), barRect.bottom(), barRect.right(), barRect.bottom()+3);
676
p.drawLine(barRect.left(), barRect.bottom(), barRect.left(), barRect.bottom()+3);
681
subDateColor = palette().active().highlightedText();
683
subDateColor = palette().active().foreground();
686
subDateColor = palette().active().mid();
688
if (sel == Selected || sel == FuzzySelection)
690
selBrush.setColor(ThemeEngine::instance()->thumbSelColor());
691
selBrush.setStyle(QBrush::SolidPattern);
692
if (sel == FuzzySelection)
693
selBrush.setStyle(QBrush::Dense4Pattern);
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);
708
QFont fnt = p.font();
709
fnt.setPointSize(fnt.pointSize()-4);
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);
719
if (d->calendar->dayOfWeek(ref.date()) == 1)
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);
732
int week = d->calendar->weekNumber(ref.date());
735
QFont fnt = p.font();
736
fnt.setPointSize(fnt.pointSize()-4);
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);
747
if (week == 1 || week == 10 || week == 20 || week == 30 || week == 40 || week == 50)
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);
754
p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
756
else if (week == 6 || week == 16 || week == 26 || week == 36 || week == 46)
758
p.drawLine(barRect.left(), barRect.bottom(),
759
barRect.left(), barRect.bottom()+d->bottomMargin/4);
767
QFont fnt = p.font();
768
fnt.setPointSize(fnt.pointSize()-4);
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);
779
if (ref.date().month() == 1)
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);
787
else if (ref.date().month() == 7)
789
p.drawLine(barRect.left(), barRect.bottom(),
790
barRect.left(), barRect.bottom()+d->bottomMargin/4);
797
if (ref.date().year() % 10 == 0)
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);
805
else if (ref.date().year() % 5 == 0)
806
p.drawLine(barRect.left(), barRect.bottom(),
807
barRect.left(), barRect.bottom()+d->bottomMargin/4);
812
ref = nextDateTime(ref);
815
// Draw all dates on the left of ref. date-time.
817
ref = d->refDateTime;
818
ref.setTime(QTime(0, 0, 0, 0));
819
ref = prevDateTime(ref);
821
for (int i = 0 ; i < d->nbItems-1 ; i++)
823
val = statForDateTime(ref, sel);
824
max = (double)maxCount();
826
if (d->scaleMode == TimeLineWidget::LogScale)
828
if (max > 0.0) max = log(max);
831
if (val <= 0) logVal = 0;
832
else logVal = log(val);
834
top = lround(dim + d->topMargin - ((logVal * dim) / max));
836
if (top < 0) val = 0;
840
top = lround(dim + d->topMargin - ((val * dim) / max));
844
barRect.setRight(d->startPos - i*d->barWidth);
845
barRect.setBottom(height() - d->bottomMargin);
846
barRect.setLeft(d->startPos - (i+1)*d->barWidth);
848
if (ref == d->cursorDateTime)
851
if (ref < d->minDateTime)
852
dateColor = palette().active().mid();
854
dateColor = palette().active().foreground();
856
p.setPen(palette().active().foreground());
857
p.fillRect(barRect, QBrush(ThemeEngine::instance()->textSpecialRegColor()));
859
p.drawLine(barRect.right(), barRect.bottom(), barRect.right(), barRect.bottom()+3);
860
p.drawLine(barRect.left(), barRect.bottom(), barRect.left(), barRect.bottom()+3);
865
subDateColor = palette().active().highlightedText();
867
subDateColor = palette().active().foreground();
870
subDateColor = palette().active().mid();
872
if (sel == Selected || sel == FuzzySelection)
874
selBrush.setColor(ThemeEngine::instance()->thumbSelColor());
875
selBrush.setStyle(QBrush::SolidPattern);
876
if (sel == FuzzySelection)
877
selBrush.setStyle(QBrush::Dense4Pattern);
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);
892
QFont fnt = p.font();
893
fnt.setPointSize(fnt.pointSize()-4);
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);
903
if (d->calendar->dayOfWeek(ref.date()) == 1)
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);
916
int week = d->calendar->weekNumber(ref.date());
919
QFont fnt = p.font();
920
fnt.setPointSize(fnt.pointSize()-4);
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);
931
if (week == 1 || week == 10 || week == 20 || week == 30 || week == 40 || week == 50)
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);
938
p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt);
940
else if (week == 6 || week == 16 || week == 26 || week == 36 || week == 46)
942
p.drawLine(barRect.left(), barRect.bottom(),
943
barRect.left(), barRect.bottom()+d->bottomMargin/4);
951
QFont fnt = p.font();
952
fnt.setPointSize(fnt.pointSize()-4);
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);
963
if (ref.date().month() == 1)
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);
971
else if (ref.date().month() == 7)
973
p.drawLine(barRect.right(), barRect.bottom(),
974
barRect.right(), barRect.bottom()+d->bottomMargin/4);
981
if (ref.date().year() % 10 == 0)
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);
989
else if (ref.date().year() % 5 == 0)
990
p.drawLine(barRect.right(), barRect.bottom(),
991
barRect.right(), barRect.bottom()+d->bottomMargin/4);
996
ref = prevDateTime(ref);
999
// Draw cursor rectangle over current date-time.
1000
if (focusRect.isValid())
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);
1007
p.setPen(palette().active().shadow());
1008
p.drawLine(p1.x(), p1.y()+1, p2.x(), p2.y()+1);
1009
p.drawRect(focusRect);
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());
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);
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);
1028
QDateTime TimeLineWidget::prevDateTime(const QDateTime& dt)
1035
prev = dt.addDays(-1);
1040
prev = dt.addDays(-7);
1045
prev = dt.addMonths(-1);
1050
prev = dt.addYears(-1);
1057
QDateTime TimeLineWidget::nextDateTime(const QDateTime& dt)
1064
next = dt.addDays(1);
1069
next = dt.addDays(7);
1074
next = dt.addMonths(1);
1079
next = dt.addYears(1);
1086
int TimeLineWidget::maxCount()
1093
max = d->maxCountByDay;
1098
max = d->maxCountByWeek;
1103
max = d->maxCountByMonth;
1108
max = d->maxCountByYear;
1115
int TimeLineWidget::statForDateTime(const QDateTime& dt, SelectionMode& selected)
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;
1129
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it =
1130
d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day));
1131
if ( it != d->dayStatMap.end() )
1133
count = it.data().first;
1134
selected = it.data().second;
1140
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it =
1141
d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week));
1142
if ( it != d->weekStatMap.end() )
1144
count = it.data().first;
1145
selected = it.data().second;
1151
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it =
1152
d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month));
1153
if ( it != d->monthStatMap.end() )
1155
count = it.data().first;
1156
selected = it.data().second;
1162
QMap<int, TimeLineWidgetPriv::StatPair>::iterator it = d->yearStatMap.find(year);
1163
if ( it != d->yearStatMap.end() )
1165
count = it.data().first;
1166
selected = it.data().second;
1175
void TimeLineWidget::setDateTimeSelected(const QDateTime& dt, SelectionMode selected)
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);
1189
dte = dts.addDays(1);
1190
setDaysRangeSelection(dts, dte, selected);
1195
dts = firstDayOfWeek(yearForWeek, week);
1196
dte = dts.addDays(7);
1197
setDaysRangeSelection(dts, dte, selected);
1198
updateWeekSelection(dts, dte);
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);
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);
1220
void TimeLineWidget::updateWeekSelection(const QDateTime dts, const QDateTime dte)
1222
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
1223
QDateTime dtsWeek, dteWeek, dt;
1225
int yearForWeek; // Used with week shared between 2 years decade (Dec/Jan).
1229
yearForWeek = dt.date().year();
1230
week = d->calendar->weekNumber(dt.date(), &yearForWeek);
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);
1243
void TimeLineWidget::updateMonthSelection(const QDateTime dts, const QDateTime dte)
1245
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
1246
QDateTime dtsMonth, dteMonth, dt;
1251
year = dt.date().year();
1252
month = dt.date().month();
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);
1265
void TimeLineWidget::updateYearSelection(const QDateTime dts, const QDateTime dte)
1267
QMap<int, TimeLineWidgetPriv::StatPair>::iterator it;
1268
QDateTime dtsYear, dteYear, dt;
1273
year = dt.date().year();
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);
1286
void TimeLineWidget::updateAllSelection()
1288
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
1292
for (it = d->dayStatMap.begin() ; it != d->dayStatMap.end(); ++it)
1294
if (it.data().second == Selected)
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);
1307
void TimeLineWidget::setDaysRangeSelection(const QDateTime dts, const QDateTime dte, SelectionMode selected)
1311
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
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;
1326
TimeLineWidget::SelectionMode TimeLineWidget::checkSelectionForDaysRange(const QDateTime dts, const QDateTime dte)
1333
QMap<TimeLineWidgetPriv::YearRefPair, TimeLineWidgetPriv::StatPair>::iterator it;
1337
year = dt.date().year();
1338
day = d->calendar->dayOfYear(dt.date());
1340
it = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day));
1341
if ( it != d->dayStatMap.end() )
1345
if (it.data().second != Unselected)
1347
if (it.data().second == FuzzySelection)
1360
if (itemsFuz == 0 && itemsSel == 0)
1364
return FuzzySelection;
1366
if (items > itemsSel)
1367
return FuzzySelection;
1372
void TimeLineWidget::paintEvent(QPaintEvent*)
1374
bitBlt(this, 0, 0, &d->pixmap);
1377
void TimeLineWidget::resizeEvent(QResizeEvent*)
1382
void TimeLineWidget::slotBackward()
1384
QDateTime ref = d->refDateTime;
1390
for (int i = 0; i < 7; i++)
1391
ref = prevDateTime(ref);
1396
for (int i = 0; i < 4; i++)
1397
ref = prevDateTime(ref);
1402
for (int i = 0; i < 12; i++)
1403
ref = prevDateTime(ref);
1408
for (int i = 0; i < 5; i++)
1409
ref = prevDateTime(ref);
1414
if (ref < d->minDateTime)
1415
ref = d->minDateTime;
1417
setRefDateTime(ref);
1420
void TimeLineWidget::slotPrevious()
1422
if (d->refDateTime <= d->minDateTime)
1424
QDateTime ref = prevDateTime(d->refDateTime);
1425
setRefDateTime(ref);
1428
void TimeLineWidget::slotNext()
1430
if (d->refDateTime >= d->maxDateTime)
1432
QDateTime ref = nextDateTime(d->refDateTime);
1433
setRefDateTime(ref);
1436
void TimeLineWidget::slotForward()
1438
QDateTime ref = d->refDateTime;
1444
for (int i = 0; i < 7; i++)
1445
ref = nextDateTime(ref);
1450
for (int i = 0; i < 4; i++)
1451
ref = nextDateTime(ref);
1456
for (int i = 0; i < 12; i++)
1457
ref = nextDateTime(ref);
1462
for (int i = 0; i < 5; i++)
1463
ref = nextDateTime(ref);
1468
if (ref > d->maxDateTime)
1469
ref = d->maxDateTime;
1471
setRefDateTime(ref);
1474
void TimeLineWidget::wheelEvent(QWheelEvent* e)
1478
if (e->state() & Qt::ShiftButton)
1486
if (e->state() & Qt::ShiftButton)
1493
void TimeLineWidget::mousePressEvent(QMouseEvent *e)
1495
if (e->button() == Qt::LeftButton)
1497
QPoint pt(e->x(), e->y());
1499
bool ctrlPressed = e->state() & Qt::ControlButton;
1500
QDateTime ref = dateTimeForPoint(pt, d->selMouseEvent);
1501
d->selStartDateTime = QDateTime();
1502
if (d->selMouseEvent)
1507
d->selStartDateTime = ref;
1508
d->selMinDateTime = ref;
1509
d->selMaxDateTime = ref;
1510
setDateTimeSelected(ref, Selected);
1514
setCursorDateTime(ref);
1516
d->validMouseEvent = true;
1522
void TimeLineWidget::mouseMoveEvent(QMouseEvent *e)
1524
if (d->validMouseEvent == true)
1526
QPoint pt(e->x(), e->y());
1529
QDateTime selEndDateTime = dateTimeForPoint(pt, sel);
1530
setCursorDateTime(selEndDateTime);
1532
// Clamp start and end date-time of current contiguous selection.
1534
if (!selEndDateTime.isNull() && !d->selStartDateTime.isNull())
1536
if (selEndDateTime > d->selStartDateTime &&
1537
selEndDateTime > d->selMaxDateTime)
1539
d->selMaxDateTime = selEndDateTime;
1541
else if (selEndDateTime < d->selStartDateTime &&
1542
selEndDateTime < d->selMinDateTime)
1544
d->selMinDateTime = selEndDateTime;
1547
QDateTime dt = d->selMinDateTime;
1550
setDateTimeSelected(dt, Unselected);
1551
dt = nextDateTime(dt);
1553
while(dt <= d->selMaxDateTime);
1556
// Now perform selections on Date Maps.
1558
if (d->selMouseEvent)
1560
if (!d->selStartDateTime.isNull() && !selEndDateTime.isNull())
1562
QDateTime dt = d->selStartDateTime;
1563
if (selEndDateTime > d->selStartDateTime)
1567
setDateTimeSelected(dt, Selected);
1568
dt = nextDateTime(dt);
1570
while(dt <= selEndDateTime);
1576
setDateTimeSelected(dt, Selected);
1577
dt = prevDateTime(dt);
1579
while(dt >= selEndDateTime);
1589
void TimeLineWidget::mouseReleaseEvent(QMouseEvent*)
1591
d->validMouseEvent = false;
1593
// Only dispatch changes about selection when user release mouse selection
1594
// to prevent multiple queries on database.
1595
if (d->selMouseEvent)
1597
updateAllSelection();
1598
emit signalSelectionChanged();
1601
d->selMouseEvent = false;
1604
QDateTime TimeLineWidget::dateTimeForPoint(const QPoint& pt, bool &isOnSelectionArea)
1606
QRect barRect, selRect;
1607
isOnSelectionArea = false;
1609
// Check on the right of reference date.
1611
QDateTime ref = d->refDateTime;
1612
ref.setTime(QTime(0, 0, 0, 0));
1614
QRect deskRect = KGlobalSettings::desktopGeometry(this);
1615
int items = deskRect.width() / d->barWidth;
1617
for (int i = 0 ; i < items ; i++)
1620
barRect.setLeft(d->startPos + i*d->barWidth);
1621
barRect.setBottom(height() - d->bottomMargin + 1);
1622
barRect.setRight(d->startPos + (i+1)*d->barWidth);
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);
1629
if (selRect.contains(pt))
1630
isOnSelectionArea = true;
1632
if (barRect.contains(pt) || selRect.contains(pt))
1634
if (i >= d->nbItems)
1636
// Point is outside visible widget area. We scrolling widget contents.
1643
ref = nextDateTime(ref);
1646
// Check on the left of reference date.
1648
ref = d->refDateTime;
1649
ref.setTime(QTime(0, 0, 0, 0));
1650
ref = prevDateTime(ref);
1652
for (int i = 0 ; i < items ; i++)
1655
barRect.setRight(d->startPos - i*d->barWidth);
1656
barRect.setBottom(height() - d->bottomMargin + 1);
1657
barRect.setLeft(d->startPos - (i+1)*d->barWidth);
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);
1664
if (selRect.contains(pt))
1665
isOnSelectionArea = true;
1667
if (barRect.contains(pt) || selRect.contains(pt))
1669
if (i >= d->nbItems-1)
1671
// Point is outside visible widget area. We scrolling widget contents.
1678
ref = prevDateTime(ref);
1684
QDateTime TimeLineWidget::firstDayOfWeek(int year, int weekNumber)
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));
1698
weekNum = d->calendar->weekNumber(dt.date(), &weekYear);
1700
while(weekNum != 1 && weekYear != year);
1702
dt = dt.addDays((weekNumber-1)*7);
1705
DDebug() << "Year= " << year << " Week= " << weekNumber
1706
<< " 1st day= " << dt << endl;
1712
void TimeLineWidget::slotThemeChanged()
1718
} // NameSpace Digikam