filtercontroller.cpp 10.1 KB
Newer Older
1
// vim: set tabstop=4 shiftwidth=4 expandtab:
2
3
/*
Gwenview: an image viewer
Aurélien Gâteau's avatar
Aurélien Gâteau committed
4
Copyright 2008 Aurélien Gâteau <agateau@kde.org>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.

*/
// Self
David Edmundson's avatar
David Edmundson committed
22
#include "filtercontroller.h"
23
24
25

// Qt
#include <QAction>
26
#include <QCompleter>
27
#include <QLineEdit>
28
29
#include <QPainter>
#include <QPainterPath>
30
#include <QTimer>
31
#include <QToolButton>
Lukáš Tinkl's avatar
Lukáš Tinkl committed
32
#include <QIcon>
33

34
// KF
Aurélien Gâteau's avatar
Aurélien Gâteau committed
35
36
#include <KComboBox>
#include <KIconLoader>
Lukáš Tinkl's avatar
Lukáš Tinkl committed
37
#include <KLocalizedString>
38
39

// Local
40
#include "gwenview_app_debug.h"
41
#include <lib/flowlayout.h>
42
#include <lib/paintutils.h>
43

Aurélien Gâteau's avatar
Aurélien Gâteau committed
44
#ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
45
// KF
Aurélien Gâteau's avatar
Aurélien Gâteau committed
46
47
48
49

// Local
#endif

50
51
namespace Gwenview
{
52

53
54
NameFilterWidget::NameFilterWidget(SortedDirModel* model)
{
55
    mFilter = new NameFilter(model);
56

57
58
59
    mModeComboBox = new KComboBox;
    mModeComboBox->addItem(i18n("Name contains"), QVariant(NameFilter::Contains));
    mModeComboBox->addItem(i18n("Name does not contain"), QVariant(NameFilter::DoesNotContain));
60

61
    mLineEdit = new QLineEdit;
62

63
    auto* layout = new QHBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
64
    layout->setContentsMargins(0, 0, 0, 0);
65
    layout->setSpacing(2);
66
67
    layout->addWidget(mModeComboBox);
    layout->addWidget(mLineEdit);
68

69
    auto* timer = new QTimer(this);
70
71
    timer->setInterval(350);
    timer->setSingleShot(true);
Laurent Montel's avatar
Laurent Montel committed
72
    connect(timer, &QTimer::timeout, this, &NameFilterWidget::applyNameFilter);
73

74
    connect(mLineEdit, SIGNAL(textChanged(QString)),
75
            timer, SLOT(start()));
76

77
    connect(mModeComboBox, SIGNAL(currentIndexChanged(int)),
78
            SLOT(applyNameFilter()));
79

80
    QTimer::singleShot(0, mLineEdit, SLOT(setFocus()));
81
82
83
84
}

NameFilterWidget::~NameFilterWidget()
{
85
    delete mFilter;
86
87
}

88
89
void NameFilterWidget::applyNameFilter()
{
90
91
92
    QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex());
    mFilter->setMode(NameFilter::Mode(data.toInt()));
    mFilter->setText(mLineEdit->text());
93
}
94

Aurélien Gâteau's avatar
Aurélien Gâteau committed
95
96
DateFilterWidget::DateFilterWidget(SortedDirModel* model)
{
97
    mFilter = new DateFilter(model);
Aurélien Gâteau's avatar
Aurélien Gâteau committed
98

99
100
101
102
    mModeComboBox = new KComboBox;
    mModeComboBox->addItem(i18n("Date >="), DateFilter::GreaterOrEqual);
    mModeComboBox->addItem(i18n("Date ="),  DateFilter::Equal);
    mModeComboBox->addItem(i18n("Date <="), DateFilter::LessOrEqual);
Aurélien Gâteau's avatar
Aurélien Gâteau committed
103

104
    mDateWidget = new DateWidget;
Aurélien Gâteau's avatar
Aurélien Gâteau committed
105

106
    auto* layout = new QHBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
107
    layout->setContentsMargins(0, 0, 0, 0);
108
109
    layout->addWidget(mModeComboBox);
    layout->addWidget(mDateWidget);
Aurélien Gâteau's avatar
Aurélien Gâteau committed
110

Laurent Montel's avatar
Laurent Montel committed
111
112
    connect(mDateWidget, &DateWidget::dateChanged,
            this, &DateFilterWidget::applyDateFilter);
113
    connect(mModeComboBox, SIGNAL(currentIndexChanged(int)),
114
            SLOT(applyDateFilter()));
115

116
    applyDateFilter();
Aurélien Gâteau's avatar
Aurélien Gâteau committed
117
118
119
120
}

