Astyle kdelibs
[kdepim.git] / kaddressbook / categoryselectwidget.cpp
blob664414f240af6ab86edf18cd098543090279d406
1 /*
2 Copyright (c) 2014 Jonathan Marten <jjm@keelhaul.me.uk>
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
20 #include "categoryselectwidget.h"
22 #include "kaddressbook_debug.h"
23 #include <KLocalizedString>
24 #include <qtoolbutton.h>
25 #include <qlayout.h>
26 #include <qstandarditemmodel.h>
27 #include <qtimer.h>
29 #include <AkonadiCore/monitor.h>
30 #include <AkonadiCore/tagmodel.h>
32 #include <widgets/kcheckcombobox.h>
34 using namespace Akonadi;
36 static const int FILTER_ROLE = Qt::UserRole + 1;
38 class CategorySelectWidgetPrivate : public QObject
40 Q_OBJECT
41 Q_DECLARE_PUBLIC(CategorySelectWidget)
43 public:
44 explicit CategorySelectWidgetPrivate(CategorySelectWidget *parent);
46 Akonadi::TagModel *tagModel;
47 int rowOffset;
48 QTimer *updateTimer;
49 KPIM::KCheckComboBox *checkCombo;
51 void init();
52 QStandardItemModel *itemModel() const;
53 void selectAll(Qt::CheckState state) const;
54 QList<Akonadi::Tag::Id> filterTags() const;
56 public Q_SLOTS:
57 void slotSelectAll();
58 void slotSelectNone();
60 void slotTagsInserted(const QModelIndex &parent, int start, int end);
61 void slotTagsRemoved(const QModelIndex &parent, int start, int end);
62 void slotTagsChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
64 void slotCheckedItemsChanged();
65 void slotCheckedItemsTimer();
67 private:
68 CategorySelectWidget *q_ptr;
71 CategorySelectWidgetPrivate::CategorySelectWidgetPrivate(CategorySelectWidget *parent)
72 : QObject(),
73 tagModel(Q_NULLPTR),
74 rowOffset(0),
75 updateTimer(Q_NULLPTR),
76 checkCombo(Q_NULLPTR),
77 q_ptr(parent)
81 void CategorySelectWidgetPrivate::init()
83 Q_Q(CategorySelectWidget);
85 QHBoxLayout *hbox = new QHBoxLayout(q);
86 hbox->setSpacing(0);
88 checkCombo = new KPIM::KCheckComboBox;
89 checkCombo->setMinimumWidth(150);
90 checkCombo->setSqueezeText(true);
91 connect(checkCombo, &KPIM::KCheckComboBox::checkedItemsChanged,
92 this, &CategorySelectWidgetPrivate::slotCheckedItemsChanged);
93 hbox->addWidget(checkCombo);
95 Monitor *monitor = new Monitor(this);
96 monitor->setTypeMonitored(Monitor::Tags);
97 tagModel = new Akonadi::TagModel(monitor, this);
99 connect(tagModel, &QAbstractItemModel::rowsInserted,
100 this, &CategorySelectWidgetPrivate::slotTagsInserted);
101 connect(tagModel, &QAbstractItemModel::rowsRemoved,
102 this, &CategorySelectWidgetPrivate::slotTagsRemoved);
103 connect(tagModel, &QAbstractItemModel::dataChanged,
104 this, &CategorySelectWidgetPrivate::slotTagsChanged);
106 updateTimer = new QTimer(this);
107 updateTimer->setSingleShot(true);
108 updateTimer->setInterval(200);
109 connect(updateTimer, &QTimer::timeout, this, &CategorySelectWidgetPrivate::slotCheckedItemsTimer);
111 QToolButton *but = new QToolButton(q);
112 but ->setAutoRaise(true);
113 but->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
114 but->setToolTip(i18nc("@action:button", "Reset category filter"));
115 connect(but, &QToolButton::clicked, this, &CategorySelectWidgetPrivate::slotSelectAll);
116 hbox->addWidget(but);
118 but = new QToolButton(q);
119 but->setAutoRaise(true);
120 but->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
121 but->setToolTip(i18nc("@action:button", "Clear category filter"));
122 connect(but, &QToolButton::clicked, this, &CategorySelectWidgetPrivate::slotSelectNone);
123 hbox->addWidget(but);
125 QStandardItem *item = new QStandardItem(i18n("(Untagged)"));
126 item->setCheckState(Qt::Checked);
127 item->setData(CategorySelectWidget::FilterUntagged, FILTER_ROLE);
128 itemModel()->appendRow(item);
130 item = new QStandardItem(i18n("(Groups)"));
131 item->setCheckState(Qt::Checked);
132 item->setData(CategorySelectWidget::FilterGroups, FILTER_ROLE);
133 itemModel()->appendRow(item);
135 rowOffset = itemModel()->rowCount();
138 QStandardItemModel *CategorySelectWidgetPrivate::itemModel() const
140 QStandardItemModel *m = qobject_cast<QStandardItemModel *>(checkCombo->model());
141 Q_ASSERT(m != NULL);
142 return m;
145 void CategorySelectWidgetPrivate::slotTagsRemoved(const QModelIndex &parent, int start, int end)
147 itemModel()->removeRows(start + rowOffset, end + rowOffset, parent);
150 void CategorySelectWidgetPrivate::slotTagsChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
152 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
153 QStandardItem *it = itemModel()->item(row + rowOffset);
154 Q_ASSERT(it != NULL);
156 QModelIndex idx = tagModel->index(row, 0);
157 it->setText(tagModel->data(idx, TagModel::NameRole).toString());
158 it->setIcon(tagModel->data(idx, Qt::DecorationRole).value<QIcon>());
159 it->setData(tagModel->data(idx, TagModel::IdRole), FILTER_ROLE);
163 void CategorySelectWidgetPrivate::slotTagsInserted(const QModelIndex &parent, int start, int end)
165 for (int row = start; row <= end; ++row) {
166 QModelIndex idx = tagModel->index(row, 0, parent);
167 #if 0
168 qCDebug(KADDRESSBOOK_LOG) << "idx" << idx << "=" << tagModel->data(idx, Qt::DisplayRole).toString()
169 << "name" << tagModel->data(idx, TagModel::NameRole).toString()
170 << "tag" << tagModel->data(idx, TagModel::TagRole)
171 << "id" << tagModel->data(idx, TagModel::IdRole).toInt();
172 #endif
173 QStandardItem *it = new QStandardItem(tagModel->data(idx, TagModel::NameRole).toString());
174 it->setIcon(tagModel->data(idx, Qt::DecorationRole).value<QIcon>());
175 it->setData(tagModel->data(idx, TagModel::IdRole), FILTER_ROLE);
176 it->setCheckState(Qt::Checked);
178 // If a tag with a parent arrives from the model, we know that its parent
179 // must already have arrived. So there is no need for a list of pending
180 // tags, as is required in Akonadi::TagModel.
182 // FIXME: not tested (no way to create hierarchial tags at present)
183 if (parent != QModelIndex()) {
184 const Tag::Id parentId = tagModel->data(idx, TagModel::IdRole).value<Tag::Id>();
185 QModelIndexList matchList = itemModel()->match(itemModel()->index(0, 0), FILTER_ROLE,
186 parentId, 1,
187 Qt::MatchExactly | Qt::MatchRecursive);
188 if (matchList.count() == 1) { // found the parent tag
189 QModelIndex parentIndex = matchList.at(0);
190 itemModel()->itemFromIndex(parentIndex)->appendRow(it);
191 } else {
192 qCWarning(KADDRESSBOOK_LOG) << "Cannot find parent with ID" << parentId;
193 itemModel()->insertRow(row + rowOffset, it);
195 } else {
196 itemModel()->insertRow(row + rowOffset, it);
201 void CategorySelectWidgetPrivate::selectAll(Qt::CheckState state) const
203 for (int row = 0; row < itemModel()->rowCount(); ++row) {
204 QStandardItem *it = itemModel()->item(row);
205 it->setCheckState(state);
209 void CategorySelectWidgetPrivate::slotSelectAll()
211 selectAll(Qt::Checked);
214 void CategorySelectWidgetPrivate::slotSelectNone()
216 selectAll(Qt::Unchecked);
219 void CategorySelectWidgetPrivate::slotCheckedItemsChanged()
221 updateTimer->start();
224 void CategorySelectWidgetPrivate::slotCheckedItemsTimer()
226 Q_Q(CategorySelectWidget);
228 bool allOn = true;
229 for (int row = 0; row < itemModel()->rowCount(); ++row) {
230 const QStandardItem *it = itemModel()->item(row);
231 Qt::CheckState rowState = static_cast<Qt::CheckState>(it->data(Qt::CheckStateRole).toInt());
232 if (rowState != Qt::Checked) {
233 allOn = false;
234 break;
238 if (allOn) {
239 checkCombo->setAlwaysShowDefaultText(true);
240 checkCombo->setDefaultText(i18n("(All)"));
241 } else {
242 checkCombo->setAlwaysShowDefaultText(false);
243 checkCombo->setDefaultText(i18n("(None)"));
246 const QStringList checkedList = checkCombo->checkedItems();
247 if (!checkedList.isEmpty()) {
248 checkCombo->setToolTip(i18n("<qt>Category filter: %1", checkedList.join(i18n(", "))));
249 } else {
250 checkCombo->setToolTip(QString());
253 Q_EMIT q->filterChanged(filterTags());
256 QList<Akonadi::Tag::Id> CategorySelectWidgetPrivate::filterTags() const
258 QList<Tag::Id> filter;
259 bool allOn = true;
260 for (int row = 0; row < itemModel()->rowCount(); ++row) {
261 const QStandardItem *it = itemModel()->item(row);
262 Q_ASSERT(it != NULL);
263 if (it->checkState() == Qt::Checked) {
264 Tag::Id id = it->data(FILTER_ROLE).toInt();
265 if (id != 0) {
266 filter.append(id);
268 } else {
269 allOn = false;
273 if (allOn) {
274 filter.clear();
275 filter.append(CategorySelectWidget::FilterAll);
278 //qCDebug(KADDRESSBOOK_LOG) << "filter" << filter;
279 return filter;
282 CategorySelectWidget::CategorySelectWidget(QWidget *parent)
283 : QWidget(parent),
284 d_ptr(new CategorySelectWidgetPrivate(this))
286 Q_D(CategorySelectWidget);
287 d->init();
290 CategorySelectWidget::~CategorySelectWidget()
292 delete d_ptr;
295 QList<Akonadi::Tag::Id> CategorySelectWidget::filterTags() const
297 Q_D(const CategorySelectWidget);
298 return d->filterTags();
301 #include "categoryselectwidget.moc"