[kde-doc-english] [kdepim] kaddressbook: KAddressBook: Add a category filter
Jonathan Marten
jjm at keelhaul.me.uk
Fri May 2 20:19:37 UTC 2014
Git commit 0a92fb453bad60345291d4e585d6266a41c84dae by Jonathan Marten.
Committed on 02/05/2014 at 20:14.
Pushed by marten into branch 'master'.
KAddressBook: Add a category filter
This is a widget which by default appears to the right of the
quick search bar. Checking or unchecking categories in this list
filters the entries that are shown.
FEATURE:332103
REVIEW:117834
FIXED-IN:4.14
GUI:
M +2 -0 kaddressbook/CMakeLists.txt
A +150 -0 kaddressbook/categoryfilterproxymodel.cpp [License: LGPL (v2+)]
A +86 -0 kaddressbook/categoryfilterproxymodel.h [License: LGPL (v2+)]
A +316 -0 kaddressbook/categoryselectwidget.cpp [License: LGPL (v2+)]
A +87 -0 kaddressbook/categoryselectwidget.h [License: LGPL (v2+)]
M +2 -1 kaddressbook/kaddressbookui.rc
M +24 -4 kaddressbook/mainwidget.cpp
M +5 -0 kaddressbook/mainwidget.h
M +2 -1 kaddressbook/settings/kaddressbook.kcfg
http://commits.kde.org/kdepim/0a92fb453bad60345291d4e585d6266a41c84dae
diff --git a/kaddressbook/CMakeLists.txt b/kaddressbook/CMakeLists.txt
index 8c39833..9feea1c 100644
--- a/kaddressbook/CMakeLists.txt
+++ b/kaddressbook/CMakeLists.txt
@@ -89,6 +89,8 @@ set(kaddressbook_merge_SRCS
set(kaddressbook_LIB_SRCS
startup.cpp
aboutdata.cpp
+ categoryfilterproxymodel.cpp
+ categoryselectwidget.cpp
contactfields.cpp
contactselectiondialog.cpp
contactselectionwidget.cpp
diff --git a/kaddressbook/categoryfilterproxymodel.cpp b/kaddressbook/categoryfilterproxymodel.cpp
new file mode 100644
index 0000000..f006274
--- /dev/null
+++ b/kaddressbook/categoryfilterproxymodel.cpp
@@ -0,0 +1,150 @@
+/*
+ Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library 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 Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include "categoryfilterproxymodel.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <akonadi/entitytreemodel.h>
+#include <akonadi/item.h>
+
+#include <kabc/addressee.h>
+#include <kabc/contactgroup.h>
+
+#include "categoryselectwidget.h"
+
+using namespace Akonadi;
+
+
+class CategoryFilterProxyModelPrivate : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PUBLIC(CategoryFilterProxyModel);
+
+public:
+ CategoryFilterProxyModelPrivate(CategoryFilterProxyModel *parent);
+
+ QList<Tag::Id> filterIdList;
+ bool filterEnabled;
+
+private:
+ CategoryFilterProxyModel *q_ptr;
+};
+
+
+CategoryFilterProxyModelPrivate::CategoryFilterProxyModelPrivate(CategoryFilterProxyModel *parent)
+ : QObject(),
+ filterEnabled(false),
+ q_ptr(parent)
+{
+}
+
+
+CategoryFilterProxyModel::CategoryFilterProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent),
+ d_ptr(new CategoryFilterProxyModelPrivate(this))
+{
+ setDynamicSortFilter(true);
+}
+
+
+CategoryFilterProxyModel::~CategoryFilterProxyModel()
+{
+ delete d_ptr;
+}
+
+
+bool CategoryFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
+{
+ Q_D(const CategoryFilterProxyModel);
+
+ const QModelIndex index = sourceModel()->index(row, 0, parent);
+ const Akonadi::Item item = index.data(EntityTreeModel::ItemRole).value<Akonadi::Item>();
+
+ if (!d->filterEnabled) return true; // filter not enabled
+ if (d->filterIdList.isEmpty()) return false; // nothing accepted
+ // all accepted
+ if (d->filterIdList.first()==CategorySelectWidget::FilterAll) return true;
+
+ //kDebug() << "for row" << row << "item" << item.url() << "filter" << d->filterIdList;
+ if (item.hasPayload<KABC::Addressee>()) {
+ const KABC::Addressee contact = item.payload<KABC::Addressee>();
+
+ const QStringList categories = contact.categories();
+ //kDebug() << "is contact" << contact.assembledName() << "cats" << categories;
+
+ int validCategories = 0;
+ int count = categories.count();
+ for (int i = 0; i<count; ++i) {
+ const QString cat = categories.at(i);
+ if (cat.startsWith(QLatin1String("akonadi:"))) {
+ const int idx = cat.indexOf(QLatin1String("?tag="));
+ if (idx>=0) {
+ ++validCategories;
+ Tag::Id id = cat.mid(idx+5).toInt();
+ if (d->filterIdList.contains(id)) {
+ //kDebug() << "matches category" << cat;
+ return true; // a category matches filter
+ }
+ }
+ }
+ }
+
+ if (validCategories>0) {
+ //kDebug() << "valid item but no match";
+ return false; // categorised but no match
+ } else {
+ //kDebug() << "item with no categories";
+ return d->filterIdList.contains(CategorySelectWidget::FilterUntagged);
+ }
+ }
+ else if (item.hasPayload<KABC::ContactGroup>()) { // a contact group item
+ return d->filterIdList.contains(CategorySelectWidget::FilterGroups);
+ }
+
+ return true; // not a recognised item
+}
+
+
+void CategoryFilterProxyModel::setFilterCategories(const QList<Akonadi::Tag::Id> &idList)
+{
+ Q_D(CategoryFilterProxyModel);
+
+ if (idList!=d->filterIdList) {
+ kDebug() << idList;
+ d->filterIdList = idList;
+ invalidateFilter();
+ }
+}
+
+
+void CategoryFilterProxyModel::setFilterEnabled(bool enable)
+{
+ Q_D(CategoryFilterProxyModel);
+
+ if (enable!=d->filterEnabled) {
+ kDebug() << enable;
+ d->filterEnabled = enable;
+ invalidateFilter();
+ }
+}
+
+
+#include "categoryfilterproxymodel.moc"
diff --git a/kaddressbook/categoryfilterproxymodel.h b/kaddressbook/categoryfilterproxymodel.h
new file mode 100644
index 0000000..f863abf
--- /dev/null
+++ b/kaddressbook/categoryfilterproxymodel.h
@@ -0,0 +1,86 @@
+/*
+ Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library 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 Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#ifndef CATEGORYFILTERPROXYMODEL_H
+#define CATEGORYFILTERPROXYMODEL_H
+
+#include <qsortfilterproxymodel.h>
+
+#include <akonadi/tag.h>
+
+class CategoryFilterProxyModelPrivate;
+
+
+/**
+ * @short A proxy model to filter contacts by categories (tags).
+ *
+ * @since 4.14
+ * @author Jonathan Marten
+ **/
+
+class CategoryFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(CategoryFilterProxyModel);
+
+public:
+ /**
+ * Constructor.
+ *
+ * @param parent The parent object
+ **/
+ explicit CategoryFilterProxyModel(QObject *parent = 0);
+
+ /**
+ * Destructor.
+ **/
+ virtual ~CategoryFilterProxyModel();
+
+public slots:
+ /**
+ * Set the categories to be accepted by the filter.
+ *
+ * @param idList A list of @c Akonadi::Tag::Id's of the categories
+ * which are to be accepted by the filter.
+ * @see CategorySelectModel::filterChanged
+ **/
+ void setFilterCategories(const QList<Akonadi::Tag::Id> &idList);
+
+ /**
+ * Enable or disable the filter.
+ *
+ * @param enable If @c true, enable the filter to accept only those categories
+ * set by @c setFilterCategories(). If @false, disable the filter so that all
+ * entries are accepted.
+ *
+ * The default state is that the filter is disabled.
+ **/
+ void setFilterEnabled(bool enable);
+
+protected:
+ /**
+ * @reimp
+ **/
+ virtual bool filterAcceptsRow(int row, const QModelIndex &parent) const;
+
+private:
+ CategoryFilterProxyModelPrivate * const d_ptr;
+};
+
+#endif // CATEGORYFILTERPROXYMODEL_H
diff --git a/kaddressbook/categoryselectwidget.cpp b/kaddressbook/categoryselectwidget.cpp
new file mode 100644
index 0000000..cca1028
--- /dev/null
+++ b/kaddressbook/categoryselectwidget.cpp
@@ -0,0 +1,316 @@
+/*
+ Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library 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 Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include "categoryselectwidget.h"
+
+#include <kicon.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kdialog.h>
+
+#include <qtoolbutton.h>
+#include <qlayout.h>
+#include <qstandarditemmodel.h>
+#include <qtimer.h>
+
+#include <akonadi/monitor.h>
+#include <akonadi/tagmodel.h>
+
+#include <widgets/kcheckcombobox.h>
+
+using namespace Akonadi;
+
+
+static const int FILTER_ROLE = Qt::UserRole+1;
+
+
+class CategorySelectWidgetPrivate : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PUBLIC(CategorySelectWidget);
+
+public:
+ CategorySelectWidgetPrivate(CategorySelectWidget *parent);
+
+ Akonadi::TagModel *tagModel;
+ int rowOffset;
+ QTimer *updateTimer;
+ KPIM::KCheckComboBox *checkCombo;
+
+ void init();
+ QStandardItemModel *itemModel() const;
+ void selectAll(Qt::CheckState state) const;
+ QList<Akonadi::Tag::Id> filterTags() const;
+
+public slots:
+ void slotSelectAll();
+ void slotSelectNone();
+
+ void slotTagsInserted(const QModelIndex &parent, int start, int end);
+ void slotTagsRemoved(const QModelIndex &parent, int start, int end);
+ void slotTagsChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+
+ void slotCheckedItemsChanged();
+ void slotCheckedItemsTimer();
+
+private:
+ CategorySelectWidget *q_ptr;
+};
+
+
+CategorySelectWidgetPrivate::CategorySelectWidgetPrivate(CategorySelectWidget *parent)
+ : QObject(),
+ q_ptr(parent)
+{
+}
+
+
+void CategorySelectWidgetPrivate::init()
+{
+ Q_Q(CategorySelectWidget);
+
+ QHBoxLayout *hbox = new QHBoxLayout(q);
+ hbox->setSpacing(0);
+
+ checkCombo = new KPIM::KCheckComboBox;
+ checkCombo->setSqueezeText(true);
+ connect(checkCombo, SIGNAL(checkedItemsChanged(QStringList)),
+ SLOT(slotCheckedItemsChanged()));
+ hbox->addWidget(checkCombo);
+
+ Monitor *monitor = new Monitor(this);
+ monitor->setTypeMonitored(Monitor::Tags);
+ tagModel = new Akonadi::TagModel(monitor, this);
+
+ connect(tagModel, SIGNAL(rowsInserted(const QModelIndex &,int,int)),
+ SLOT(slotTagsInserted(const QModelIndex &,int,int)));
+ connect(tagModel, SIGNAL(rowsRemoved(const QModelIndex &,int,int)),
+ SLOT(slotTagsRemoved(const QModelIndex &,int,int)));
+ connect(tagModel, SIGNAL(dataChanged(const QModelIndex &,const QModelIndex &)),
+ SLOT(slotTagsChanged(const QModelIndex &,const QModelIndex &)));
+
+ updateTimer = new QTimer(this);
+ updateTimer->setSingleShot(true);
+ updateTimer->setInterval(200);
+ connect(updateTimer, SIGNAL(timeout()), SLOT(slotCheckedItemsTimer()));
+
+ hbox->addSpacing(KDialog::spacingHint());
+
+ QToolButton *but = new QToolButton(q);
+ but ->setAutoRaise(true);
+ but->setIcon(KIcon(QLatin1String("edit-undo")));
+ but->setToolTip(i18nc("@action:button", "Reset category filter"));
+ connect(but, SIGNAL(clicked(bool)), SLOT(slotSelectAll()));
+ hbox->addWidget(but);
+
+ but = new QToolButton(q);
+ but->setAutoRaise(true);
+ but->setIcon(KIcon(QLatin1String("edit-clear")));
+ but->setToolTip(i18nc("@action:button", "Clear category filter"));
+ connect(but, SIGNAL(clicked(bool)), SLOT(slotSelectNone()));
+ hbox->addWidget(but);
+
+ QStandardItem *item = new QStandardItem(i18n("(Untagged)"));
+ item->setCheckState(Qt::Checked);
+ item->setData(CategorySelectWidget::FilterUntagged, FILTER_ROLE);
+ itemModel()->appendRow(item);
+
+ item = new QStandardItem(i18n("(Groups)"));
+ item->setCheckState(Qt::Checked);
+ item->setData(CategorySelectWidget::FilterGroups, FILTER_ROLE);
+ itemModel()->appendRow(item);
+
+ rowOffset = itemModel()->rowCount();
+}
+
+
+QStandardItemModel *CategorySelectWidgetPrivate::itemModel() const
+{
+ QStandardItemModel *m = qobject_cast<QStandardItemModel *>(checkCombo->model());
+ Q_ASSERT(m!=NULL);
+ return m;
+}
+
+
+void CategorySelectWidgetPrivate::slotTagsRemoved(const QModelIndex &parent, int start, int end)
+{
+ itemModel()->removeRows(start+rowOffset, end+rowOffset, parent);
+}
+
+
+void CategorySelectWidgetPrivate::slotTagsChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ for (int row = topLeft.row(); row<=bottomRight.row(); ++row) {
+ QStandardItem *it = itemModel()->item(row+rowOffset);
+ Q_ASSERT(it!=NULL);
+
+ QModelIndex idx = tagModel->index(row, 0);
+ it->setText(tagModel->data(idx, TagModel::NameRole).toString());
+ it->setIcon(tagModel->data(idx, Qt::DecorationRole).value<QIcon>());
+ it->setData(tagModel->data(idx, TagModel::IdRole), FILTER_ROLE);
+ }
+}
+
+
+void CategorySelectWidgetPrivate::slotTagsInserted(const QModelIndex &parent, int start, int end)
+{
+ for (int row = start; row<=end; ++row) {
+ QModelIndex idx = tagModel->index(row, 0, parent);
+ kDebug() << "idx" << idx << "=" << tagModel->data(idx, Qt::DisplayRole).toString()
+ << "name" << tagModel->data(idx, TagModel::NameRole).toString()
+ << "tag" << tagModel->data(idx, TagModel::TagRole)
+ << "id" << tagModel->data(idx, TagModel::IdRole).toInt();
+
+ QStandardItem *it = new QStandardItem(tagModel->data(idx, TagModel::NameRole).toString());
+ it->setIcon(tagModel->data(idx, Qt::DecorationRole).value<QIcon>());
+ it->setData(tagModel->data(idx, TagModel::IdRole), FILTER_ROLE);
+ it->setCheckState(Qt::Checked);
+
+ // If a tag with a parent arrives from the model, we know that its parent
+ // must already have arrived. So there is no need for a list of pending
+ // tags, as is required in Akonadi::TagModel.
+ //
+ // FIXME: not tested (no way to create hierarchial tags at present)
+ if (parent!=QModelIndex()) {
+ const Tag::Id parentId = tagModel->data(idx, TagModel::IdRole).value<Tag::Id>();
+ QModelIndexList matchList = itemModel()->match(itemModel()->index(0, 0), FILTER_ROLE,
+ parentId, 1,
+ Qt::MatchExactly|Qt::MatchRecursive);
+ if (matchList.count()==1) { // found the parent tag
+ QModelIndex parentIndex = matchList.first();
+ itemModel()->itemFromIndex(parentIndex)->appendRow(it);
+ } else {
+ kWarning() << "Cannot find parent with ID" << parentId;
+ itemModel()->insertRow(row+rowOffset, it);
+ }
+ } else {
+ itemModel()->insertRow(row+rowOffset, it);
+ }
+ }
+}
+
+
+void CategorySelectWidgetPrivate::selectAll(Qt::CheckState state) const
+{
+ for (int row = 0; row<itemModel()->rowCount(); ++row) {
+ QStandardItem *it = itemModel()->item(row);
+ it->setCheckState(state);
+ }
+}
+
+
+void CategorySelectWidgetPrivate::slotSelectAll()
+{
+ selectAll(Qt::Checked);
+}
+
+
+void CategorySelectWidgetPrivate::slotSelectNone()
+{
+ selectAll(Qt::Unchecked);
+}
+
+
+void CategorySelectWidgetPrivate::slotCheckedItemsChanged()
+{
+ updateTimer->start();
+}
+
+
+void CategorySelectWidgetPrivate::slotCheckedItemsTimer()
+{
+ Q_Q(CategorySelectWidget);
+
+ bool allOn = true;
+ for (int row = 0; row<itemModel()->rowCount(); ++row) {
+ const QStandardItem *it = itemModel()->item(row);
+ Qt::CheckState rowState = static_cast<Qt::CheckState>(it->data(Qt::CheckStateRole).toInt());
+ if (rowState!=Qt::Checked) {
+ allOn = false;
+ break;
+ }
+ }
+
+ if (allOn) {
+ checkCombo->setAlwaysShowDefaultText(true);
+ checkCombo->setDefaultText(i18n("(All)"));
+ } else {
+ checkCombo->setAlwaysShowDefaultText(false);
+ checkCombo->setDefaultText(i18n("(None)"));
+ }
+
+ const QStringList checkedList = checkCombo->checkedItems();
+ if (!checkedList.isEmpty()) {
+ checkCombo->setToolTip(i18n("<qt>Category filter: %1", checkedList.join(i18n(", "))));
+ } else {
+ checkCombo->setToolTip(QString());
+ }
+
+ emit q->filterChanged(filterTags());
+}
+
+
+QList<Akonadi::Tag::Id> CategorySelectWidgetPrivate::filterTags() const
+{
+ QList<Tag::Id> filter;
+ bool allOn = true;
+ for (int row = 0; row<itemModel()->rowCount(); ++row) {
+ const QStandardItem *it = itemModel()->item(row);
+ Q_ASSERT(it!=NULL);
+ if (it->checkState()==Qt::Checked) {
+ Tag::Id id = it->data(FILTER_ROLE).toInt();
+ if (id!=0) filter.append(id);
+ } else {
+ allOn = false;
+ }
+ }
+
+ if (allOn) {
+ filter.clear();
+ filter.append(CategorySelectWidget::FilterAll);
+ }
+
+ kDebug() << "filter" << filter;
+ return filter;
+}
+
+
+CategorySelectWidget::CategorySelectWidget(QWidget *parent)
+ : QWidget(parent),
+ d_ptr(new CategorySelectWidgetPrivate(this))
+{
+ Q_D(CategorySelectWidget);
+ d->init();
+}
+
+
+CategorySelectWidget::~CategorySelectWidget()
+{
+ delete d_ptr;
+}
+
+
+QList<Akonadi::Tag::Id> CategorySelectWidget::filterTags() const
+{
+ Q_D(const CategorySelectWidget);
+ return d->filterTags();
+}
+
+
+#include "categoryselectwidget.moc"
diff --git a/kaddressbook/categoryselectwidget.h b/kaddressbook/categoryselectwidget.h
new file mode 100644
index 0000000..eff9fc3
--- /dev/null
+++ b/kaddressbook/categoryselectwidget.h
@@ -0,0 +1,87 @@
+/*
+ Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library 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 Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#ifndef CATEGORYSELECTWIDGET_H
+#define CATEGORYSELECTWIDGET_H
+
+#include <qwidget.h>
+#include <akonadi/tag.h>
+
+class CategorySelectWidgetPrivate;
+
+/**
+ * @short A widget to specify a category (tag) filter.
+ *
+ * @since 4.14
+ * @author Jonathan Marten
+ **/
+
+class CategorySelectWidget : public QWidget
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(CategorySelectWidget);
+
+public:
+ /**
+ * Constructor.
+ *
+ * @param parent The parent widget
+ **/
+ explicit CategorySelectWidget(QWidget *parent = 0);
+
+ /**
+ * Destructor.
+ **/
+ virtual ~CategorySelectWidget();
+
+ /**
+ * Special @c Akonadi::Tag::Id values for filtering.
+ **/
+ enum FilterTag
+ {
+ FilterAll = -2, /**< All items */
+ FilterUntagged = -3, /**< Untagged items */
+ FilterGroups = -4 /**< Contact groups */
+ };
+
+ /**
+ * Get the current tag filter list.
+ *
+ * @return The filter list, as would be sent by @c filterChanged()
+ *
+ * @see filterChanged
+ **/
+ QList<Akonadi::Tag::Id> filterTags() const;
+
+signals:
+ /**
+ * The tag filter selection has changed.
+ *
+ * @param idList A list of @c Akonadi::Tag::Id's of the tags which
+ * are to be included in the filter.
+ *
+ * @see CategorySelectModel::filterChanged
+ **/
+ void filterChanged(const QList<Akonadi::Tag::Id> &idList);
+
+private:
+ CategorySelectWidgetPrivate * const d_ptr;
+};
+
+#endif // CATEGORYSELECTWIDGET_H
diff --git a/kaddressbook/kaddressbookui.rc b/kaddressbook/kaddressbookui.rc
index 6f4a774..623b2b8 100644
--- a/kaddressbook/kaddressbookui.rc
+++ b/kaddressbook/kaddressbookui.rc
@@ -1,5 +1,5 @@
<!DOCTYPE kpartgui>
-<kpartgui name="kaddressbook" version="18">
+<kpartgui name="kaddressbook" version="19">
<MenuBar>
<Menu name="file"><text>&File</text>
@@ -98,6 +98,7 @@
<Action name="akonadi_contact_create"/>
<Action name="akonadi_contact_group_create"/>
<Action name="quick_search"/>
+ <Action name="category_filter"/>
</ToolBar>
</kpartgui>
diff --git a/kaddressbook/mainwidget.cpp b/kaddressbook/mainwidget.cpp
index c2555fe..8983649 100644
--- a/kaddressbook/mainwidget.cpp
+++ b/kaddressbook/mainwidget.cpp
@@ -29,7 +29,8 @@
#include "xxportmanager.h"
#include "utils.h"
#include "kaddressbookadaptor.h"
-
+#include "categoryselectwidget.h"
+#include "categoryfilterproxymodel.h"
#include "kaddressbookgrantlee/formatter/grantleecontactformatter.h"
#include "kaddressbookgrantlee/formatter/grantleecontactgroupformatter.h"
@@ -154,6 +155,9 @@ MainWidget::MainWidget( KXMLGUIClient *guiClient, QWidget *parent )
* mContactsFilterModel
* ^
* |
+ * mCategorySelectWidget --> mCategoryFilterModel
+ * ^
+ * |
* mItemTree
* ^
* |
@@ -184,6 +188,8 @@ MainWidget::MainWidget( KXMLGUIClient *guiClient, QWidget *parent )
* selectionProxyModel: Filters out all collections and items that are no children
* of the collections currently selected in selectionModel
* mItemTree: Filters out all collections
+ * mCategorySelectWidget: Selects a list of categories for filtering
+ * mCategoryFilterModel: Filters the contacts by the selected categories
* mContactsFilterModel: Filters the contacts by the content of mQuickSearchWidget
* mItemView: Shows the items (contacts and contact groups) in a view
*
@@ -226,8 +232,17 @@ MainWidget::MainWidget( KXMLGUIClient *guiClient, QWidget *parent )
mItemTree->addMimeTypeExclusionFilter( Akonadi::Collection::mimeType() );
mItemTree->setHeaderGroup( Akonadi::EntityTreeModel::ItemListHeaders );
+ mCategoryFilterModel = new CategoryFilterProxyModel( this );
+ mCategoryFilterModel->setSourceModel( mItemTree );
+ mCategoryFilterModel->setFilterCategories( mCategorySelectWidget->filterTags() );
+ mCategoryFilterModel->setFilterEnabled( true );
+
+ connect(mCategorySelectWidget, SIGNAL(filterChanged(const QList<Akonadi::Tag::Id> &)),
+ mCategoryFilterModel, SLOT(setFilterCategories(const QList<Akonadi::Tag::Id> &)));
+
mContactsFilterModel = new Akonadi::ContactsFilterProxyModel( this );
- mContactsFilterModel->setSourceModel( mItemTree );
+ mContactsFilterModel->setSourceModel( mCategoryFilterModel );
+
connect( mQuickSearchWidget, SIGNAL(filterStringChanged(QString)),
mContactsFilterModel, SLOT(setFilterString(QString)) );
connect( mQuickSearchWidget, SIGNAL(filterStringChanged(QString)),
@@ -519,6 +534,9 @@ void MainWidget::setupGui()
// the quick search widget which is embedded in the toolbar action
mQuickSearchWidget = new QuickSearchWidget;
+ // the category filter widget which is embedded in the toolbar action
+ mCategorySelectWidget = new CategorySelectWidget;
+
// setup the default actions
Akonadi::ContactDefaultActions *actions = new Akonadi::ContactDefaultActions( this );
actions->connectToView( mContactDetails );
@@ -560,6 +578,10 @@ void MainWidget::setupActions( KActionCollection *collection )
action->setText( i18n( "Quick search" ) );
action->setDefaultWidget( mQuickSearchWidget );
+ action = collection->addAction( QLatin1String("category_filter") );
+ action->setText( i18n( "Category filter" ) );
+ action->setDefaultWidget( mCategorySelectWidget );
+
action = collection->addAction( QLatin1String("select_all") );
action->setText( i18n( "Select All" ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_A ) );
@@ -955,5 +977,3 @@ void MainWidget::slotCheckNewCalendar( const QModelIndex &parent, int begin, int
}
}
}
-
-
diff --git a/kaddressbook/mainwidget.h b/kaddressbook/mainwidget.h
index 0075edf..69ccb6d 100644
--- a/kaddressbook/mainwidget.h
+++ b/kaddressbook/mainwidget.h
@@ -59,6 +59,8 @@ class QStackedWidget;
class QuickSearchWidget;
class XXPortManager;
class QActionGroup;
+class CategorySelectWidget;
+class CategoryFilterProxyModel;
class KADDRESSBOOK_EXPORT MainWidget : public QWidget
{
@@ -121,10 +123,13 @@ private:
Akonadi::EntityMimeTypeFilterModel *mItemTree;
Akonadi::EntityMimeTypeFilterModel *mAllContactsModel;
Akonadi::ContactsFilterProxyModel *mContactsFilterModel;
+ CategoryFilterProxyModel *mCategoryFilterModel;
QuickSearchWidget *mQuickSearchWidget;
Akonadi::EntityTreeView *mCollectionView;
Akonadi::EntityTreeView *mItemView;
+ CategorySelectWidget *mCategorySelectWidget;
+
QWidget *mDetailsPane;
QStackedWidget *mDetailsViewStack;
ContactSwitcher *mContactSwitcher;
diff --git a/kaddressbook/settings/kaddressbook.kcfg b/kaddressbook/settings/kaddressbook.kcfg
index f3a3e38..cc06279 100644
--- a/kaddressbook/settings/kaddressbook.kcfg
+++ b/kaddressbook/settings/kaddressbook.kcfg
@@ -55,7 +55,8 @@
<label>Viewing mode</label>
<tooltip>Viewing mode (number of columns)</tooltip>
<whatsthis>Choose the layout for the main view</whatsthis>
- </entry>
+ </entry>
+
</group>
</kcfg>
More information about the kde-doc-english
mailing list