/* ============================================================ * * 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 * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2006-2007 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. * * ============================================================ */ /** @file searchadvanceddialog.cpp */ // TQt includes. #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include #include // 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 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("

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("

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("

You can edit the search rules " "by adding/removing criteria.")); groupbox1->setColumnLayout(0, TQt::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("

You can group or ungroup any search criteria " "from the Search Rule set.")); groupbox2->setColumnLayout(0, TQt::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, TQt::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("

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 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 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 childRules = group->childRules(); for (TQValueList::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 BaseList; typedef TQValueList 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 childRules = group->childRules(); BaseList::iterator itPos = d->baseList.find(group); for (TQValueList::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 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 childRules = group->childRules(); for (TQValueList::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 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 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