/*************************************************************************** copyright : (C) 2003-2006 by Robby Stephenson email : robby@periapsis.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of version 2 of the GNU General Public License as * * published by the Free Software Foundation; * * * ***************************************************************************/ // The tqlayout borrows heavily from kmsearchpatternedit.cpp in kmail // which is authored by Marc Mutz under the GPL #include "filterdialog.h" #include "tellico_kernel.h" #include "document.h" #include "collection.h" #include "fieldcompletion.h" #include "../tellico_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Tellico::FilterRuleWidget; using Tellico::FilterRuleWidgetLister; using Tellico::FilterDialog; FilterRuleWidget::FilterRuleWidget(FilterRule* rule_, TQWidget* parent_, const char* name_/*=0*/) : TQHBox(parent_, name_), m_editRegExp(0), m_editRegExpDialog(0) { initLists(); initWidget(); if(rule_) { setRule(rule_); } else { reset(); } } void FilterRuleWidget::initLists() { //---------- initialize list of filter fields if(m_ruleFieldList.isEmpty()) { m_ruleFieldList.append('<' + i18n("Any Field") + '>'); m_ruleFieldList += Kernel::self()->fieldTitles(); } //---------- initialize list of filter operators if(m_ruleFuncList.isEmpty()) { // also see FilterRule::matches() and FilterRule::Function // if you change the following strings! m_ruleFuncList.append(i18n("contains")); m_ruleFuncList.append(i18n("does not contain")); m_ruleFuncList.append(i18n("equals")); m_ruleFuncList.append(i18n("does not equal")); m_ruleFuncList.append(i18n("matches regexp")); m_ruleFuncList.append(i18n("does not match regexp")); } } void FilterRuleWidget::initWidget() { setSpacing(4); m_ruleField = new KComboBox(this); connect(m_ruleField, TQT_SIGNAL(activated(int)), TQT_SIGNAL(signalModified())); connect(m_ruleField, TQT_SIGNAL(activated(int)), TQT_SLOT(slotRuleFieldChanged(int))); m_ruleFunc = new KComboBox(this); connect(m_ruleFunc, TQT_SIGNAL(activated(int)), TQT_SIGNAL(signalModified())); m_ruleValue = new KLineEdit(this); connect(m_ruleValue, TQT_SIGNAL(textChanged(const TQString&)), TQT_SIGNAL(signalModified())); if(!KTrader::self()->query(TQString::fromLatin1("KRegExpEditor/KRegExpEditor")).isEmpty()) { m_editRegExp = new KPushButton(i18n("Edit..."), this); connect(m_editRegExp, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotEditRegExp())); connect(m_ruleFunc, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotRuleFunctionChanged(int))); slotRuleFunctionChanged(m_ruleFunc->currentItem()); } m_ruleField->insertStringList(m_ruleFieldList); // don't show sliders when popping up this menu // m_ruleField->setSizeLimit(m_ruleField->count()); // m_ruleField->adjustSize(); m_ruleFunc->insertStringList(m_ruleFuncList); // m_ruleFunc->adjustSize(); // connect(m_ruleField, TQT_SIGNAL(textChanged(const TQString &)), // this, TQT_SIGNAL(fieldChanged(const TQString &))); // connect(m_ruleValue, TQT_SIGNAL(textChanged(const TQString &)), // this, TQT_SIGNAL(contentsChanged(const TQString &))); } void FilterRuleWidget::slotEditRegExp() { if(m_editRegExpDialog == 0) { m_editRegExpDialog = KParts::ComponentFactory::createInstanceFromQuery(TQString::fromLatin1("KRegExpEditor/KRegExpEditor"), TQString(), TQT_TQOBJECT(this)); } KRegExpEditorInterface* iface = static_cast(m_editRegExpDialog->tqt_cast(TQString::fromLatin1("KRegExpEditorInterface"))); if(iface) { iface->setRegExp(m_ruleValue->text()); if(m_editRegExpDialog->exec() == TQDialog::Accepted) { m_ruleValue->setText(iface->regExp()); } } } void FilterRuleWidget::slotRuleFieldChanged(int which_) { Q_UNUSED(which_); TQString fieldTitle = m_ruleField->currentText(); if(fieldTitle.isEmpty() || fieldTitle[0] == '<') { m_ruleValue->setCompletionObject(0); return; } Data::FieldPtr field = Data::Document::self()->collection()->fieldByTitle(fieldTitle); if(field && (field->flags() & Data::Field::AllowCompletion)) { FieldCompletion* completion = new FieldCompletion(field->flags() & Data::Field::AllowMultiple); completion->setItems(Kernel::self()->valuesByFieldName(field->name())); completion->setIgnoreCase(true); m_ruleValue->setCompletionObject(completion); m_ruleValue->setAutoDeleteCompletionObject(true); } else { m_ruleValue->setCompletionObject(0); } } void FilterRuleWidget::slotRuleFunctionChanged(int which_) { // The 5th and 6th functions are for regexps m_editRegExp->setEnabled(which_ == 4 || which_ == 5); } void FilterRuleWidget::setRule(const FilterRule* rule_) { if(!rule_) { reset(); return; } blockSignals(true); if(rule_->fieldName().isEmpty()) { m_ruleField->setCurrentItem(0); // "All Fields" } else { m_ruleField->setCurrentText(Kernel::self()->fieldTitleByName(rule_->fieldName())); } //--------------set function and contents m_ruleFunc->setCurrentItem(static_cast(rule_->function())); m_ruleValue->setText(rule_->pattern()); if(m_editRegExp) { slotRuleFunctionChanged(static_cast(rule_->function())); } blockSignals(false); } Tellico::FilterRule* FilterRuleWidget::rule() const { TQString field; // empty string if(m_ruleField->currentItem() > 0) { // 0 is "All Fields", field is empty field = Kernel::self()->fieldNameByTitle(m_ruleField->currentText()); } return new FilterRule(field, m_ruleValue->text().stripWhiteSpace(), static_cast(m_ruleFunc->currentItem())); } void FilterRuleWidget::reset() { // kdDebug() << "FilterRuleWidget::reset()" << endl; blockSignals(true); m_ruleField->setCurrentItem(0); m_ruleFunc->setCurrentItem(0); m_ruleValue->clear(); if(m_editRegExp) { m_editRegExp->setEnabled(false); } blockSignals(false); } void FilterRuleWidget::setFocus() { m_ruleValue->setFocus(); } /***************************************************************/ namespace { static const int FILTER_MIN_RULE_WIDGETS = 1; static const int FILTER_MAX_RULES = 8; } FilterRuleWidgetLister::FilterRuleWidgetLister(TQWidget* parent_, const char* name_) : KWidgetLister(FILTER_MIN_RULE_WIDGETS, FILTER_MAX_RULES, parent_, name_) { // slotClear(); } void FilterRuleWidgetLister::setFilter(Filter::Ptr filter_) { // if(mWidgetList.first()) { // move this below next 'if'? // mWidgetList.first()->blockSignals(true); // } if(filter_->isEmpty()) { slotClear(); // mWidgetList.first()->blockSignals(false); return; } const int count = static_cast(filter_->count()); if(count > mMaxWidgets) { myDebug() << "FilterRuleWidgetLister::setFilter() - more rules than allowed!" << endl; } // set the right number of widgets setNumberOfShownWidgetsTo(TQMAX(count, mMinWidgets)); // load the actions into the widgets TQPtrListIterator wIt(mWidgetList); for(TQPtrListIterator rIt(*filter_); rIt.current() && wIt.current(); ++rIt, ++wIt) { static_cast(*wIt)->setRule(*rIt); } for( ; wIt.current(); ++wIt) { // clear any remaining static_cast(*wIt)->reset(); } // mWidgetList.first()->blockSignals(false); } void FilterRuleWidgetLister::reset() { slotClear(); } void FilterRuleWidgetLister::setFocus() { if(!mWidgetList.isEmpty()) { mWidgetList.getFirst()->setFocus(); } } TQWidget* FilterRuleWidgetLister::createWidget(TQWidget* parent_) { TQWidget* w = new FilterRuleWidget(static_cast(0), parent_); connect(w, TQT_SIGNAL(signalModified()), TQT_SIGNAL(signalModified())); return w; } void FilterRuleWidgetLister::clearWidget(TQWidget* widget_) { if(widget_) { static_cast(widget_)->reset(); } } const TQPtrList& FilterRuleWidgetLister::widgetList() const { return mWidgetList; } /***************************************************************/ namespace { static const int FILTER_MIN_WIDTH = 600; } // modal dialog so I don't have to worry about updating stuff // don't show apply button if not saving, i.e. just modifying existing filter FilterDialog::FilterDialog(Mode mode_, TQWidget* parent_, const char* name_/*=0*/) : KDialogBase(parent_, name_, true, (mode_ == CreateFilter ? i18n("Advanced Filter") : i18n("Modify Filter")), (mode_ == CreateFilter ? Help|Ok|Apply|Cancel : Help|Ok|Cancel), Ok, false), m_filter(0), m_mode(mode_), m_saveFilter(0) { init(); } void FilterDialog::init() { TQWidget* page = new TQWidget(this); setMainWidget(page); TQVBoxLayout* topLayout = new TQVBoxLayout(page, 0, KDialog::spacingHint()); TQGroupBox* m_matchGroup = new TQGroupBox(1, Qt::Horizontal, i18n("Filter Criteria"), page); topLayout->addWidget(m_matchGroup); TQVButtonGroup* bg = new TQVButtonGroup(m_matchGroup); bg->setFrameShape(TQFrame::NoFrame); bg->setInsideMargin(0); m_matchAll = new TQRadioButton(i18n("Match a&ll of the following"), bg); m_matchAny = new TQRadioButton(i18n("Match an&y of the following"), bg); m_matchAll->setChecked(true); connect(bg, TQT_SIGNAL(clicked(int)), TQT_SLOT(slotFilterChanged())); m_ruleLister = new FilterRuleWidgetLister(m_matchGroup); connect(m_ruleLister, TQT_SIGNAL(widgetRemoved()), TQT_SLOT(slotShrink())); connect(m_ruleLister, TQT_SIGNAL(signalModified()), TQT_SLOT(slotFilterChanged())); m_ruleLister->setFocus(); TQHBoxLayout* blay = new TQHBoxLayout(topLayout); blay->addWidget(new TQLabel(i18n("Filter name:"), page)); m_filterName = new KLineEdit(page); blay->addWidget(m_filterName); connect(m_filterName, TQT_SIGNAL(textChanged(const TQString&)), TQT_SLOT(slotFilterChanged())); // only when creating a new filter can it be saved if(m_mode == CreateFilter) { m_saveFilter = new KPushButton(SmallIconSet(TQString::fromLatin1("filter")), i18n("&Save Filter"), page); blay->addWidget(m_saveFilter); m_saveFilter->setEnabled(false); connect(m_saveFilter, TQT_SIGNAL(clicked()), TQT_SLOT(slotSaveFilter())); enableButtonApply(false); } enableButtonOK(false); // disable at start actionButton(Help)->setDefault(false); // Help automatically becomes default when OK is disabled actionButton(Cancel)->setDefault(true); // Help automatically becomes default when OK is disabled setMinimumWidth(TQMAX(minimumWidth(), FILTER_MIN_WIDTH)); setHelp(TQString::fromLatin1("filter-dialog")); } Tellico::FilterPtr FilterDialog::currentFilter() { if(!m_filter) { m_filter = new Filter(Filter::MatchAny); } if(m_matchAll->isChecked()) { m_filter->setMatch(Filter::MatchAll); } else { m_filter->setMatch(Filter::MatchAny); } m_filter->clear(); // deletes all old rules for(TQPtrListIterator it(m_ruleLister->widgetList()); it.current(); ++it) { FilterRuleWidget* rw = static_cast(it.current()); FilterRule* rule = rw->rule(); if(rule && !rule->isEmpty()) { m_filter->append(rule); } } // only set name if it has rules if(!m_filter->isEmpty()) { m_filter->setName(m_filterName->text()); } return m_filter; } void FilterDialog::setFilter(FilterPtr filter_) { if(!filter_) { slotClear(); return; } if(filter_->op() == Filter::MatchAll) { m_matchAll->setChecked(true); } else { m_matchAny->setChecked(true); } m_ruleLister->setFilter(filter_); m_filterName->setText(filter_->name()); m_filter = filter_; } void FilterDialog::slotOk() { slotApply(); accept(); } void FilterDialog::slotApply() { emit signalUpdateFilter(currentFilter()); } void FilterDialog::slotClear() { // kdDebug() << "FilterDialog::slotClear()" << endl; m_matchAll->setChecked(true); m_ruleLister->reset(); m_filterName->clear(); } void FilterDialog::slotShrink() { updateGeometry(); TQApplication::sendPostedEvents(); resize(width(), sizeHint().height()); } void FilterDialog::slotFilterChanged() { bool emptyFilter = currentFilter()->isEmpty(); if(m_saveFilter) { m_saveFilter->setEnabled(!m_filterName->text().isEmpty() && !emptyFilter); enableButtonApply(!emptyFilter); } enableButtonOK(!emptyFilter); actionButton(Ok)->setDefault(!emptyFilter); } void FilterDialog::slotSaveFilter() { // non-op if editing an existing filter if(m_mode != CreateFilter) { return; } // in this case, currentFilter() either creates a new filter or // updates the current one. If creating a new one, then I want to copy it bool wasEmpty = (m_filter == 0); FilterPtr filter = new Filter(*currentFilter()); if(wasEmpty) { m_filter = filter; } // this keeps the saving completely decoupled from the filter setting in the detailed view if(filter->isEmpty()) { m_filter = 0; return; } Kernel::self()->addFilter(filter); } #include "filterdialog.moc"