File indexing completed on 2024-04-28 16:32:00
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 // The layout borrows heavily from kmsearchpatternedit.cpp in kmail 0026 // which is authored by Marc Mutz <Marc@Mutz.com> under the GPL 0027 0028 #include "filterdialog.h" 0029 #include "tellico_kernel.h" 0030 #include "document.h" 0031 #include "collection.h" 0032 #include "fieldcompletion.h" 0033 #include "gui/filterrulewidgetlister.h" 0034 #include "gui/filterrulewidget.h" 0035 #include "tellico_debug.h" 0036 0037 #include <KLocalizedString> 0038 #include <KHelpClient> 0039 0040 #include <QGroupBox> 0041 #include <QRadioButton> 0042 #include <QButtonGroup> 0043 #include <QLabel> 0044 #include <QApplication> 0045 #include <QFrame> 0046 #include <QVBoxLayout> 0047 #include <QPushButton> 0048 #include <QLineEdit> 0049 #include <QDialogButtonBox> 0050 0051 using Tellico::FilterDialog; 0052 0053 namespace { 0054 static const int FILTER_MIN_WIDTH = 600; 0055 } 0056 0057 // modal dialog so I don't have to worry about updating stuff 0058 // don't show apply button if not saving, i.e. just modifying existing filter 0059 FilterDialog::FilterDialog(Mode mode_, QWidget* parent_) 0060 : QDialog(parent_), m_filter(nullptr), m_mode(mode_), m_saveFilter(nullptr) { 0061 setModal(true); 0062 setWindowTitle(mode_ == CreateFilter ? i18n("Advanced Filter") : i18n("Modify Filter")); 0063 0064 QVBoxLayout* topLayout = new QVBoxLayout(); 0065 setLayout(topLayout); 0066 0067 QDialogButtonBox* buttonBox; 0068 if(mode_ == CreateFilter) { 0069 buttonBox = new QDialogButtonBox(QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply); 0070 } else { 0071 buttonBox = new QDialogButtonBox(QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Cancel); 0072 } 0073 m_okButton = buttonBox->button(QDialogButtonBox::Ok); 0074 m_applyButton = buttonBox->button(QDialogButtonBox::Apply); 0075 connect(m_okButton, &QAbstractButton::clicked, this, &FilterDialog::slotOk); 0076 if(m_applyButton) { 0077 connect(m_applyButton, &QAbstractButton::clicked, this, &FilterDialog::slotApply); 0078 } 0079 connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); 0080 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0081 connect(buttonBox, &QDialogButtonBox::helpRequested, this, &FilterDialog::slotHelp); 0082 0083 QGroupBox* m_matchGroup = new QGroupBox(i18n("Filter Criteria"), this); 0084 QVBoxLayout* vlay = new QVBoxLayout(m_matchGroup); 0085 topLayout->addWidget(m_matchGroup); 0086 m_matchGroup->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); 0087 0088 m_matchAll = new QRadioButton(i18n("Match a&ll of the following"), m_matchGroup); 0089 m_matchAny = new QRadioButton(i18n("Match an&y of the following"), m_matchGroup); 0090 m_matchAll->setChecked(true); 0091 0092 vlay->addWidget(m_matchAll); 0093 vlay->addWidget(m_matchAny); 0094 0095 QButtonGroup* bg = new QButtonGroup(m_matchGroup); 0096 bg->addButton(m_matchAll); 0097 bg->addButton(m_matchAny); 0098 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 0099 void (QButtonGroup::* buttonClicked)(int) = &QButtonGroup::buttonClicked; 0100 connect(bg, buttonClicked, this, &FilterDialog::slotFilterChanged); 0101 #else 0102 connect(bg, &QButtonGroup::idClicked, this, &FilterDialog::slotFilterChanged); 0103 #endif 0104 0105 m_ruleLister = new FilterRuleWidgetLister(m_matchGroup); 0106 connect(m_ruleLister, &KWidgetLister::widgetRemoved, this, &FilterDialog::slotShrink); 0107 connect(m_ruleLister, &FilterRuleWidgetLister::signalModified, this, &FilterDialog::slotFilterChanged); 0108 m_ruleLister->setFocus(); 0109 vlay->addWidget(m_ruleLister); 0110 0111 QHBoxLayout* blay = new QHBoxLayout(); 0112 topLayout->addLayout(blay); 0113 QLabel* lab = new QLabel(i18n("Filter name:"), this); 0114 blay->addWidget(lab); 0115 0116 m_filterName = new QLineEdit(this); 0117 blay->addWidget(m_filterName); 0118 connect(m_filterName, &QLineEdit::textChanged, this, &FilterDialog::slotFilterChanged); 0119 0120 // only when creating a new filter can it be saved 0121 if(m_mode == CreateFilter) { 0122 m_saveFilter = new QPushButton(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("&Save Filter"), this); 0123 blay->addWidget(m_saveFilter); 0124 m_saveFilter->setEnabled(false); 0125 connect(m_saveFilter, &QAbstractButton::clicked, this, &FilterDialog::slotSaveFilter); 0126 m_applyButton->setEnabled(false); 0127 } 0128 m_okButton->setEnabled(false); // disable at start 0129 buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true); 0130 setMinimumWidth(qMax(minimumWidth(), FILTER_MIN_WIDTH)); 0131 topLayout->addWidget(buttonBox); 0132 } 0133 0134 Tellico::FilterPtr FilterDialog::currentFilter(bool alwaysCreateNew_) { 0135 FilterPtr newFilter(new Filter(Filter::MatchAny)); 0136 0137 if(m_matchAll->isChecked()) { 0138 newFilter->setMatch(Filter::MatchAll); 0139 } else { 0140 newFilter->setMatch(Filter::MatchAny); 0141 } 0142 0143 foreach(QWidget* widget, m_ruleLister->widgetList()) { 0144 FilterRuleWidget* rw = static_cast<FilterRuleWidget*>(widget); 0145 FilterRule* rule = rw->rule(); 0146 if(rule && !rule->isEmpty()) { 0147 newFilter->append(rule); 0148 } else { 0149 delete rule; 0150 } 0151 } 0152 newFilter->setName(m_filterName->text()); 0153 if(!m_filter || !alwaysCreateNew_) { 0154 m_filter = newFilter; 0155 } 0156 return newFilter; 0157 } 0158 0159 void FilterDialog::setFilter(Tellico::FilterPtr filter_) { 0160 if(!filter_) { 0161 slotClear(); 0162 return; 0163 } 0164 0165 if(filter_->op() == Filter::MatchAll) { 0166 m_matchAll->setChecked(true); 0167 } else { 0168 m_matchAny->setChecked(true); 0169 } 0170 0171 m_ruleLister->setFilter(filter_); 0172 m_filterName->setText(filter_->name()); 0173 m_filter = filter_; 0174 } 0175 0176 void FilterDialog::slotOk() { 0177 slotApply(); 0178 accept(); 0179 } 0180 0181 void FilterDialog::slotApply() { 0182 emit signalUpdateFilter(currentFilter()); 0183 } 0184 0185 void FilterDialog::slotHelp() { 0186 KHelpClient::invokeHelp(QStringLiteral("filter-dialog")); 0187 } 0188 0189 void FilterDialog::slotClear() { 0190 // myDebug(); 0191 m_matchAll->setChecked(true); 0192 m_ruleLister->reset(); 0193 m_filterName->clear(); 0194 } 0195 0196 void FilterDialog::slotShrink() { 0197 updateGeometry(); 0198 QApplication::sendPostedEvents(); 0199 resize(width(), sizeHint().height()); 0200 } 0201 0202 void FilterDialog::slotFilterChanged() { 0203 const bool hadFilter = m_filter && !m_filter->isEmpty(); 0204 const bool emptyFilter = currentFilter(true)->isEmpty(); 0205 // an empty filter can be ok if the filter was originally not empty 0206 const bool enableOk = !currentFilter()->isEmpty() || hadFilter; 0207 if(m_saveFilter) { 0208 m_saveFilter->setEnabled(!m_filterName->text().isEmpty() && !emptyFilter); 0209 if(m_applyButton) { 0210 m_applyButton->setEnabled(!emptyFilter); 0211 } 0212 } 0213 if(m_applyButton) { 0214 m_applyButton->setEnabled(enableOk); 0215 } 0216 m_okButton->setEnabled(enableOk); 0217 m_okButton->setDefault(enableOk); 0218 } 0219 0220 void FilterDialog::slotSaveFilter() { 0221 // non-op if editing an existing filter 0222 if(m_mode != CreateFilter) { 0223 return; 0224 } 0225 0226 // in this case, currentFilter() either creates a new filter or 0227 // updates the current one. If creating a new one, then I want to copy it 0228 const bool wasEmpty = !m_filter; 0229 FilterPtr filter(new Filter(*currentFilter())); 0230 if(wasEmpty) { 0231 m_filter = filter; 0232 } 0233 // this keeps the saving completely decoupled from the filter setting in the detailed view 0234 if(filter->isEmpty()) { 0235 m_filter = FilterPtr(); 0236 return; 0237 } 0238 Kernel::self()->addFilter(filter); 0239 }