/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2007-12-08 * Description : Time line sidebar tab contents. * * Copyright (C) 2007-2008 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ // TQt includes. #include #include #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include #include #include #include #include #include #include #include #if KDE_IS_VERSION(3,2,0) #include #else #include #endif // Local includes. #include "album.h" #include "albummanager.h" #include "ddebug.h" #include "searchtextbar.h" #include "timelinefolderview.h" #include "timelinewidget.h" #include "timelineview.h" #include "timelineview.moc" namespace Digikam { class TimeLineViewPriv { public: TimeLineViewPriv() { timeUnitCB = 0; scaleBG = 0; cursorDateLabel = 0; cursorCountLabel = 0; timeLineWidget = 0; timer = 0; resetButton = 0; saveButton = 0; scrollBar = 0; timeLineFolderView = 0; nameEdit = 0; searchDateBar = 0; } TQScrollBar *scrollBar; TQTimer *timer; TQComboBox *timeUnitCB; TQHButtonGroup *scaleBG; TQPushButton *resetButton; TQPushButton *saveButton; TQLabel *cursorCountLabel; KLineEdit *nameEdit; KSqueezedTextLabel *cursorDateLabel; SearchTextBar *searchDateBar; TimeLineWidget *timeLineWidget; TimeLineFolderView *timeLineFolderView; }; TimeLineView::TimeLineView(TQWidget *parent) : TQWidget(parent, 0, TQt::WDestructiveClose) { d = new TimeLineViewPriv; d->timer = new TQTimer(this); TQVBoxLayout *vlay = new TQVBoxLayout(this); TQFrame *panel = new TQFrame(this); panel->setFrameStyle(TQFrame::StyledPanel | TQFrame::Sunken); panel->setLineWidth(1); TQGridLayout *grid = new TQGridLayout(panel, 4, 3); // --------------------------------------------------------------- TQWidget *hbox1 = new TQWidget(panel); TQHBoxLayout *hlay = new TQHBoxLayout(hbox1); TQLabel *label1 = new TQLabel(i18n("Time Unit:"), hbox1); d->timeUnitCB = new TQComboBox(false, hbox1); d->timeUnitCB->insertItem(i18n("Day"), TimeLineWidget::Day); d->timeUnitCB->insertItem(i18n("Week"), TimeLineWidget::Week); d->timeUnitCB->insertItem(i18n("Month"), TimeLineWidget::Month); d->timeUnitCB->insertItem(i18n("Year"), TimeLineWidget::Year); d->timeUnitCB->setCurrentItem((int)TimeLineWidget::Month); d->timeUnitCB->setFocusPolicy(TQ_NoFocus); TQWhatsThis::add(d->timeUnitCB, i18n("

Select the histogram time unit here.

" "You can change the graph decade to zoom in or zoom out over time.")); d->scaleBG = new TQHButtonGroup(hbox1); d->scaleBG->setExclusive(true); d->scaleBG->setFrameShape(TQFrame::NoFrame); d->scaleBG->setInsideMargin( 0 ); TQWhatsThis::add(d->scaleBG, i18n("

Select the histogram scale here.

" "If the date count's maximal values are small, you can use the linear scale.

" "Logarithmic scale can be used when the maximal values are big; " "if it is used, all values (small and large) will be visible on the " "graph.")); TQPushButton *linHistoButton = new TQPushButton( d->scaleBG ); TQToolTip::add( linHistoButton, i18n( "

Linear" ) ); d->scaleBG->insert(linHistoButton, TimeLineWidget::LinScale); TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); linHistoButton->setToggleButton(true); TQPushButton *logHistoButton = new TQPushButton( d->scaleBG ); TQToolTip::add( logHistoButton, i18n( "

Logarithmic" ) ); d->scaleBG->insert(logHistoButton, TimeLineWidget::LogScale); TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); logHistoButton->setToggleButton(true); hlay->setMargin(0); hlay->setSpacing(KDialog::spacingHint()); hlay->addWidget(label1); hlay->addWidget(d->timeUnitCB); hlay->addItem(new TQSpacerItem(10, 10, TQSizePolicy::Expanding, TQSizePolicy::Minimum)); hlay->addWidget(d->scaleBG); // --------------------------------------------------------------- d->timeLineWidget = new TimeLineWidget(panel); d->scrollBar = new TQScrollBar(panel); d->scrollBar->setOrientation(Qt::Horizontal); d->scrollBar->setMinValue(0); d->scrollBar->setLineStep(1); d->cursorDateLabel = new KSqueezedTextLabel(0, panel); d->cursorCountLabel = new TQLabel(panel); d->cursorCountLabel->setAlignment(TQt::AlignRight); // --------------------------------------------------------------- TQHBox *hbox2 = new TQHBox(panel); hbox2->setMargin(0); hbox2->setSpacing(KDialog::spacingHint()); d->resetButton = new TQPushButton(hbox2); d->resetButton->setPixmap(SmallIcon("reload_page")); TQToolTip::add(d->resetButton, i18n("Clear current selection")); TQWhatsThis::add(d->resetButton, i18n("

