File indexing completed on 2024-04-28 05:08:22
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 "gui/filterrulewidgetlister.h" 0031 #include "gui/filterrulewidget.h" 0032 0033 #include <KLocalizedString> 0034 #include <KHelpClient> 0035 0036 #include <QGroupBox> 0037 #include <QRadioButton> 0038 #include <QButtonGroup> 0039 #include <QLabel> 0040 #include <QApplication> 0041 #include <QFrame> 0042 #include <QVBoxLayout> 0043 #include <QPushButton> 0044 #include <QLineEdit> 0045 #include <QDialogButtonBox> 0046 0047 using Tellico::FilterDialog; 0048 0049 namespace { 0050 static const int FILTER_MIN_WIDTH = 600; 0051 } 0052 0053 // modal dialog so I don't have to worry about updating stuff 0054 // don't show apply button if not saving, i.e. just modifying existing filter 0055 FilterDialog::FilterDialog(Mode mode_, QWidget* parent_) 0056 : QDialog(parent_), m_filter(nullptr), m_mode(mode_), m_saveFilter(nullptr) { 0057 setModal(true); 0058 setWindowTitle(mode_ == CreateFilter ? i18n("Advanced Filter") : i18n("Modify Filter")); 0059 0060 QVBoxLayout* topLayout = new QVBoxLayout(); 0061 setLayout(topLayout); 0062 0063 QDialogButtonBox* buttonBox; 0064 if(mode_ == CreateFilter) { 0065 buttonBox = new QDialogButtonBox(QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply); 0066 } else { 0067 buttonBox = new QDialogButtonBox(QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Cancel); 0068 } 0069 m_okButton = buttonBox->button(QDialogButtonBox::Ok); 0070 m_applyButton = buttonBox->button(QDialogButtonBox::Apply); 0071 connect(m_okButton, &QAbstractButton::clicked, this, &FilterDialog::slotOk); 0072 if(m_applyButton) { 0073 connect(m_applyButton, &QAbstractButton::clicked, this, &FilterDialog::slotApply); 0074 } 0075 connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); 0076 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0077 connect(buttonBox, &QDialogButtonBox::helpRequested, this, &FilterDialog::slotHelp); 0078 0079 QGroupBox* m_matchGroup = new QGroupBox(i18n("Filter Criteria"), this); 0080 QVBoxLayout* vlay = new QVBoxLayout(m_matchGroup); 0081 topLayout->addWidget(m_matchGroup); 0082 m_matchGroup->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); 0083 0084 m_matchAll = new QRadioButton(i18n("Match a&ll of the following"), m_matchGroup); 0085 m_matchAny = new QRadioButton(i18n("Match an&y of the following"), m_matchGroup); 0086 m_matchAll->setChecked(true); 0087 0088 vlay->addWidget(m_matchAll); 0089 vlay->addWidget(m_matchAny); 0090 0091 QButtonGroup* bg = new QButtonGroup(m_matchGroup); 0092 bg->addButton(m_matchAll); 0093 bg->addButton(m_matchAny); 0094 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 0095 void (QButtonGroup::* buttonClicked)(int) = &QButtonGroup::buttonClicked; 0096 connect(bg, buttonClicked, this, &FilterDialog::slotFilterChanged); 0097 #else 0098 connect(bg, &QButtonGroup::idClicked, this, &FilterDialog::slotFilterChanged); 0099 #endif 0100 0101 m_ruleLister = new FilterRuleWidgetLister(m_matchGroup); 0102 connect(m_ruleLister, &KWidgetLister::widgetRemoved, this, &FilterDialog::slotShrink); 0103 connect(m_ruleLister, &FilterRuleWidgetLister::signalModified, this, &FilterDialog::slotFilterChanged); 0104 m_ruleLister->setFocus(); 0105 vlay->addWidget(m_ruleLister); 0106 0107 QHBoxLayout* blay = new QHBoxLayout(); 0108 topLayout->addLayout(blay); 0109 QLabel* lab = new QLabel(i18n("Filter name:"), this); 0110 blay->addWidget(lab); 0111 0112 m_filterName = new QLineEdit(this); 0113 blay->addWidget(m_filterName); 0114 connect(m_filterName, &QLineEdit::textChanged, this, &FilterDialog::slotFilterChanged); 0115 0116 // only when creating a new filter can it be saved 0117 if(m_mode == CreateFilter) { 0118 m_saveFilter = new QPushButton(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("&Save Filter"), this); 0119 blay->addWidget(m_saveFilter); 0120 m_saveFilter->setEnabled(false); 0121 connect(m_saveFilter, &QAbstractButton::clicked, this, &FilterDialog::slotSaveFilter); 0122 m_applyButton->setEnabled(false); 0123 } 0124 m_okButton->setEnabled(false); // disable at start 0125 buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true); 0126 setMinimumWidth(qMax(minimumWidth(), FILTER_MIN_WIDTH)); 0127 topLayout->addWidget(buttonBox); 0128 } 0129 0130 Tellico::FilterPtr FilterDialog::currentFilter(bool alwaysCreateNew_) { 0131 FilterPtr newFilter(new Filter(Filter::MatchAny)); 0132 0133 if(m_matchAll->isChecked()) { 0134 newFilter->setMatch(Filter::MatchAll); 0135 } else { 0136 newFilter->setMatch(Filter::MatchAny); 0137 } 0138 0139 foreach(QWidget* widget, m_ruleLister->widgetList()) { 0140 FilterRuleWidget* rw = static_cast<FilterRuleWidget*>(widget); 0141 FilterRule* rule = rw->rule(); 0142 if(rule && !rule->isEmpty()) { 0143 newFilter->append(rule); 0144 } else { 0145 delete rule; 0146 } 0147 } 0148 newFilter->setName(m_filterName->text()); 0149 if(!m_filter || !alwaysCreateNew_) { 0150 m_filter = newFilter; 0151 } 0152 return newFilter; 0153 } 0154 0155 void FilterDialog::setFilter(Tellico::FilterPtr filter_) { 0156 if(!filter_) { 0157 slotClear(); 0158 return; 0159 } 0160 0161 if(filter_->op() == Filter::MatchAll) { 0162 m_matchAll->setChecked(true); 0163 } else { 0164 m_matchAny->setChecked(true); 0165 } 0166 0167 m_ruleLister->setFilter(filter_); 0168 m_filterName->setText(filter_->name()); 0169 m_filter = filter_; 0170 } 0171 0172 void FilterDialog::slotOk() { 0173 slotApply(); 0174 accept(); 0175 } 0176 0177 void FilterDialog::slotApply() { 0178 emit signalUpdateFilter(currentFilter()); 0179 } 0180 0181 void FilterDialog::slotHelp() { 0182 KHelpClient::invokeHelp(QStringLiteral("filter-dialog")); 0183 } 0184 0185 void FilterDialog::slotClear() { 0186 // myDebug(); 0187 m_matchAll->setChecked(true); 0188 m_ruleLister->reset(); 0189 m_filterName->clear(); 0190 } 0191 0192 void FilterDialog::slotShrink() { 0193 updateGeometry(); 0194 QApplication::sendPostedEvents(); 0195 resize(width(), sizeHint().height()); 0196 } 0197 0198 void FilterDialog::slotFilterChanged() { 0199 const bool hadFilter = m_filter && !m_filter->isEmpty(); 0200 const bool emptyFilter = currentFilter(true)->isEmpty(); 0201 // an empty filter can be ok if the filter was originally not empty 0202 const bool enableOk = !currentFilter()->isEmpty() || hadFilter; 0203 if(m_saveFilter) { 0204 m_saveFilter->setEnabled(!m_filterName->text().isEmpty() && !emptyFilter); 0205 if(m_applyButton) { 0206 m_applyButton->setEnabled(!emptyFilter); 0207 } 0208 } 0209 if(m_applyButton) { 0210 m_applyButton->setEnabled(enableOk); 0211 } 0212 m_okButton->setEnabled(enableOk); 0213 m_okButton->setDefault(enableOk); 0214 } 0215 0216 void FilterDialog::slotSaveFilter() { 0217 // non-op if editing an existing filter 0218 if(m_mode != CreateFilter) { 0219 return; 0220 } 0221 0222 // in this case, currentFilter() either creates a new filter or 0223 // updates the current one. If creating a new one, then I want to copy it 0224 const bool wasEmpty = !m_filter; 0225 FilterPtr filter(new Filter(*currentFilter())); 0226 if(wasEmpty) { 0227 m_filter = filter; 0228 } 0229 // this keeps the saving completely decoupled from the filter setting in the detailed view 0230 if(filter->isEmpty()) { 0231 m_filter = FilterPtr(); 0232 return; 0233 } 0234 Kernel::self()->addFilter(filter); 0235 }