DateFilterWidget::~DateFilterWidget()
{
121
    delete mFilter;
Aurélien Gâteau's avatar
Aurélien Gâteau committed
122
123
}

124
125
void DateFilterWidget::applyDateFilter()
{
126
127
128
    QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex());
    mFilter->setMode(DateFilter::Mode(data.toInt()));
    mFilter->setDate(mDateWidget->date());
Aurélien Gâteau's avatar
Aurélien Gâteau committed
129
130
}

Aurélien Gâteau's avatar
Aurélien Gâteau committed
131
132
#ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
RatingFilterWidget::RatingFilterWidget(SortedDirModel* model)
133
{
134
135
136
137
    mModeComboBox = new KComboBox;
    mModeComboBox->addItem(i18n("Rating >="), RatingFilter::GreaterOrEqual);
    mModeComboBox->addItem(i18n("Rating =") , RatingFilter::Equal);
    mModeComboBox->addItem(i18n("Rating <="), RatingFilter::LessOrEqual);
138

139
140
141
    mRatingWidget = new KRatingWidget;
    mRatingWidget->setHalfStepsEnabled(true);
    mRatingWidget->setMaxRating(10);
142

143
    auto* layout = new QHBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
144
    layout->setContentsMargins(0, 0, 0, 0);
145
146
    layout->addWidget(mModeComboBox);
    layout->addWidget(mRatingWidget);
Aurélien Gâteau's avatar
Aurélien Gâteau committed
147

148
    mFilter = new RatingFilter(model);
Aurélien Gâteau's avatar
Aurélien Gâteau committed
149

150
    QObject::connect(mModeComboBox, SIGNAL(currentIndexChanged(int)),
151
                     SLOT(updateFilterMode()));
152

153
    QObject::connect(mRatingWidget, SIGNAL(ratingChanged(int)),
154
                     SLOT(slotRatingChanged(int)));
155

156
    updateFilterMode();
Aurélien Gâteau's avatar
Aurélien Gâteau committed
157
158
}

159
160
RatingFilterWidget::~RatingFilterWidget()
{
161
    delete mFilter;
Aurélien Gâteau's avatar
Aurélien Gâteau committed
162
163
}

164
165
void RatingFilterWidget::slotRatingChanged(int value)
{
166
    mFilter->setRating(value);
167
168
}

169
170
void RatingFilterWidget::updateFilterMode()
{
171
172
    QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex());
    mFilter->setMode(RatingFilter::Mode(data.toInt()));
Aurélien Gâteau's avatar
Aurélien Gâteau committed
173
174
}

175
176

TagFilterWidget::TagFilterWidget(SortedDirModel* model)
177
{
178
    mFilter = new TagFilter(model);
179

180
181
182
    mModeComboBox = new KComboBox;
    mModeComboBox->addItem(i18n("Tagged"), QVariant(true));
    mModeComboBox->addItem(i18n("Not Tagged"), QVariant(false));
183

184
    mTagComboBox = new QComboBox;
185

186
    auto* layout = new QHBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
187
    layout->setContentsMargins(0, 0, 0, 0);
188
189
    layout->addWidget(mModeComboBox);
    layout->addWidget(mTagComboBox);
190

191
192
193
    AbstractSemanticInfoBackEnd* backEnd = model->semanticInfoBackEnd();
    backEnd->refreshAllTags();
    TagModel* tagModel = TagModel::createAllTagsModel(this, backEnd);
194

195
    auto* completer = new QCompleter(mTagComboBox);
196
197
    completer->setCaseSensitivity(Qt::CaseInsensitive);
    completer->setModel(tagModel);
198
199
200
201
202
    mTagComboBox->setCompleter(completer);
    mTagComboBox->setInsertPolicy(QComboBox::NoInsert);
    mTagComboBox->setEditable(true);
    mTagComboBox->setModel(tagModel);
    mTagComboBox->setCurrentIndex(-1);
203

204
    connect(mTagComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTagSetFilter()));
205

206
    connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTagSetFilter()));
207

208
    QTimer::singleShot(0, mTagComboBox, SLOT(setFocus()));
209
210
}

211
212
TagFilterWidget::~TagFilterWidget()
{
213
    delete mFilter;
214
215
}

216
217
void TagFilterWidget::updateTagSetFilter()
{
218
    QModelIndex index = mTagComboBox->model()->index(mTagComboBox->currentIndex(), 0);
219
    if (!index.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
220
        qCWarning(GWENVIEW_APP_LOG) << "Invalid index";
221
222
223
        return;
    }
    SemanticInfoTag tag = index.data(TagModel::TagRole).toString();
224
    mFilter->setTag(tag);
225

226
227
    bool wantMatchingTag = mModeComboBox->itemData(mModeComboBox->currentIndex()).toBool();
    mFilter->setWantMatchingTag(wantMatchingTag);
228
}
229
#endif
230