If you press this button, current " "dates selection from time-line will be " "clear.")); d->nameEdit = new KLineEdit(hbox2); TQWhatsThis::add(d->nameEdit, i18n("

Enter the name of the current dates search to save in the " "\"My Date Searches\" view")); d->saveButton = new TQPushButton(hbox2); d->saveButton->setPixmap(SmallIcon("filesave")); d->saveButton->setEnabled(false); TQToolTip::add(d->saveButton, i18n("Save current selection to a new virtual Album")); TQWhatsThis::add(d->saveButton, i18n("

If you press this button, current " "dates selection from time-line will be " "saved to a new search virtual Album using name " "set on the left side.")); // --------------------------------------------------------------- grid->addMultiCellWidget(hbox1, 0, 0, 0, 3); grid->addMultiCellWidget(d->cursorDateLabel, 1, 1, 0, 2); grid->addMultiCellWidget(d->cursorCountLabel, 1, 1, 3, 3); grid->addMultiCellWidget(d->timeLineWidget, 2, 2, 0, 3); grid->addMultiCellWidget(d->scrollBar, 3, 3, 0, 3); grid->addMultiCellWidget(hbox2, 4, 4, 0, 3); grid->setColStretch(2, 10); grid->setMargin(KDialog::spacingHint()); grid->setSpacing(KDialog::spacingHint()); // --------------------------------------------------------------- d->timeLineFolderView = new TimeLineFolderView(this); d->searchDateBar = new SearchTextBar(this, "TimeLineViewSearchDateBar"); vlay->addWidget(panel); vlay->addWidget(d->timeLineFolderView); vlay->addItem(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), TQSizePolicy::Minimum, TQSizePolicy::Minimum)); vlay->addWidget(d->searchDateBar); vlay->setMargin(0); vlay->setSpacing(0); // --------------------------------------------------------------- connect(AlbumManager::instance(), TQT_SIGNAL(signalDatesMapDirty(const TQMap&)), d->timeLineWidget, TQT_SLOT(slotDatesMap(const TQMap&))); connect(d->timeLineFolderView, TQT_SIGNAL(signalAlbumSelected(SAlbum*)), this, TQT_SLOT(slotAlbumSelected(SAlbum*))); connect(d->timeLineFolderView, TQT_SIGNAL(signalRenameAlbum(SAlbum*)), this, TQT_SLOT(slotRenameAlbum(SAlbum*))); connect(d->timeLineFolderView, TQT_SIGNAL(signalTextSearchFilterMatch(bool)), d->searchDateBar, TQT_SLOT(slotSearchResult(bool))); connect(d->searchDateBar, TQT_SIGNAL(signalTextChanged(const TQString&)), d->timeLineFolderView, TQT_SLOT(slotTextSearchFilterChanged(const TQString&))); connect(d->timeUnitCB, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotTimeUnitChanged(int))); connect(d->scaleBG, TQT_SIGNAL(released(int)), this, TQT_SLOT(slotScaleChanged(int))); connect(d->timeLineWidget, TQT_SIGNAL(signalDateMapChanged()), this, TQT_SLOT(slotInit())); connect(d->timeLineWidget, TQT_SIGNAL(signalCursorPositionChanged()), this, TQT_SLOT(slotCursorPositionChanged())); connect(d->timeLineWidget, TQT_SIGNAL(signalSelectionChanged()), this, TQT_SLOT(slotSelectionChanged())); connect(d->timeLineWidget, TQT_SIGNAL(signalRefDateTimeChanged()), this, TQT_SLOT(slotRefDateTimeChanged())); connect(d->timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotUpdateCurrentDateSearchAlbum())); connect(d->resetButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotResetSelection())); connect(d->saveButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotSaveSelection())); connect(d->scrollBar, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(slotScrollBarValueChanged(int))); connect(d->nameEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotCheckAboutSelection())); connect(d->nameEdit, TQT_SIGNAL(returnPressed(const TQString&)), d->saveButton, TQT_SLOT(animateClick())); } TimeLineView::~TimeLineView() { writeConfig(); delete d->timer; delete d; } TimeLineFolderView* TimeLineView::folderView() const { return d->timeLineFolderView; } SearchTextBar* TimeLineView::searchBar() const { return d->searchDateBar; } void TimeLineView::slotInit() { // Date Maps are loaded from AlbumManager to TimeLineWidget after than GUI is initialized. // AlbumManager query Date KIO slave to stats items from database and it can take a while. // We waiting than TimeLineWidget is ready before to set last config from users. readConfig(); disconnect(d->timeLineWidget, TQT_SIGNAL(signalDateMapChanged()), this, TQT_SLOT(slotInit())); connect(d->timeLineWidget, TQT_SIGNAL(signalDateMapChanged()), this, TQT_SLOT(slotCursorPositionChanged())); } void TimeLineView::readConfig() { TDEConfig* config = kapp->config(); config->setGroup("TimeLine SideBar"); d->timeUnitCB->setCurrentItem(config->readNumEntry("Histogram TimeUnit", TimeLineWidget::Month)); slotTimeUnitChanged(d->timeUnitCB->currentItem()); d->scaleBG->setButton(config->readNumEntry("Histogram Scale", TimeLineWidget::LinScale)); slotScaleChanged(d->scaleBG->selectedId()); TQDateTime now = TQDateTime::currentDateTime(); d->timeLineWidget->setCursorDateTime(config->readDateTimeEntry("Cursor Position", &now)); d->timeLineWidget->setCurrentIndex(d->timeLineWidget->indexForCursorDateTime()); } void TimeLineView::writeConfig() { TDEConfig* config = kapp->config(); config->setGroup("TimeLine SideBar"); config->writeEntry("Histogram TimeUnit", d->timeUnitCB->currentItem()); config->writeEntry("Histogram Scale", d->scaleBG->selectedId()); config->writeEntry("Cursor Position", d->timeLineWidget->cursorDateTime()); config->sync(); } void TimeLineView::setActive(bool val) { if (d->timeLineFolderView->selectedItem()) { d->timeLineFolderView->setActive(val); } else if (val) { int totalCount = 0; DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount); if (list.isEmpty()) { AlbumManager::instance()->setCurrentAlbum(0); } else { AlbumList sList = AlbumManager::instance()->allSAlbums(); for (AlbumList::iterator it = sList.begin(); it != sList.end(); ++it) { SAlbum* salbum = (SAlbum*)(*it); if (salbum->title() == d->timeLineFolderView->currentTimeLineSearchName()) AlbumManager::instance()->setCurrentAlbum(salbum); } } } } void TimeLineView::slotRefDateTimeChanged() { d->scrollBar->blockSignals(true); d->scrollBar->setMaxValue(d->timeLineWidget->totalIndex()-1); d->scrollBar->setValue(d->timeLineWidget->indexForRefDateTime()-1); d->scrollBar->blockSignals(false); } void TimeLineView::slotTimeUnitChanged(int mode) { d->timeLineWidget->setTimeUnit((TimeLineWidget::TimeUnit)mode); } void TimeLineView::slotScrollBarValueChanged(int val) { d->timeLineWidget->setCurrentIndex(val); } void TimeLineView::slotScaleChanged(int mode) { d->timeLineWidget->setScaleMode((TimeLineWidget::ScaleMode)mode); } void TimeLineView::slotCursorPositionChanged() { TQString txt; int val = d->timeLineWidget->cursorInfo(txt); d->cursorDateLabel->setText(txt); d->cursorCountLabel->setText(TQString::number(val)); } void TimeLineView::slotSelectionChanged() { d->timer->start(100, true); } /** Called from d->timer event.*/ void TimeLineView::slotUpdateCurrentDateSearchAlbum() { slotCheckAboutSelection(); createNewDateSearchAlbum(d->timeLineFolderView->currentTimeLineSearchName()); } void TimeLineView::slotSaveSelection() { TQString name = d->nameEdit->text(); if (!checkName(name)) return; createNewDateSearchAlbum(name); } void TimeLineView::createNewDateSearchAlbum(const TQString& name) { int totalCount = 0; TQDateTime start, end; DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount); if (list.isEmpty()) { AlbumManager::instance()->setCurrentAlbum(0); return; } d->timeLineFolderView->blockSignals(true); d->timeLineFolderView->clearSelection(); d->timeLineFolderView->blockSignals(false); // We will make now the Url for digiKam Search KIO-Slave KURL url; url.setProtocol("digikamsearch"); int grp = list.count(); TQString path("1 AND 2"); if (grp > 1 ) { for (int i = 1 ; i < grp; i++) { path.append(" OR "); path.append(TQString("%1 AND %2").arg(i*2+1).arg(i*2+2)); } } url.setPath(path); int i = 0; DateRangeList::iterator it; for (it = list.begin() ; it != list.end(); ++it) { start = (*it).first; end = (*it).second; url.addQueryItem(TQString("%1.key").arg(i*2+1), TQString("imagedate")); url.addQueryItem(TQString("%1.op").arg(i*2+1), TQString("GT")); url.addQueryItem(TQString("%1.val").arg(i*2+1), start.date().toString(Qt::ISODate)); url.addQueryItem(TQString("%1.key").arg(i*2+2), TQString("imagedate")); url.addQueryItem(TQString("%1.op").arg(i*2+2), TQString("LT")); url.addQueryItem(TQString("%1.val").arg(i*2+2), end.date().toString(Qt::ISODate)); i++; } url.addQueryItem("name", name); url.addQueryItem("count", TQString::number(grp*2)); url.addQueryItem("type", TQString("datesearch")); //DDebug() << url << endl; SAlbum* album = AlbumManager::instance()->createSAlbum(url, false); AlbumManager::instance()->setCurrentAlbum(album); } void TimeLineView::slotAlbumSelected(SAlbum* salbum) { if (!salbum) { slotResetSelection(); return; } // Date Search url for KIO-Slave is something like that : // digikamsearch:1 AND 2 OR 3 AND 4 OR 5 AND 6? // 1.key=imagedate&1.op=GT&1.val=2006-02-06& // 2.key=imagedate&2.op=LT&2.val=2006-02-07& // 3.key=imagedate&3.op=GT&3.val=2006-02-10& // 4.key=imagedate&4.op=LT&4.val=2006-02-11& // 5.key=imagedate&5.op=GT&5.val=2006-02-12& // 6.key=imagedate&6.op=LT&6.val=2006-02-13& // name=TimeLineSelection& // count=6 // type=datesearch // Check if a special url query exist to identify a SAlbum dedicaced to Date Search KURL url = salbum->kurl(); TQMap queries = url.queryItems(); if (queries.isEmpty()) return; TQString type = url.queryItem("type"); if (type != TQString("datesearch")) return; bool ok = false; int count = url.queryItem("count").toInt(&ok); if (!ok || count <= 0) return; //DDebug() << url << endl; TQMap::iterator it2; TQString key; TQDateTime start, end; DateRangeList list; for (int i = 1 ; i <= count ; i+=2) { key = TQString("%1.val").arg(TQString::number(i)); it2 = queries.find(key); if (it2 != queries.end()) start = TQDateTime(TQDate::fromString(it2.data(), Qt::ISODate)); //DDebug() << key << " :: " << it2.data() << endl; key = TQString("%1.val").arg(TQString::number(i+1)); it2 = queries.find(key); if (it2 != queries.end()) end = TQDateTime(TQDate::fromString(it2.data(), Qt::ISODate)); //DDebug() << key << " :: " << it2.data() << endl; list.append(DateRange(start, end)); } /* DateRangeList::iterator it3; for (it3 = list.begin() ; it3 != list.end(); ++it3) DDebug() << (*it3).first.date().toString(Qt::ISODate) << " :: " << (*it3).second.date().toString(Qt::ISODate) << endl; */ d->timeLineWidget->setSelectedDateRange(list); AlbumManager::instance()->setCurrentAlbum(salbum); } void TimeLineView::slotResetSelection() { d->timeLineWidget->slotResetSelection(); slotCheckAboutSelection(); AlbumManager::instance()->setCurrentAlbum(0); } bool TimeLineView::checkName(TQString& name) { bool checked = checkAlbum(name); while (!checked) { TQString label = i18n( "Search name already exists.\n" "Please enter a new name:" ); bool ok; #if KDE_IS_VERSION(3,2,0) TQString newTitle = KInputDialog::getText(i18n("Name exists"), label, name, &ok, this); #else TQString newTitle = KLineEditDlg::getText(i18n("Name exists"), label, name, ok, this); #endif if (!ok) return false; name = newTitle; checked = checkAlbum(name); } return true; } bool TimeLineView::checkAlbum(const TQString& name) const { AlbumList list = AlbumManager::instance()->allSAlbums(); for (AlbumList::Iterator it = list.begin() ; it != list.end() ; ++it) { SAlbum *album = (SAlbum*)(*it); if ( album->title() == name ) return false; } return true; } void TimeLineView::slotCheckAboutSelection() { int totalCount = 0; DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount); if (!list.isEmpty()) { d->nameEdit->setEnabled(true); if (!d->nameEdit->text().isEmpty()) d->saveButton->setEnabled(true); } else { d->nameEdit->setEnabled(false); d->saveButton->setEnabled(false); } } void TimeLineView::slotRenameAlbum(SAlbum* salbum) { if (!salbum) return; TQString oldName(salbum->title()); bool ok; #if KDE_IS_VERSION(3,2,0) TQString name = KInputDialog::getText(i18n("Rename Album (%1)").arg(oldName), i18n("Enter new album name:"), oldName, &ok, this); #else TQString name = KLineEditDlg::getText(i18n("Rename Album (%1)").arg(oldName), i18n("Enter new album name:"), oldName, &ok, this); #endif if (!ok || name == oldName || name.isEmpty()) return; if (!checkName(name)) return; KURL url = salbum->kurl(); url.removeQueryItem("name"); url.addQueryItem("name", name); AlbumManager::instance()->updateSAlbum(salbum, url); } } // NameSpace Digikam