You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/digikam/timelineview.cpp

646 lines
21 KiB

/* ============================================================
*
* 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 <caulier dot gilles at gmail dot com>
*
* 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 <tqtimer.h>
#include <tqframe.h>
#include <tqhbox.h>
#include <tqlayout.h>
#include <tqcombobox.h>
#include <tqpushbutton.h>
#include <tqhbuttongroup.h>
#include <tqmap.h>
#include <tqscrollbar.h>
#include <tqwhatsthis.h>
#include <tqtooltip.h>
// KDE includes.
#include <klocale.h>
#include <tdeconfig.h>
#include <kdialog.h>
#include <klineedit.h>
#include <kiconloader.h>
#include <kapplication.h>
#include <ksqueezedtextlabel.h>
#include <kstandarddirs.h>
#include <tdeversion.h>
#include <kmessagebox.h>
#if KDE_IS_VERSION(3,2,0)
#include <kinputdialog.h>
#else
#include <klineeditdlg.h>
#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("<p>Select the histogram time unit here.<p>"
"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("<p>Select the histogram scale here.<p>"
"If the date count's maximal values are small, you can use the linear scale.<p>"
"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( "<p>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( "<p>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("<p>If you press this button, current "
"dates selection from time-line will be "
"clear."));
d->nameEdit = new KLineEdit(hbox2);
TQWhatsThis::add(d->nameEdit, i18n("<p>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("<p>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<TQDateTime, int>&)),
d->timeLineWidget, TQT_SLOT(slotDatesMap(const TQMap<TQDateTime, int>&)));
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<TQString, TQString> 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<TQString, TQString>::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