Aurélien Gâteau's avatar
Doc    
Aurélien Gâteau committed
231
232
233
/**
 * A container for all filter widgets. It features a close button on the right.
 */
234
235
class FilterWidgetContainer : public QFrame
{
236
public:
Aurélien Gâteau's avatar
Aurélien Gâteau committed
237
238
    FilterWidgetContainer()
    {
239
240
241
242
243
244
245
        QPalette pal = palette();
        pal.setColor(QPalette::Window, pal.color(QPalette::Highlight));
        setPalette(pal);
    }

    void setFilterWidget(QWidget* widget)
    {
246
        auto* closeButton = new QToolButton;
Laurent Montel's avatar
Laurent Montel committed
247
        closeButton->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
248
249
        closeButton->setAutoRaise(true);
        closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
250
        int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
251
        closeButton->setIconSize(QSize(size, size));
Laurent Montel's avatar
Laurent Montel committed
252
        connect(closeButton, &QAbstractButton::clicked, this, &QObject::deleteLater);
253
        auto* layout = new QHBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
254
        layout->setContentsMargins(2, 2, 2, 2);
255
256
257
258
        layout->setSpacing(2);
        layout->addWidget(widget);
        layout->addWidget(closeButton);
    }
259
260

protected:
261
    void paintEvent(QPaintEvent*) override
262
263
264
265
266
267
268
269
270
271
    {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        QPainterPath path = PaintUtils::roundedRectangle(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), 6);

        QColor color = palette().color(QPalette::Highlight);
        painter.fillPath(path, PaintUtils::alphaAdjustedF(color, 0.5));
        painter.setPen(color);
        painter.drawPath(path);
    }
272
273
274
275
276
};


FilterController::FilterController(QFrame* frame, SortedDirModel* dirModel)
: QObject(frame)
277
{
278
279
280
281
    q = this;
    mFrame = frame;
    mDirModel = dirModel;
    mFilterWidgetCount = 0;
282

283
    mFrame->hide();
284
    auto* layout = new FlowLayout(mFrame);
285
286
    layout->setSpacing(2);

287
288
    addAction(i18nc("@action:inmenu", "Filter by Name"),
              SLOT(addFilterByName()),
289
              QKeySequence(Qt::CTRL | Qt::Key_I));
290
291
292
293
294

    addAction(i18nc("@action:inmenu", "Filter by Date"),
              SLOT(addFilterByDate()),
              QKeySequence());

295
#ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
296
297
298
299
300
301
302
    addAction(i18nc("@action:inmenu", "Filter by Rating"),
              SLOT(addFilterByRating()),
              QKeySequence());

    addAction(i18nc("@action:inmenu", "Filter by Tag"),
              SLOT(addFilterByTag()),
              QKeySequence());
303
#endif
304
305
}

306
307
QList<QAction*> FilterController::actionList() const
{
308
    return mActionList;
309
310
}

311
312
void FilterController::addFilterByName()
{
313
    addFilter(new NameFilterWidget(mDirModel));
314
315
}

316
317
void FilterController::addFilterByDate()
{
318
    addFilter(new DateFilterWidget(mDirModel));
Aurélien Gâteau's avatar
Aurélien Gâteau committed
319
320
}

321
#ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
322
323
void FilterController::addFilterByRating()
{
324
    addFilter(new RatingFilterWidget(mDirModel));
325
326
}

327
328
void FilterController::addFilterByTag()
{
329
    addFilter(new TagFilterWidget(mDirModel));
330
}
331
#endif
332

333
334
void FilterController::slotFilterWidgetClosed()
{
335
336
337
    mFilterWidgetCount--;
    if (mFilterWidgetCount == 0) {
        mFrame->hide();
338
    }
339
340
}

341

Laurent Montel's avatar
Laurent Montel committed
342
void FilterController::addAction(const QString& text, const char* slot, const QKeySequence &shortcut)
343
{
344
    auto* action = new QAction(text, q);
345
    action->setShortcut(shortcut);
346
347
348
349
350
351
352
353
354
355
    QObject::connect(action, SIGNAL(triggered()), q, slot);
    mActionList << action;
}


void FilterController::addFilter(QWidget* widget)
{
    if (mFrame->isHidden()) {
        mFrame->show();
    }
356
    auto* container = new FilterWidgetContainer;
357
358
359
360
    container->setFilterWidget(widget);
    mFrame->layout()->addWidget(container);

    mFilterWidgetCount++;
Laurent Montel's avatar
Laurent Montel committed
361
    QObject::connect(container, &QObject::destroyed, q, &FilterController::slotFilterWidgetClosed);
362
363
}

364
} // namespace