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/searchadvanceddialog.cpp

665 lines
20 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2005-01-01
* Description : a dialog to perform advanced search in albums
*
* Copyright (C) 2005 by Tom Albers <tomalbers@kde.nl>
* Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
* Copyright (C) 2006-2007 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.
*
* ============================================================ */
/** @file searchadvanceddialog.cpp */
// TQt includes.
#include <tqvbox.h>
#include <tqlayout.h>
#include <tqpushbutton.h>
#include <tqcombobox.h>
#include <tqhgroupbox.h>
#include <tqvgroupbox.h>
#include <tqlabel.h>
#include <tqtimer.h>
#include <tqwhatsthis.h>
// KDE includes.
#include <kurl.h>
#include <kiconloader.h>
#include <klocale.h>
#include <klineedit.h>
// Local includes.
#include "ddebug.h"
#include "searchwidgets.h"
#include "searchresultsview.h"
#include "searchadvanceddialog.h"
#include "searchadvanceddialog.moc"
namespace Digikam
{
class SearchAdvancedDialogPriv
{
public:
SearchAdvancedDialogPriv()
{
timer = 0;
title = 0;
optionsCombo = 0;
resultsView = 0;
ungroupButton = 0;
groupButton = 0;
delButton = 0;
addButton = 0;
rulesBox = 0;
}
TQVGroupBox *rulesBox;
TQPushButton *addButton;
TQPushButton *delButton;
TQPushButton *groupButton;
TQPushButton *ungroupButton;
TQComboBox *optionsCombo;
TQValueList<SearchAdvancedBase*> baseList;
TQTimer *timer;
KLineEdit *title;
SearchResultsView *resultsView;
};
SearchAdvancedDialog::SearchAdvancedDialog(TQWidget* parent, KURL& url)
: KDialogBase(parent, 0, true, i18n("Advanced Search"),
Help|Ok|Cancel, Ok, true), m_url(url)
{
d = new SearchAdvancedDialogPriv;
d->timer = new TQTimer(this);
setHelp("advancedsearchtool.anchor", "digikam");
TQWidget *page = new TQWidget( this );
setMainWidget(page);
resize(configDialogSize("AdvancedSearch Dialog"));
// ----------------------------------------------------------------
// two columns, one for the rules, one for the preview.
TQHBoxLayout* hbox = new TQHBoxLayout( page );
TQVBoxLayout* leftSide = new TQVBoxLayout();
d->resultsView = new SearchResultsView(page);
d->resultsView->setMinimumSize(TQSize(200, 200));
TQWhatsThis::add(d->resultsView, i18n("<p>Here you can review the images found "
"using the current search settings."));
hbox->addLayout(leftSide, 10);
hbox->setSpacing(spacingHint());
hbox->addWidget(d->resultsView, 5);
// ----------------------------------------------------------------
// Box for all the rules
d->rulesBox = new TQVGroupBox(i18n("Search Rules"), page);
TQWhatsThis::add(d->rulesBox, i18n("<p>Here you can review the search rules used to filter image-"
"searching in album library."));
d->rulesBox->layout()->setSpacing( spacingHint() );
d->rulesBox->layout()->setMargin( spacingHint() );
d->rulesBox->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding );
d->rulesBox->layout()->setAlignment( TQt::AlignTop );
// ----------------------------------------------------------------
// Box for the add/delete
TQGroupBox *groupbox1 = new TQGroupBox( i18n("Add/Delete Option"), page, "groupbox1" );
TQWhatsThis::add(groupbox1, i18n("<p>You can edit the search rules "
"by adding/removing criteria."));
groupbox1->setColumnLayout(0, Qt::Vertical );
groupbox1->layout()->setSpacing( KDialog::spacingHint() );
groupbox1->layout()->setMargin( KDialog::marginHint() );
d->optionsCombo = new TQComboBox(groupbox1);
d->optionsCombo->insertItem(i18n("As well as"), 0);
d->optionsCombo->insertItem(i18n("Or"), 1);
d->optionsCombo->setEnabled(false);
d->addButton = new TQPushButton(i18n("&Add"), groupbox1);
d->delButton = new TQPushButton(i18n("&Del"), groupbox1);
d->addButton->setIconSet(SmallIcon("add"));
d->delButton->setIconSet(SmallIcon("remove"));
TQHBoxLayout* box1 = new TQHBoxLayout(groupbox1->layout());
box1->addWidget(d->optionsCombo);
box1->addWidget(d->addButton);
box1->addStretch(10);
box1->addWidget(d->delButton);
// ----------------------------------------------------------------
// Box for the group/ungroup
TQGroupBox *groupbox2 = new TQGroupBox( i18n("Group/Ungroup Options"), page, "groupbox2" );
TQWhatsThis::add(groupbox1, i18n("<p>You can group or ungroup any search criteria "
"from the Search Rule set."));
groupbox2->setColumnLayout(0, Qt::Vertical);
groupbox2->layout()->setSpacing( KDialog::spacingHint() );
groupbox2->layout()->setMargin( KDialog::marginHint() );
d->groupButton = new TQPushButton(i18n("&Group"), groupbox2);
d->ungroupButton = new TQPushButton(i18n("&Ungroup"), groupbox2);
TQHBoxLayout* box2 = new TQHBoxLayout(groupbox2->layout());
box2->addWidget(d->groupButton);
box2->addStretch(10);
box2->addWidget(d->ungroupButton);
// ----------------------------------------------------------------
// box for saving the search.
TQGroupBox *groupbox3 = new TQGroupBox( page, "groupbox3");
groupbox3->setColumnLayout(0, Qt::Vertical );
groupbox3->layout()->setSpacing( KDialog::spacingHint() );
groupbox3->setFrameStyle( TQFrame::NoFrame );
TQLabel* label = new TQLabel(i18n("&Save search as: "), groupbox3);
d->title = new KLineEdit(groupbox3, "searchTitle");
TQWhatsThis::add(d->title, i18n("<p>Enter the name used to save the current search in "
"\"My Searches\" view"));
TQHBoxLayout* box3 = new TQHBoxLayout(groupbox3->layout());
box3->addWidget(label);
box3->addWidget(d->title);
label->setBuddy(d->title);
// ----------------------------------------------------------------
leftSide->addWidget( d->rulesBox );
leftSide->addStretch(10); // Push the rulesbox to top and the buttons down.
leftSide->addWidget(groupbox1);
leftSide->addWidget(groupbox2);
leftSide->addWidget(groupbox3);
// ----------------------------------------------------------------
if ( url.isEmpty() )
{
d->title->setText(i18n("Last Search"));
slotAddRule();
}
else
{
d->title->setText(url.queryItem("name"));
fillWidgets( url );
}
slotChangeButtonStates();
d->timer->start(0, true);
// ----------------------------------------------------------------
connect(d->addButton, TQT_SIGNAL(clicked()),
this, TQT_SLOT(slotAddRule()));
connect(d->delButton, TQT_SIGNAL(clicked()),
this, TQT_SLOT(slotDelRules()));
connect(d->groupButton, TQT_SIGNAL(clicked()),
this, TQT_SLOT(slotGroupRules()));
connect(d->ungroupButton, TQT_SIGNAL(clicked()),
this, TQT_SLOT(slotUnGroupRules()));
connect(d->timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotTimeOut()));
connect(d->title, TQT_SIGNAL(textChanged(const TQString&)),
this, TQT_SLOT(slotChangeButtonStates()));
}
SearchAdvancedDialog::~SearchAdvancedDialog()
{
saveDialogSize("AdvancedSearch Dialog");
delete d->timer;
delete d;
}
void SearchAdvancedDialog::slotAddRule()
{
SearchAdvancedBase::Option type = SearchAdvancedBase::NONE;
if ( !d->baseList.isEmpty() )
{
if (d->optionsCombo->currentItem() == 0 )
type = SearchAdvancedBase::AND;
else
type = SearchAdvancedBase::OR;
}
SearchAdvancedRule* rule = new SearchAdvancedRule( d->rulesBox, type );
d->baseList.append(rule);
connect( rule, TQT_SIGNAL( signalBaseItemToggled() ),
this, TQT_SLOT( slotChangeButtonStates() ) );
connect( rule, TQT_SIGNAL( signalPropertyChanged() ),
this, TQT_SLOT(slotPropertyChanged()));
slotChangeButtonStates();
slotPropertyChanged();
}
void SearchAdvancedDialog::slotDelRules()
{
if (d->baseList.isEmpty())
return;
typedef TQValueList<SearchAdvancedBase*> BaseList;
BaseList itemsToRemove;
for (BaseList::iterator it = d->baseList.begin();
it != d->baseList.end(); ++it)
{
SearchAdvancedBase* base = *it;
if (base->isChecked())
{
itemsToRemove.append(base);
}
}
for (BaseList::iterator it = itemsToRemove.begin();
it != itemsToRemove.end(); ++it)
{
SearchAdvancedBase* base = (SearchAdvancedBase*) *it;
d->baseList.remove(base);
delete base;
}
BaseList::iterator it = d->baseList.begin();
if (it != d->baseList.end())
(*it)->removeOption();
slotChangeButtonStates();
slotPropertyChanged();
if (d->baseList.isEmpty())
{
d->optionsCombo->setEnabled(false);
d->addButton->setEnabled(true);
enableButtonOK( false );
}
}
void SearchAdvancedDialog::slotGroupRules()
{
typedef TQValueList<SearchAdvancedBase*> BaseList;
BaseList itemsToGroup;
BaseList groupsToUnGroupAndGroup;
for (BaseList::iterator it = d->baseList.begin();
it != d->baseList.end(); ++it)
{
SearchAdvancedBase* base = *it;
if ( base->isChecked() )
{
itemsToGroup.append( base );
if ( base->type() == SearchAdvancedBase::GROUP)
groupsToUnGroupAndGroup.append( base );
}
}
// ungroup every found group so it can be regrouped later on.
for (BaseList::iterator it = groupsToUnGroupAndGroup.begin();
it != groupsToUnGroupAndGroup.end(); ++it)
{
SearchAdvancedGroup* group = (SearchAdvancedGroup*)*it;
BaseList::iterator itemsToGroupPos = itemsToGroup.find(group);
BaseList::iterator itPos = d->baseList.find(group);
TQValueList<SearchAdvancedRule*> childRules = group->childRules();
for (TQValueList<SearchAdvancedRule*>::iterator iter = childRules.begin();
iter != childRules.end(); ++iter)
{
d->baseList.insert(itPos, *iter);
itemsToGroup.insert(itemsToGroupPos, *iter);
}
group->removeRules();
d->baseList.remove(group);
itemsToGroup.remove(group);
delete group;
}
// if there is only one or no item return
if (itemsToGroup.size() < 2)
return;
BaseList::iterator it = itemsToGroup.begin();
SearchAdvancedRule* rule = (SearchAdvancedRule*)(*it);
SearchAdvancedGroup* group = new SearchAdvancedGroup(d->rulesBox);
BaseList::iterator itPos = d->baseList.find(rule);
d->baseList.insert(itPos, group);
for (BaseList::iterator it = itemsToGroup.begin();
it != itemsToGroup.end(); ++it)
{
SearchAdvancedBase* base = *it;
if (base->type() == SearchAdvancedBase::RULE)
{
SearchAdvancedRule* rule = (SearchAdvancedRule*)base;
group->addRule(rule);
d->baseList.remove(rule);
}
}
for (BaseList::iterator it = d->baseList.begin();
it != d->baseList.end(); ++it)
{
d->rulesBox->layout()->remove((*it)->widget());
d->rulesBox->layout()->add((*it)->widget());
}
connect( group, TQT_SIGNAL( signalBaseItemToggled() ),
this, TQT_SLOT( slotChangeButtonStates() ) );
slotChangeButtonStates();
slotPropertyChanged();
}
void SearchAdvancedDialog::slotUnGroupRules()
{
typedef TQValueList<SearchAdvancedBase*> BaseList;
typedef TQValueList<SearchAdvancedGroup*> GroupList;
GroupList itemsToUnGroup;
for (BaseList::iterator it = d->baseList.begin();
it != d->baseList.end(); ++it)
{
SearchAdvancedBase* base = *it;
if (base->type() == SearchAdvancedBase::GROUP &&
base->isChecked())
{
itemsToUnGroup.append((SearchAdvancedGroup*)base);
}
}
if (itemsToUnGroup.isEmpty())
return;
for (GroupList::iterator it = itemsToUnGroup.begin();
it != itemsToUnGroup.end(); ++it)
{
SearchAdvancedGroup *group = *it;
TQValueList<SearchAdvancedRule*> childRules = group->childRules();
BaseList::iterator itPos = d->baseList.find(group);
for (TQValueList<SearchAdvancedRule*>::iterator iter = childRules.begin();
iter != childRules.end(); ++iter)
{
d->baseList.insert(itPos, *iter);
}
group->removeRules();
d->baseList.remove(group);
delete group;
}
for (BaseList::iterator it = d->baseList.begin();
it != d->baseList.end(); ++it)
{
d->rulesBox->layout()->remove((*it)->widget());
d->rulesBox->layout()->add((*it)->widget());
}
slotChangeButtonStates();
slotPropertyChanged();
}
void SearchAdvancedDialog::slotPropertyChanged()
{
d->timer->start(500, true);
}
void SearchAdvancedDialog::slotOk()
{
// calculate the latest url and name.
slotTimeOut();
// Since it's not possible to check the state of the ok button,
// check the state of the add button.
if ( d->addButton->isEnabled() )
KDialogBase::slotOk();
}
void SearchAdvancedDialog::slotTimeOut()
{
if (d->baseList.isEmpty())
return;
typedef TQValueList<SearchAdvancedBase*> BaseList;
TQString grouping;
int count = 0;
bool emptyVal = false;
KURL url;
url.setProtocol("digikamsearch");
for (BaseList::iterator it = d->baseList.begin();
it != d->baseList.end(); ++it)
{
SearchAdvancedBase* base = *it;
if (base->type() == SearchAdvancedBase::RULE)
{
SearchAdvancedRule* rule = (SearchAdvancedRule*)base;
TQString val = rule->urlValue();
if ( !val.isEmpty() )
{
if (rule->option() != SearchAdvancedBase::NONE &&
!count == 0 )
grouping += (rule->option() == SearchAdvancedBase::AND) ?
" AND " : " OR ";
grouping += TQString::number(++count);
url.addQueryItem( TQString::number(count) + ".key", rule->urlKey());
url.addQueryItem( TQString::number(count) + ".op", rule->urlOperator());
url.addQueryItem( TQString::number(count) + ".val", val);
}
else
emptyVal = true;
}
else
{
SearchAdvancedGroup* group = (SearchAdvancedGroup*)base;
TQString tempGrouping;
int curCount = count;
TQValueList<SearchAdvancedRule*> childRules = group->childRules();
for (TQValueList<SearchAdvancedRule*>::iterator iter =
childRules.begin();
iter != childRules.end(); ++iter)
{
SearchAdvancedRule* rule = (SearchAdvancedRule*)(*iter);
TQString val = rule->urlValue();
if ( !val.isEmpty() )
{
if (rule->option() != SearchAdvancedBase::NONE &&
!count == 0 )
tempGrouping += (rule->option() == SearchAdvancedBase::AND) ?
" AND " : " OR ";
tempGrouping += TQString::number(++count);
url.addQueryItem( TQString::number(count) + ".key", rule->urlKey());
url.addQueryItem( TQString::number(count) + ".op", rule->urlOperator());
url.addQueryItem( TQString::number(count) + ".val", val);
}
else
emptyVal = true;
}
if (!tempGrouping.isEmpty())
{
if (group->option() != SearchAdvancedBase::NONE &&
!curCount == 0 )
grouping += (group->option() == SearchAdvancedBase::AND) ?
" AND " : " OR ";
grouping += " ( " + tempGrouping + " ) ";
}
}
}
url.setPath(grouping);
url.addQueryItem("name", d->title->text());
url.addQueryItem("count", TQString::number(count));
m_url = url;
if (!count == 0)
d->resultsView->openURL( url );
DDebug() << url << endl;
if (!d->baseList.isEmpty())
{
if (!d->title->text().isEmpty())
enableButtonOK( !emptyVal );
d->addButton->setEnabled( !emptyVal );
d->optionsCombo->setEnabled( !emptyVal );
}
}
void SearchAdvancedDialog::slotChangeButtonStates()
{
bool group = false;
int counter = 0;
typedef TQValueList<SearchAdvancedBase*> BaseList;
for (BaseList::iterator it = d->baseList.begin();
it != d->baseList.end(); ++it)
{
SearchAdvancedBase* base = *it;
if (base->isChecked())
{
++counter;
if (base->type() == SearchAdvancedBase::GROUP)
group = true;
}
}
d->ungroupButton->setEnabled( group );
if ( counter == 0)
{
d->delButton->setEnabled(false);
d->groupButton->setEnabled(false);
}
else if ( counter == 1)
{
if (d->baseList.count() > 1)
d->delButton->setEnabled(true);
d->groupButton->setEnabled(false);
}
else if ( counter > 1 )
{
d->delButton->setEnabled(true);
d->groupButton->setEnabled(true);
}
enableButtonOK( !d->title->text().isEmpty() );
}
void SearchAdvancedDialog::fillWidgets( const KURL& url )
{
int count = url.queryItem("count").toInt();
if (count <= 0)
return;
TQMap<int, KURL> rulesMap;
for (int i=1; i<=count; i++)
{
KURL newRule;
TQString key = url.queryItem(TQString::number(i) + ".key");
TQString op = url.queryItem(TQString::number(i) + ".op");
TQString val = url.queryItem(TQString::number(i) + ".val");
newRule.setPath("1");
newRule.addQueryItem("1.key",key);
newRule.addQueryItem("1.op",op);
newRule.addQueryItem("1.val",val);
rulesMap.insert(i, newRule);
}
TQStringList strList = TQStringList::split(" ", url.path());
SearchAdvancedGroup* group = 0;
bool groupingActive = false;
SearchAdvancedBase::Option type = SearchAdvancedBase::NONE;
for ( TQStringList::Iterator it = strList.begin(); it != strList.end(); ++it )
{
bool ok;
int num = (*it).toInt(&ok);
if (ok)
{
SearchAdvancedRule* rule = new SearchAdvancedRule( d->rulesBox, type );
rule->setValues( rulesMap[num] );
connect( rule, TQT_SIGNAL( signalBaseItemToggled() ),
this, TQT_SLOT( slotChangeButtonStates() ) );
connect( rule, TQT_SIGNAL( signalPropertyChanged() ),
this, TQT_SLOT(slotPropertyChanged()));
if (groupingActive)
group->addRule(rule);
else
d->baseList.append(rule);
}
else if (*it == "OR")
{
type = SearchAdvancedRule::OR;
}
else if (*it == "AND")
{
type = SearchAdvancedRule::AND;
}
else if (*it == "(")
{
group = new SearchAdvancedGroup(d->rulesBox);
d->baseList.append(group);
connect( group, TQT_SIGNAL( signalBaseItemToggled() ),
this, TQT_SLOT( slotChangeButtonStates() ) );
groupingActive = true;
}
else if (*it == ")")
{
groupingActive = false;
}
else
{
DDebug() << "IGNORED:" << *it << endl;
}
}
enableButtonOK( true );
}
} // namespace Digikam