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.
tellico/src/filterdialog.cpp

429 lines
14 KiB

/***************************************************************************
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 <Marc@Mutz.com> under the GPL
#include "filterdialog.h"
#include "tellico_kernel.h"
#include "document.h"
#include "collection.h"
#include "fieldcompletion.h"
#include "../tellico_debug.h"
#include <tdelocale.h>
#include <kcombobox.h>
#include <klineedit.h>
#include <kpushbutton.h>
#include <tdeparts/componentfactory.h>
#include <kregexpeditorinterface.h>
#include <kiconloader.h>
#include <tqlayout.h>
#include <tqgroupbox.h>
#include <tqradiobutton.h>
#include <tqvbuttongroup.h>
#include <tqhbox.h>
#include <tqvbox.h>
#include <tqlabel.h>
#include <tqapplication.h>
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(!TDETrader::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<TQDialog>(TQString::fromLatin1("KRegExpEditor/KRegExpEditor"),
TQString(), TQT_TQOBJECT(this));
}
KRegExpEditorInterface* iface = static_cast<KRegExpEditorInterface *>(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<int>(rule_->function()));
m_ruleValue->setText(rule_->pattern());
if(m_editRegExp) {
slotRuleFunctionChanged(static_cast<int>(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<FilterRule::Function>(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<int>(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<TQWidget> wIt(mWidgetList);
for(TQPtrListIterator<FilterRule> rIt(*filter_); rIt.current() && wIt.current(); ++rIt, ++wIt) {
static_cast<FilterRuleWidget*>(*wIt)->setRule(*rIt);
}
for( ; wIt.current(); ++wIt) { // clear any remaining
static_cast<FilterRuleWidget*>(*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<Tellico::FilterRule*>(0), parent_);
connect(w, TQT_SIGNAL(signalModified()), TQT_SIGNAL(signalModified()));
return w;
}
void FilterRuleWidgetLister::clearWidget(TQWidget* widget_) {
if(widget_) {
static_cast<FilterRuleWidget*>(widget_)->reset();
}
}
const TQPtrList<TQWidget>& 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<TQWidget> it(m_ruleLister->widgetList()); it.current(); ++it) {
FilterRuleWidget* rw = static_cast<FilterRuleWidget*>(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"