File indexing completed on 2024-05-05 04:19:15
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2008 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "filtercontroller.h" 0023 0024 // Qt 0025 #include <QAction> 0026 #include <QCompleter> 0027 #include <QIcon> 0028 #include <QLineEdit> 0029 #include <QPainter> 0030 #include <QPainterPath> 0031 #include <QTimer> 0032 #include <QToolButton> 0033 0034 // KF 0035 #include <KComboBox> 0036 #include <KIconLoader> 0037 #include <KLocalizedString> 0038 0039 // Local 0040 #include "gwenview_app_debug.h" 0041 #include <chrono> 0042 #include <lib/flowlayout.h> 0043 #include <lib/paintutils.h> 0044 0045 using namespace std::chrono_literals; 0046 0047 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE 0048 // KF 0049 0050 // Local 0051 #endif 0052 0053 namespace Gwenview 0054 { 0055 NameFilterWidget::NameFilterWidget(SortedDirModel *model) 0056 : mFilter(new NameFilter(model)) 0057 { 0058 mModeComboBox = new KComboBox; 0059 mModeComboBox->addItem(i18n("Name contains"), QVariant(NameFilter::Contains)); 0060 mModeComboBox->addItem(i18n("Name does not contain"), QVariant(NameFilter::DoesNotContain)); 0061 0062 mLineEdit = new QLineEdit; 0063 0064 auto layout = new QHBoxLayout(this); 0065 layout->setContentsMargins(0, 0, 0, 0); 0066 layout->setSpacing(2); 0067 layout->addWidget(mModeComboBox); 0068 layout->addWidget(mLineEdit); 0069 0070 auto timer = new QTimer(this); 0071 timer->setInterval(350ms); 0072 timer->setSingleShot(true); 0073 connect(timer, &QTimer::timeout, this, &NameFilterWidget::applyNameFilter); 0074 0075 connect(mLineEdit, SIGNAL(textChanged(QString)), timer, SLOT(start())); 0076 0077 connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(applyNameFilter())); 0078 0079 QTimer::singleShot(0, mLineEdit, SLOT(setFocus())); 0080 } 0081 0082 NameFilterWidget::~NameFilterWidget() 0083 { 0084 delete mFilter; 0085 } 0086 0087 void NameFilterWidget::applyNameFilter() 0088 { 0089 const QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex()); 0090 mFilter->setMode(NameFilter::Mode(data.toInt())); 0091 mFilter->setText(mLineEdit->text()); 0092 } 0093 0094 DateFilterWidget::DateFilterWidget(SortedDirModel *model) 0095 : mFilter(new DateFilter(model)) 0096 { 0097 mModeComboBox = new KComboBox; 0098 mModeComboBox->addItem(i18n("Date >="), DateFilter::GreaterOrEqual); 0099 mModeComboBox->addItem(i18n("Date ="), DateFilter::Equal); 0100 mModeComboBox->addItem(i18n("Date <="), DateFilter::LessOrEqual); 0101 0102 mDateWidget = new DateWidget; 0103 0104 auto layout = new QHBoxLayout(this); 0105 layout->setContentsMargins(0, 0, 0, 0); 0106 layout->addWidget(mModeComboBox); 0107 layout->addWidget(mDateWidget); 0108 0109 connect(mDateWidget, &DateWidget::dateChanged, this, &DateFilterWidget::applyDateFilter); 0110 connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(applyDateFilter())); 0111 0112 applyDateFilter(); 0113 } 0114 0115 DateFilterWidget::~DateFilterWidget() 0116 { 0117 delete mFilter; 0118 } 0119 0120 void DateFilterWidget::applyDateFilter() 0121 { 0122 const QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex()); 0123 mFilter->setMode(DateFilter::Mode(data.toInt())); 0124 mFilter->setDate(mDateWidget->date()); 0125 } 0126 0127 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE 0128 RatingFilterWidget::RatingFilterWidget(SortedDirModel *model) 0129 { 0130 mModeComboBox = new KComboBox; 0131 mModeComboBox->addItem(i18n("Rating >="), RatingFilter::GreaterOrEqual); 0132 mModeComboBox->addItem(i18n("Rating ="), RatingFilter::Equal); 0133 mModeComboBox->addItem(i18n("Rating <="), RatingFilter::LessOrEqual); 0134 0135 mRatingWidget = new KRatingWidget; 0136 mRatingWidget->setHalfStepsEnabled(true); 0137 mRatingWidget->setMaxRating(10); 0138 0139 auto layout = new QHBoxLayout(this); 0140 layout->setContentsMargins(0, 0, 0, 0); 0141 layout->addWidget(mModeComboBox); 0142 layout->addWidget(mRatingWidget); 0143 0144 mFilter = new RatingFilter(model); 0145 0146 QObject::connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateFilterMode())); 0147 0148 QObject::connect(mRatingWidget, SIGNAL(ratingChanged(int)), SLOT(slotRatingChanged(int))); 0149 0150 updateFilterMode(); 0151 } 0152 0153 RatingFilterWidget::~RatingFilterWidget() 0154 { 0155 delete mFilter; 0156 } 0157 0158 void RatingFilterWidget::slotRatingChanged(int value) 0159 { 0160 mFilter->setRating(value); 0161 } 0162 0163 void RatingFilterWidget::updateFilterMode() 0164 { 0165 const QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex()); 0166 mFilter->setMode(RatingFilter::Mode(data.toInt())); 0167 } 0168 0169 TagFilterWidget::TagFilterWidget(SortedDirModel *model) 0170 : mFilter(new TagFilter(model)) 0171 { 0172 mModeComboBox = new KComboBox; 0173 mModeComboBox->addItem(i18n("Tagged"), QVariant(true)); 0174 mModeComboBox->addItem(i18n("Not Tagged"), QVariant(false)); 0175 0176 mTagComboBox = new QComboBox; 0177 0178 auto layout = new QHBoxLayout(this); 0179 layout->setContentsMargins(0, 0, 0, 0); 0180 layout->addWidget(mModeComboBox); 0181 layout->addWidget(mTagComboBox); 0182 0183 AbstractSemanticInfoBackEnd *backEnd = model->semanticInfoBackEnd(); 0184 backEnd->refreshAllTags(); 0185 TagModel *tagModel = TagModel::createAllTagsModel(this, backEnd); 0186 0187 auto completer = new QCompleter(mTagComboBox); 0188 completer->setCaseSensitivity(Qt::CaseInsensitive); 0189 completer->setModel(tagModel); 0190 mTagComboBox->setCompleter(completer); 0191 mTagComboBox->setInsertPolicy(QComboBox::NoInsert); 0192 mTagComboBox->setEditable(true); 0193 mTagComboBox->setModel(tagModel); 0194 mTagComboBox->setCurrentIndex(-1); 0195 0196 connect(mTagComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTagSetFilter())); 0197 0198 connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTagSetFilter())); 0199 0200 QTimer::singleShot(0, mTagComboBox, SLOT(setFocus())); 0201 } 0202 0203 TagFilterWidget::~TagFilterWidget() 0204 { 0205 delete mFilter; 0206 } 0207 0208 void TagFilterWidget::updateTagSetFilter() 0209 { 0210 QModelIndex index = mTagComboBox->model()->index(mTagComboBox->currentIndex(), 0); 0211 if (!index.isValid()) { 0212 qCWarning(GWENVIEW_APP_LOG) << "Invalid index"; 0213 return; 0214 } 0215 SemanticInfoTag tag = index.data(TagModel::TagRole).toString(); 0216 mFilter->setTag(tag); 0217 0218 bool wantMatchingTag = mModeComboBox->itemData(mModeComboBox->currentIndex()).toBool(); 0219 mFilter->setWantMatchingTag(wantMatchingTag); 0220 } 0221 #endif 0222 0223 /** 0224 * A container for all filter widgets. It features a close button on the right. 0225 */ 0226 class FilterWidgetContainer : public QFrame 0227 { 0228 public: 0229 FilterWidgetContainer() 0230 { 0231 QPalette pal = palette(); 0232 pal.setColor(QPalette::Window, pal.color(QPalette::Highlight)); 0233 setPalette(pal); 0234 } 0235 0236 void setFilterWidget(QWidget *widget) 0237 { 0238 auto closeButton = new QToolButton; 0239 closeButton->setIcon(QIcon::fromTheme(QStringLiteral("window-close"))); 0240 closeButton->setAutoRaise(true); 0241 closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); 0242 int size = style()->pixelMetric(QStyle::PM_SmallIconSize); 0243 closeButton->setIconSize(QSize(size, size)); 0244 connect(closeButton, &QAbstractButton::clicked, this, &QObject::deleteLater); 0245 auto layout = new QHBoxLayout(this); 0246 layout->setContentsMargins(2, 2, 2, 2); 0247 layout->setSpacing(2); 0248 layout->addWidget(widget); 0249 layout->addWidget(closeButton); 0250 } 0251 0252 protected: 0253 void paintEvent(QPaintEvent *) override 0254 { 0255 QPainter painter(this); 0256 painter.setRenderHint(QPainter::Antialiasing); 0257 const QPainterPath path = PaintUtils::roundedRectangle(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), 6); 0258 0259 const QColor color = palette().color(QPalette::Highlight); 0260 painter.fillPath(path, PaintUtils::alphaAdjustedF(color, 0.5)); 0261 painter.setPen(color); 0262 painter.drawPath(path); 0263 } 0264 }; 0265 0266 FilterController::FilterController(QFrame *frame, SortedDirModel *dirModel) 0267 : QObject(frame) 0268 { 0269 q = this; 0270 mFrame = frame; 0271 mDirModel = dirModel; 0272 mFilterWidgetCount = 0; 0273 0274 mFrame->hide(); 0275 auto layout = new FlowLayout(mFrame); 0276 layout->setSpacing(2); 0277 0278 addAction(i18nc("@action:inmenu", "Filter by Name"), SLOT(addFilterByName()), QKeySequence(Qt::CTRL | Qt::Key_I)); 0279 0280 addAction(i18nc("@action:inmenu", "Filter by Date"), SLOT(addFilterByDate()), QKeySequence()); 0281 0282 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE 0283 addAction(i18nc("@action:inmenu", "Filter by Rating"), SLOT(addFilterByRating()), QKeySequence()); 0284 0285 addAction(i18nc("@action:inmenu", "Filter by Tag"), SLOT(addFilterByTag()), QKeySequence()); 0286 #endif 0287 } 0288 0289 QList<QAction *> FilterController::actionList() const 0290 { 0291 return mActionList; 0292 } 0293 0294 void FilterController::addFilterByName() 0295 { 0296 addFilter(new NameFilterWidget(mDirModel)); 0297 } 0298 0299 void FilterController::addFilterByDate() 0300 { 0301 addFilter(new DateFilterWidget(mDirModel)); 0302 } 0303 0304 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE 0305 void FilterController::addFilterByRating() 0306 { 0307 addFilter(new RatingFilterWidget(mDirModel)); 0308 } 0309 0310 void FilterController::addFilterByTag() 0311 { 0312 addFilter(new TagFilterWidget(mDirModel)); 0313 } 0314 #endif 0315 0316 void FilterController::slotFilterWidgetClosed() 0317 { 0318 mFilterWidgetCount--; 0319 if (mFilterWidgetCount == 0) { 0320 mFrame->hide(); 0321 } 0322 } 0323 0324 void FilterController::addAction(const QString &text, const char *slot, const QKeySequence &shortcut) 0325 { 0326 auto action = new QAction(text, q); 0327 action->setShortcut(shortcut); 0328 QObject::connect(action, SIGNAL(triggered()), q, slot); 0329 mActionList << action; 0330 } 0331 0332 void FilterController::addFilter(QWidget *widget) 0333 { 0334 if (mFrame->isHidden()) { 0335 mFrame->show(); 0336 } 0337 auto container = new FilterWidgetContainer; 0338 container->setFilterWidget(widget); 0339 mFrame->layout()->addWidget(container); 0340 0341 mFilterWidgetCount++; 0342 QObject::connect(container, &QObject::destroyed, q, &FilterController::slotFilterWidgetClosed); 0343 } 0344 0345 } // namespace 0346 0347 #include "moc_filtercontroller.cpp"