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
20 #include "categoryselectwidget.h"
22 #include "kaddressbook_debug.h"
23 #include <KLocalizedString>
24 #include <qtoolbutton.h>
26 #include <qstandarditemmodel.h>
29 #include <AkonadiCore/monitor.h>
30 #include <AkonadiCore/tagmodel.h>
32 #include <Libkdepim/KCheckComboBox>
34 using namespace Akonadi
;
36 static const int FILTER_ROLE
= Qt::UserRole
+ 1;
38 class CategorySelectWidgetPrivate
: public QObject
41 Q_DECLARE_PUBLIC(CategorySelectWidget
)
44 explicit CategorySelectWidgetPrivate(CategorySelectWidget
*parent
);
46 Akonadi::TagModel
*tagModel
;
49 KPIM::KCheckComboBox
*checkCombo
;
52 QStandardItemModel
*itemModel() const;
53 void selectAll(Qt::CheckState state
) const;
54 QList
<Akonadi::Tag::Id
> filterTags() const;
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();
68 CategorySelectWidget
*q_ptr
;
71 CategorySelectWidgetPrivate::CategorySelectWidgetPrivate(CategorySelectWidget
*parent
)
75 updateTimer(Q_NULLPTR
),
76 checkCombo(Q_NULLPTR
),
81 void CategorySelectWidgetPrivate::init()
83 Q_Q(CategorySelectWidget
);
85 QHBoxLayout
*hbox
= new QHBoxLayout(q
);
89 checkCombo
= new KPIM::KCheckComboBox
;
90 checkCombo
->setMinimumWidth(150);
91 checkCombo
->setSqueezeText(true);
92 connect(checkCombo
, &KPIM::KCheckComboBox::checkedItemsChanged
,
93 this, &CategorySelectWidgetPrivate::slotCheckedItemsChanged
);
94 hbox
->addWidget(checkCombo
);
96 Monitor
*monitor
= new Monitor(this);
97 monitor
->setTypeMonitored(Monitor::Tags
);
98 tagModel
= new Akonadi::TagModel(monitor
, this);
100 connect(tagModel
, &QAbstractItemModel::rowsInserted
,
101 this, &CategorySelectWidgetPrivate::slotTagsInserted
);
102 connect(tagModel
, &QAbstractItemModel::rowsRemoved
,
103 this, &CategorySelectWidgetPrivate::slotTagsRemoved
);
104 connect(tagModel
, &QAbstractItemModel::dataChanged
,
105 this, &CategorySelectWidgetPrivate::slotTagsChanged
);
107 updateTimer
= new QTimer(this);
108 updateTimer
->setSingleShot(true);
109 updateTimer
->setInterval(200);
110 connect(updateTimer
, &QTimer::timeout
, this, &CategorySelectWidgetPrivate::slotCheckedItemsTimer
);
112 QToolButton
*but
= new QToolButton(q
);
113 but
->setAutoRaise(true);
114 but
->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
115 but
->setToolTip(i18nc("@action:button", "Reset category filter"));
116 connect(but
, &QToolButton::clicked
, this, &CategorySelectWidgetPrivate::slotSelectAll
);
117 hbox
->addWidget(but
);
119 but
= new QToolButton(q
);
120 but
->setAutoRaise(true);
121 but
->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
122 but
->setToolTip(i18nc("@action:button", "Clear category filter"));
123 connect(but
, &QToolButton::clicked
, this, &CategorySelectWidgetPrivate::slotSelectNone
);
124 hbox
->addWidget(but
);
126 QStandardItem
*item
= new QStandardItem(i18n("(Untagged)"));
127 item
->setCheckState(Qt::Checked
);
128 item
->setData(CategorySelectWidget::FilterUntagged
, FILTER_ROLE
);
129 itemModel()->appendRow(item
);
131 item
= new QStandardItem(i18n("(Groups)"));
132 item
->setCheckState(Qt::Checked
);
133 item
->setData(CategorySelectWidget::FilterGroups
, FILTER_ROLE
);
134 itemModel()->appendRow(item
);
136 rowOffset
= itemModel()->rowCount();
139 QStandardItemModel
*CategorySelectWidgetPrivate::itemModel() const
141 QStandardItemModel
*m
= qobject_cast
<QStandardItemModel
*>(checkCombo
->model());
146 void CategorySelectWidgetPrivate::slotTagsRemoved(const QModelIndex
&parent
, int start
, int end
)
148 itemModel()->removeRows(start
+ rowOffset
, end
+ rowOffset
, parent
);
151 void CategorySelectWidgetPrivate::slotTagsChanged(const QModelIndex
&topLeft
, const QModelIndex
&bottomRight
)
153 for (int row
= topLeft
.row(); row
<= bottomRight
.row(); ++row
) {
154 QStandardItem
*it
= itemModel()->item(row
+ rowOffset
);
155 Q_ASSERT(it
!= NULL
);
157 QModelIndex idx
= tagModel
->index(row
, 0);
158 it
->setText(tagModel
->data(idx
, TagModel::NameRole
).toString());
159 it
->setIcon(tagModel
->data(idx
, Qt::DecorationRole
).value
<QIcon
>());
160 it
->setData(tagModel
->data(idx
, TagModel::IdRole
), FILTER_ROLE
);
164 void CategorySelectWidgetPrivate::slotTagsInserted(const QModelIndex
&parent
, int start
, int end
)
166 for (int row
= start
; row
<= end
; ++row
) {
167 QModelIndex idx
= tagModel
->index(row
, 0, parent
);
169 qCDebug(KADDRESSBOOK_LOG
) << "idx" << idx
<< "=" << tagModel
->data(idx
, Qt::DisplayRole
).toString()
170 << "name" << tagModel
->data(idx
, TagModel::NameRole
).toString()
171 << "tag" << tagModel
->data(idx
, TagModel::TagRole
)
172 << "id" << tagModel
->data(idx
, TagModel::IdRole
).toInt();
174 QStandardItem
*it
= new QStandardItem(tagModel
->data(idx
, TagModel::NameRole
).toString());
175 it
->setIcon(tagModel
->data(idx
, Qt::DecorationRole
).value
<QIcon
>());
176 it
->setData(tagModel
->data(idx
, TagModel::IdRole
), FILTER_ROLE
);
177 it
->setCheckState(Qt::Checked
);
179 // If a tag with a parent arrives from the model, we know that its parent
180 // must already have arrived. So there is no need for a list of pending
181 // tags, as is required in Akonadi::TagModel.
183 // FIXME: not tested (no way to create hierarchial tags at present)
184 if (parent
!= QModelIndex()) {
185 const Tag::Id parentId
= tagModel
->data(idx
, TagModel::IdRole
).value
<Tag::Id
>();
186 QModelIndexList matchList
= itemModel()->match(itemModel()->index(0, 0), FILTER_ROLE
,
188 Qt::MatchExactly
| Qt::MatchRecursive
);
189 if (matchList
.count() == 1) { // found the parent tag
190 QModelIndex parentIndex
= matchList
.at(0);
191 itemModel()->itemFromIndex(parentIndex
)->appendRow(it
);
193 qCWarning(KADDRESSBOOK_LOG
) << "Cannot find parent with ID" << parentId
;
194 itemModel()->insertRow(row
+ rowOffset
, it
);
197 itemModel()->insertRow(row
+ rowOffset
, it
);
202 void CategorySelectWidgetPrivate::selectAll(Qt::CheckState state
) const
204 for (int row
= 0; row
< itemModel()->rowCount(); ++row
) {
205 QStandardItem
*it
= itemModel()->item(row
);
206 it
->setCheckState(state
);
210 void CategorySelectWidgetPrivate::slotSelectAll()
212 selectAll(Qt::Checked
);
215 void CategorySelectWidgetPrivate::slotSelectNone()
217 selectAll(Qt::Unchecked
);
220 void CategorySelectWidgetPrivate::slotCheckedItemsChanged()
222 updateTimer
->start();
225 void CategorySelectWidgetPrivate::slotCheckedItemsTimer()
227 Q_Q(CategorySelectWidget
);
230 for (int row
= 0; row
< itemModel()->rowCount(); ++row
) {
231 const QStandardItem
*it
= itemModel()->item(row
);
232 Qt::CheckState rowState
= static_cast<Qt::CheckState
>(it
->data(Qt::CheckStateRole
).toInt());
233 if (rowState
!= Qt::Checked
) {
240 checkCombo
->setAlwaysShowDefaultText(true);
241 checkCombo
->setDefaultText(i18n("(All)"));
243 checkCombo
->setAlwaysShowDefaultText(false);
244 checkCombo
->setDefaultText(i18n("(None)"));
247 const QStringList checkedList
= checkCombo
->checkedItems();
248 if (!checkedList
.isEmpty()) {
249 checkCombo
->setToolTip(i18n("<qt>Category filter: %1", checkedList
.join(i18n(", "))));
251 checkCombo
->setToolTip(QString());
254 Q_EMIT q
->filterChanged(filterTags());
257 QList
<Akonadi::Tag::Id
> CategorySelectWidgetPrivate::filterTags() const
259 QList
<Tag::Id
> filter
;
261 for (int row
= 0; row
< itemModel()->rowCount(); ++row
) {
262 const QStandardItem
*it
= itemModel()->item(row
);
263 Q_ASSERT(it
!= NULL
);
264 if (it
->checkState() == Qt::Checked
) {
265 Tag::Id id
= it
->data(FILTER_ROLE
).toInt();
276 filter
.append(CategorySelectWidget::FilterAll
);
279 //qCDebug(KADDRESSBOOK_LOG) << "filter" << filter;
283 CategorySelectWidget::CategorySelectWidget(QWidget
*parent
)
285 d_ptr(new CategorySelectWidgetPrivate(this))
287 Q_D(CategorySelectWidget
);
291 CategorySelectWidget::~CategorySelectWidget()
296 QList
<Akonadi::Tag::Id
> CategorySelectWidget::filterTags() const
298 Q_D(const CategorySelectWidget
);
299 return d
->filterTags();
302 #include "categoryselectwidget.moc"