1
/* ============================================================
3
* This file is a part of digiKam project
4
* http://www.digikam.org
7
* Description : Time line sidebar tab contents.
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
* ============================================================ */
30
#include <qcombobox.h>
31
#include <qpushbutton.h>
32
#include <qhbuttongroup.h>
34
#include <qscrollbar.h>
35
#include <qwhatsthis.h>
43
#include <klineedit.h>
44
#include <kiconloader.h>
45
#include <kapplication.h>
46
#include <ksqueezedtextlabel.h>
47
#include <kstandarddirs.h>
48
#include <kdeversion.h>
49
#include <kmessagebox.h>
51
#if KDE_IS_VERSION(3,2,0)
52
#include <kinputdialog.h>
54
#include <klineeditdlg.h>
60
#include "albummanager.h"
62
#include "searchtextbar.h"
63
#include "timelinefolderview.h"
64
#include "timelinewidget.h"
65
#include "timelineview.h"
66
#include "timelineview.moc"
71
class TimeLineViewPriv
87
timeLineFolderView = 0;
92
QScrollBar *scrollBar;
96
QComboBox *timeUnitCB;
98
QHButtonGroup *scaleBG;
100
QPushButton *resetButton;
101
QPushButton *saveButton;
103
QLabel *cursorCountLabel;
107
KSqueezedTextLabel *cursorDateLabel;
109
SearchTextBar *searchDateBar;
111
TimeLineWidget *timeLineWidget;
113
TimeLineFolderView *timeLineFolderView;
116
TimeLineView::TimeLineView(QWidget *parent)
117
: QWidget(parent, 0, Qt::WDestructiveClose)
119
d = new TimeLineViewPriv;
120
d->timer = new QTimer(this);
122
QVBoxLayout *vlay = new QVBoxLayout(this);
123
QFrame *panel = new QFrame(this);
124
panel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
125
panel->setLineWidth(1);
127
QGridLayout *grid = new QGridLayout(panel, 4, 3);
129
// ---------------------------------------------------------------
131
QWidget *hbox1 = new QWidget(panel);
132
QHBoxLayout *hlay = new QHBoxLayout(hbox1);
134
QLabel *label1 = new QLabel(i18n("Time Unit:"), hbox1);
135
d->timeUnitCB = new QComboBox(false, hbox1);
136
d->timeUnitCB->insertItem(i18n("Day"), TimeLineWidget::Day);
137
d->timeUnitCB->insertItem(i18n("Week"), TimeLineWidget::Week);
138
d->timeUnitCB->insertItem(i18n("Month"), TimeLineWidget::Month);
139
d->timeUnitCB->insertItem(i18n("Year"), TimeLineWidget::Year);
140
d->timeUnitCB->setCurrentItem((int)TimeLineWidget::Month);
141
d->timeUnitCB->setFocusPolicy(QWidget::NoFocus);
142
QWhatsThis::add(d->timeUnitCB, i18n("<p>Select the histogram time unit here.<p>"
143
"You can change the graph decade to zoom in or zoom out over time."));
145
d->scaleBG = new QHButtonGroup(hbox1);
146
d->scaleBG->setExclusive(true);
147
d->scaleBG->setFrameShape(QFrame::NoFrame);
148
d->scaleBG->setInsideMargin( 0 );
149
QWhatsThis::add(d->scaleBG, i18n("<p>Select the histogram scale here.<p>"
150
"If the date count's maximal values are small, you can use the linear scale.<p>"
151
"Logarithmic scale can be used when the maximal values are big; "
152
"if it is used, all values (small and large) will be visible on the "
155
QPushButton *linHistoButton = new QPushButton( d->scaleBG );
156
QToolTip::add( linHistoButton, i18n( "<p>Linear" ) );
157
d->scaleBG->insert(linHistoButton, TimeLineWidget::LinScale);
158
KGlobal::dirs()->addResourceType("histogram-lin", KGlobal::dirs()->kde_default("data") + "digikam/data");
159
QString directory = KGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png");
160
linHistoButton->setPixmap( QPixmap( directory + "histogram-lin.png" ) );
161
linHistoButton->setToggleButton(true);
163
QPushButton *logHistoButton = new QPushButton( d->scaleBG );
164
QToolTip::add( logHistoButton, i18n( "<p>Logarithmic" ) );
165
d->scaleBG->insert(logHistoButton, TimeLineWidget::LogScale);
166
KGlobal::dirs()->addResourceType("histogram-log", KGlobal::dirs()->kde_default("data") + "digikam/data");
167
directory = KGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png");
168
logHistoButton->setPixmap( QPixmap( directory + "histogram-log.png" ) );
169
logHistoButton->setToggleButton(true);
172
hlay->setSpacing(KDialog::spacingHint());
173
hlay->addWidget(label1);
174
hlay->addWidget(d->timeUnitCB);
175
hlay->addItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum));
176
hlay->addWidget(d->scaleBG);
178
// ---------------------------------------------------------------
180
d->timeLineWidget = new TimeLineWidget(panel);
181
d->scrollBar = new QScrollBar(panel);
182
d->scrollBar->setOrientation(Qt::Horizontal);
183
d->scrollBar->setMinValue(0);
184
d->scrollBar->setLineStep(1);
186
d->cursorDateLabel = new KSqueezedTextLabel(0, panel);
187
d->cursorCountLabel = new QLabel(panel);
188
d->cursorCountLabel->setAlignment(Qt::AlignRight);
190
// ---------------------------------------------------------------
192
QHBox *hbox2 = new QHBox(panel);
194
hbox2->setSpacing(KDialog::spacingHint());
196
d->resetButton = new QPushButton(hbox2);
197
d->resetButton->setPixmap(SmallIcon("reload_page"));
198
QToolTip::add(d->resetButton, i18n("Clear current selection"));
199
QWhatsThis::add(d->resetButton, i18n("<p>If you press this button, current "
200
"dates selection from time-line will be "
202
d->nameEdit = new KLineEdit(hbox2);
203
QWhatsThis::add(d->nameEdit, i18n("<p>Enter the name of the current dates search to save in the "
204
"\"My Date Searches\" view"));
206
d->saveButton = new QPushButton(hbox2);
207
d->saveButton->setPixmap(SmallIcon("filesave"));
208
d->saveButton->setEnabled(false);
209
QToolTip::add(d->saveButton, i18n("Save current selection to a new virtual Album"));
210
QWhatsThis::add(d->saveButton, i18n("<p>If you press this button, current "
211
"dates selection from time-line will be "
212
"saved to a new search virtual Album using name "
213
"set on the left side."));
215
// ---------------------------------------------------------------
217
grid->addMultiCellWidget(hbox1, 0, 0, 0, 3);
218
grid->addMultiCellWidget(d->cursorDateLabel, 1, 1, 0, 2);
219
grid->addMultiCellWidget(d->cursorCountLabel, 1, 1, 3, 3);
220
grid->addMultiCellWidget(d->timeLineWidget, 2, 2, 0, 3);
221
grid->addMultiCellWidget(d->scrollBar, 3, 3, 0, 3);
222
grid->addMultiCellWidget(hbox2, 4, 4, 0, 3);
223
grid->setColStretch(2, 10);
224
grid->setMargin(KDialog::spacingHint());
225
grid->setSpacing(KDialog::spacingHint());
227
// ---------------------------------------------------------------
229
d->timeLineFolderView = new TimeLineFolderView(this);
230
d->searchDateBar = new SearchTextBar(this, "TimeLineViewSearchDateBar");
232
vlay->addWidget(panel);
233
vlay->addWidget(d->timeLineFolderView);
234
vlay->addItem(new QSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(),
235
QSizePolicy::Minimum, QSizePolicy::Minimum));
236
vlay->addWidget(d->searchDateBar);
240
// ---------------------------------------------------------------
242
connect(AlbumManager::instance(), SIGNAL(signalDatesMapDirty(const QMap<QDateTime, int>&)),
243
d->timeLineWidget, SLOT(slotDatesMap(const QMap<QDateTime, int>&)));
245
connect(d->timeLineFolderView, SIGNAL(signalAlbumSelected(SAlbum*)),
246
this, SLOT(slotAlbumSelected(SAlbum*)));
248
connect(d->timeLineFolderView, SIGNAL(signalRenameAlbum(SAlbum*)),
249
this, SLOT(slotRenameAlbum(SAlbum*)));
251
connect(d->timeLineFolderView, SIGNAL(signalTextSearchFilterMatch(bool)),
252
d->searchDateBar, SLOT(slotSearchResult(bool)));
254
connect(d->searchDateBar, SIGNAL(signalTextChanged(const QString&)),
255
d->timeLineFolderView, SLOT(slotTextSearchFilterChanged(const QString&)));
257
connect(d->timeUnitCB, SIGNAL(activated(int)),
258
this, SLOT(slotTimeUnitChanged(int)));
260
connect(d->scaleBG, SIGNAL(released(int)),
261
this, SLOT(slotScaleChanged(int)));
263
connect(d->timeLineWidget, SIGNAL(signalDateMapChanged()),
264
this, SLOT(slotInit()));
266
connect(d->timeLineWidget, SIGNAL(signalCursorPositionChanged()),
267
this, SLOT(slotCursorPositionChanged()));
269
connect(d->timeLineWidget, SIGNAL(signalSelectionChanged()),
270
this, SLOT(slotSelectionChanged()));
272
connect(d->timeLineWidget, SIGNAL(signalRefDateTimeChanged()),
273
this, SLOT(slotRefDateTimeChanged()));
275
connect(d->timer, SIGNAL(timeout()),
276
this, SLOT(slotUpdateCurrentDateSearchAlbum()));
278
connect(d->resetButton, SIGNAL(clicked()),
279
this, SLOT(slotResetSelection()));
281
connect(d->saveButton, SIGNAL(clicked()),
282
this, SLOT(slotSaveSelection()));
284
connect(d->scrollBar, SIGNAL(valueChanged(int)),
285
this, SLOT(slotScrollBarValueChanged(int)));
287
connect(d->nameEdit, SIGNAL(textChanged(const QString&)),
288
this, SLOT(slotCheckAboutSelection()));
290
connect(d->nameEdit, SIGNAL(returnPressed(const QString&)),
291
d->saveButton, SLOT(animateClick()));
294
TimeLineView::~TimeLineView()
301
TimeLineFolderView* TimeLineView::folderView() const
303
return d->timeLineFolderView;
306
SearchTextBar* TimeLineView::searchBar() const
308
return d->searchDateBar;
311
void TimeLineView::slotInit()
313
// Date Maps are loaded from AlbumManager to TimeLineWidget after than GUI is initialized.
314
// AlbumManager query Date KIO slave to stats items from database and it can take a while.
315
// We waiting than TimeLineWidget is ready before to set last config from users.
319
disconnect(d->timeLineWidget, SIGNAL(signalDateMapChanged()),
320
this, SLOT(slotInit()));
322
connect(d->timeLineWidget, SIGNAL(signalDateMapChanged()),
323
this, SLOT(slotCursorPositionChanged()));
326
void TimeLineView::readConfig()
328
KConfig* config = kapp->config();
329
config->setGroup("TimeLine SideBar");
331
d->timeUnitCB->setCurrentItem(config->readNumEntry("Histogram TimeUnit", TimeLineWidget::Month));
332
slotTimeUnitChanged(d->timeUnitCB->currentItem());
334
d->scaleBG->setButton(config->readNumEntry("Histogram Scale", TimeLineWidget::LinScale));
335
slotScaleChanged(d->scaleBG->selectedId());
337
QDateTime now = QDateTime::currentDateTime();
338
d->timeLineWidget->setCursorDateTime(config->readDateTimeEntry("Cursor Position", &now));
339
d->timeLineWidget->setCurrentIndex(d->timeLineWidget->indexForCursorDateTime());
342
void TimeLineView::writeConfig()
344
KConfig* config = kapp->config();
345
config->setGroup("TimeLine SideBar");
346
config->writeEntry("Histogram TimeUnit", d->timeUnitCB->currentItem());
347
config->writeEntry("Histogram Scale", d->scaleBG->selectedId());
348
config->writeEntry("Cursor Position", d->timeLineWidget->cursorDateTime());
352
void TimeLineView::setActive(bool val)
354
if (d->timeLineFolderView->selectedItem())
356
d->timeLineFolderView->setActive(val);
361
DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount);
364
AlbumManager::instance()->setCurrentAlbum(0);
368
AlbumList sList = AlbumManager::instance()->allSAlbums();
369
for (AlbumList::iterator it = sList.begin(); it != sList.end(); ++it)
371
SAlbum* salbum = (SAlbum*)(*it);
372
if (salbum->title() == d->timeLineFolderView->currentTimeLineSearchName())
373
AlbumManager::instance()->setCurrentAlbum(salbum);
379
void TimeLineView::slotRefDateTimeChanged()
381
d->scrollBar->blockSignals(true);
382
d->scrollBar->setMaxValue(d->timeLineWidget->totalIndex()-1);
383
d->scrollBar->setValue(d->timeLineWidget->indexForRefDateTime()-1);
384
d->scrollBar->blockSignals(false);
387
void TimeLineView::slotTimeUnitChanged(int mode)
389
d->timeLineWidget->setTimeUnit((TimeLineWidget::TimeUnit)mode);
392
void TimeLineView::slotScrollBarValueChanged(int val)
394
d->timeLineWidget->setCurrentIndex(val);
397
void TimeLineView::slotScaleChanged(int mode)
399
d->timeLineWidget->setScaleMode((TimeLineWidget::ScaleMode)mode);
402
void TimeLineView::slotCursorPositionChanged()
405
int val = d->timeLineWidget->cursorInfo(txt);
406
d->cursorDateLabel->setText(txt);
407
d->cursorCountLabel->setText(QString::number(val));
410
void TimeLineView::slotSelectionChanged()
412
d->timer->start(100, true);
415
/** Called from d->timer event.*/
416
void TimeLineView::slotUpdateCurrentDateSearchAlbum()
418
slotCheckAboutSelection();
419
createNewDateSearchAlbum(d->timeLineFolderView->currentTimeLineSearchName());
422
void TimeLineView::slotSaveSelection()
424
QString name = d->nameEdit->text();
425
if (!checkName(name))
427
createNewDateSearchAlbum(name);
430
void TimeLineView::createNewDateSearchAlbum(const QString& name)
433
QDateTime start, end;
434
DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount);
438
AlbumManager::instance()->setCurrentAlbum(0);
442
d->timeLineFolderView->blockSignals(true);
443
d->timeLineFolderView->clearSelection();
444
d->timeLineFolderView->blockSignals(false);
446
// We will make now the Url for digiKam Search KIO-Slave
449
url.setProtocol("digikamsearch");
451
int grp = list.count();
452
QString path("1 AND 2");
456
for (int i = 1 ; i < grp; i++)
459
path.append(QString("%1 AND %2").arg(i*2+1).arg(i*2+2));
465
DateRangeList::iterator it;
466
for (it = list.begin() ; it != list.end(); ++it)
470
url.addQueryItem(QString("%1.key").arg(i*2+1), QString("imagedate"));
471
url.addQueryItem(QString("%1.op").arg(i*2+1), QString("GT"));
472
url.addQueryItem(QString("%1.val").arg(i*2+1), start.date().toString(Qt::ISODate));
473
url.addQueryItem(QString("%1.key").arg(i*2+2), QString("imagedate"));
474
url.addQueryItem(QString("%1.op").arg(i*2+2), QString("LT"));
475
url.addQueryItem(QString("%1.val").arg(i*2+2), end.date().toString(Qt::ISODate));
479
url.addQueryItem("name", name);
480
url.addQueryItem("count", QString::number(grp*2));
481
url.addQueryItem("type", QString("datesearch"));
483
//DDebug() << url << endl;
485
SAlbum* album = AlbumManager::instance()->createSAlbum(url, false);
486
AlbumManager::instance()->setCurrentAlbum(album);
489
void TimeLineView::slotAlbumSelected(SAlbum* salbum)
493
slotResetSelection();
497
// Date Search url for KIO-Slave is something like that :
498
// digikamsearch:1 AND 2 OR 3 AND 4 OR 5 AND 6?
499
// 1.key=imagedate&1.op=GT&1.val=2006-02-06&
500
// 2.key=imagedate&2.op=LT&2.val=2006-02-07&
501
// 3.key=imagedate&3.op=GT&3.val=2006-02-10&
502
// 4.key=imagedate&4.op=LT&4.val=2006-02-11&
503
// 5.key=imagedate&5.op=GT&5.val=2006-02-12&
504
// 6.key=imagedate&6.op=LT&6.val=2006-02-13&
505
// name=TimeLineSelection&
509
// Check if a special url query exist to identify a SAlbum dedicaced to Date Search
510
KURL url = salbum->kurl();
511
QMap<QString, QString> queries = url.queryItems();
512
if (queries.isEmpty()) return;
514
QString type = url.queryItem("type");
515
if (type != QString("datesearch")) return;
518
int count = url.queryItem("count").toInt(&ok);
519
if (!ok || count <= 0) return;
521
//DDebug() << url << endl;
523
QMap<QString, QString>::iterator it2;
525
QDateTime start, end;
527
for (int i = 1 ; i <= count ; i+=2)
529
key = QString("%1.val").arg(QString::number(i));
530
it2 = queries.find(key);
531
if (it2 != queries.end())
532
start = QDateTime(QDate::fromString(it2.data(), Qt::ISODate));
534
//DDebug() << key << " :: " << it2.data() << endl;
536
key = QString("%1.val").arg(QString::number(i+1));
537
it2 = queries.find(key);
538
if (it2 != queries.end())
539
end = QDateTime(QDate::fromString(it2.data(), Qt::ISODate));
541
//DDebug() << key << " :: " << it2.data() << endl;
543
list.append(DateRange(start, end));
547
DateRangeList::iterator it3;
548
for (it3 = list.begin() ; it3 != list.end(); ++it3)
549
DDebug() << (*it3).first.date().toString(Qt::ISODate) << " :: "
550
<< (*it3).second.date().toString(Qt::ISODate) << endl;
553
d->timeLineWidget->setSelectedDateRange(list);
554
AlbumManager::instance()->setCurrentAlbum(salbum);
557
void TimeLineView::slotResetSelection()
559
d->timeLineWidget->slotResetSelection();
560
slotCheckAboutSelection();
561
AlbumManager::instance()->setCurrentAlbum(0);
564
bool TimeLineView::checkName(QString& name)
566
bool checked = checkAlbum(name);
570
QString label = i18n( "Search name already exists.\n"
571
"Please enter a new name:" );
573
#if KDE_IS_VERSION(3,2,0)
574
QString newTitle = KInputDialog::getText(i18n("Name exists"), label, name, &ok, this);
576
QString newTitle = KLineEditDlg::getText(i18n("Name exists"), label, name, ok, this);
578
if (!ok) return false;
581
checked = checkAlbum(name);
587
bool TimeLineView::checkAlbum(const QString& name) const
589
AlbumList list = AlbumManager::instance()->allSAlbums();
591
for (AlbumList::Iterator it = list.begin() ; it != list.end() ; ++it)
593
SAlbum *album = (SAlbum*)(*it);
594
if ( album->title() == name )
600
void TimeLineView::slotCheckAboutSelection()
603
DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount);
606
d->nameEdit->setEnabled(true);
608
if (!d->nameEdit->text().isEmpty())
609
d->saveButton->setEnabled(true);
613
d->nameEdit->setEnabled(false);
614
d->saveButton->setEnabled(false);
618
void TimeLineView::slotRenameAlbum(SAlbum* salbum)
622
QString oldName(salbum->title());
625
#if KDE_IS_VERSION(3,2,0)
626
QString name = KInputDialog::getText(i18n("Rename Album (%1)").arg(oldName),
627
i18n("Enter new album name:"),
630
QString name = KLineEditDlg::getText(i18n("Rename Album (%1)").arg(oldName),
631
i18n("Enter new album name:"),
635
if (!ok || name == oldName || name.isEmpty()) return;
637
if (!checkName(name)) return;
639
KURL url = salbum->kurl();
640
url.removeQueryItem("name");
641
url.addQueryItem("name", name);
642
AlbumManager::instance()->updateSAlbum(salbum, url);
645
} // NameSpace Digikam