Skip to content

Instantly share code, notes, and snippets.

@kovrov
Created December 3, 2010 02:06
Show Gist options
  • Save kovrov/726464 to your computer and use it in GitHub Desktop.
Save kovrov/726464 to your computer and use it in GitHub Desktop.
QMF FolderListModel
// Qt
#include <QMap>
#include <QtAlgorithms>
// QMF
#include <qmfclient/qmailstore.h> // QMailStore
// project
#include "folderlistmodel.h"
#define CONNECT(a,b,c,d) if (!QObject::connect(a,b,c,d)) { Q_ASSERT (false); }
struct TreeItem
{
TreeItem(const QMailFolderId &folder_id, const QString &folder_name, TreeItem *parent_ptr)
: parent (parent_ptr),
id (folder_id),
name (folder_name)
{}
~TreeItem()
{
qDeleteAll(children);
}
QList<TreeItem*> children;
TreeItem *parent;
QMailFolderId id;
QString name;
};
FolderListModel::FolderListModel(QObject *parent)
: QAbstractItemModel(parent),
mRootItem (NULL)
{
QMailStore *store = QMailStore::instance();
CONNECT (store, SIGNAL(accountsRemoved(QMailAccountIdList)),
this, SLOT(onAccountsRemoved(QMailAccountIdList)));
CONNECT (store, SIGNAL(foldersAdded(QMailFolderIdList)),
this, SLOT(onFoldersAdded(QMailFolderIdList)));
CONNECT (store, SIGNAL(foldersUpdated(QMailFolderIdList)),
this, SLOT(onFoldersUpdated(QMailFolderIdList)));
CONNECT (store, SIGNAL(foldersRemoved(QMailFolderIdList)),
this, SLOT(onFoldersRemoved(QMailFolderIdList)));
}
FolderListModel::~FolderListModel()
{
delete mRootItem;
}
static
void recursive_setup(const QMailAccountId &account_id, TreeItem *parent)
{
QMailFolderKey key
= QMailFolderKey(QMailFolderKey::parentAccountId(account_id))
& QMailFolderKey(QMailFolderKey::parentFolderId(parent->id))
& QMailFolderKey(QMailFolderKey::status(QMailFolder::NonMail,
QMailDataComparator::Excludes));
foreach (const QMailFolderId &id, QMailStore::instance()->queryFolders(key)) {
const QMailFolder folder(id);
TreeItem *item = new TreeItem(folder.id(), folder.displayName(), parent);
parent->children.append(item);
recursive_setup(account_id, item);
}
}
void FolderListModel::setAccountId(const QMailAccountId &id)
{
if (mRootItem && QMailFolder(mRootItem->id).parentAccountId() == id)
return;
QMailAccount account(id);
if (!account.id().isValid()) {
if (mRootItem) {
delete mRootItem;
mRootItem = NULL;
emit layoutChanged();
}
return;
}
emit layoutAboutToBeChanged();
if (mRootItem)
delete mRootItem;
mRootItem = new TreeItem(QMailFolderId(), account.name(), NULL);
recursive_setup(id, mRootItem);
emit layoutChanged();
}
QMailFolderId FolderListModel::idFromIndex(const QModelIndex &index) const
{
if (!index.isValid())
return QMailFolderId();
return static_cast<TreeItem*>(index.internalPointer())->id;
}
int FolderListModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 0;
if (NULL == mRootItem)
return 0;
TreeItem *parent_item = parent.isValid()
? static_cast<TreeItem*>(parent.internalPointer())
: mRootItem;
return parent_item->children.count();
}
int FolderListModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED (parent);
return 1;
}
QVariant FolderListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
return static_cast<TreeItem*>(index.internalPointer())->name;
}
Qt::ItemFlags FolderListModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QModelIndex FolderListModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TreeItem *parent_item = parent.isValid()
? static_cast<TreeItem*>(parent.internalPointer())
: mRootItem;
Q_ASSERT (parent_item);
if (TreeItem *tree_item = parent_item->children.value(row))
return createIndex(row, column, tree_item);
return QModelIndex();
}
QModelIndex FolderListModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *parent_item = static_cast<TreeItem*>(index.internalPointer())->parent;
if (parent_item == mRootItem)
return QModelIndex();
int row = parent_item->parent
? parent_item->parent->children.indexOf(parent_item)
: 0;
return createIndex(row, 0, parent_item);
}
void FolderListModel::onAccountsRemoved(const QMailAccountIdList &list)
{
if (NULL == mRootItem)
return;
if (list.indexOf(QMailFolder(mRootItem->id).parentAccountId()) == -1)
return;
delete mRootItem;
mRootItem = NULL;
emit layoutChanged();
}
static
TreeItem* find_node(TreeItem* tree_item, const QMailFolderId &id)
{
Q_ASSERT (NULL != tree_item);
if (tree_item->id == id)
return tree_item;
foreach (TreeItem *child_item, tree_item->children) {
if (TreeItem *res = find_node(child_item, id))
return res;
}
return NULL;
}
void FolderListModel::onFoldersAdded(const QMailFolderIdList &list)
{
const QMailAccountId &folder_id = QMailFolder(mRootItem->id).parentAccountId();
QMap<TreeItem*, QList<TreeItem*> > new_folders;
foreach (const QMailFolderId &id, list) {
const QMailFolder folder(id);
TreeItem *parent_item = find_node(mRootItem, folder.parentFolderId());
if (NULL == parent_item)
continue;
new_folders[parent_item] << new TreeItem(folder.id(), folder.displayName(), parent_item);
}
foreach (TreeItem *parent_item, new_folders.keys()) {
const QModelIndex &parent_index = (parent_item->parent)
? createIndex(parent_item->parent->children.indexOf(parent_item), 0, parent_item)
: QModelIndex();
int first = parent_item->children.count();
int last = first + new_folders[parent_item].count();
beginInsertRows(parent_index, first, last);
foreach (TreeItem *item, new_folders[parent_item]) {
Q_ASSERT (item->parent == parent_item);
item->parent->children.append(item);
recursive_setup(folder_id, item);
}
endInsertRows();
}
}
static
void find_all_in(const QMailFolderIdList &list, TreeItem *parent, QList<TreeItem*> *res)
{
if (list.indexOf(parent->id) != -1)
res->append(parent);
foreach (TreeItem *item, parent->children)
find_all_in(list, item, res);
}
void FolderListModel::onFoldersUpdated(const QMailFolderIdList &list)
{
QList<TreeItem*> found;
find_all_in(list, mRootItem, &found);
foreach (TreeItem *item, found) {
const QMailFolder folder(item->id);
if (folder.displayName() != item->name) {
item->name = folder.displayName();
const QModelIndex &index = (item->parent)
? createIndex(item->parent->children.indexOf(item), 0, item)
: QModelIndex();
emit dataChanged(index, index);
}
}
}
static
void find_roots_in(const QMailFolderIdList &list, TreeItem *parent, QList<TreeItem*> *res)
{
if (list.indexOf(parent->id) != -1) {
res->append(parent);
return;
}
foreach (TreeItem *item, parent->children)
find_roots_in(list, item, res);
}
void FolderListModel::onFoldersRemoved(const QMailFolderIdList &list)
{
if (NULL == mRootItem)
return;
if (list.indexOf(mRootItem->id) != -1) {
delete mRootItem;
mRootItem = NULL;
// TODO: emit rowsRemoved instead of
emit layoutChanged();
return;
}
QList<TreeItem*> found;
find_roots_in(list, mRootItem, &found);
if (found.isEmpty())
return;
QMap<TreeItem*, QList<TreeItem*> > folders;
foreach (TreeItem* item, found) {
folders[item->parent] << item;
}
foreach (TreeItem *parent_item, folders.keys()) {
const QModelIndex &parent_index = (parent_item->parent)
? createIndex(parent_item->parent->children.indexOf(parent_item), 0, parent_item)
: QModelIndex();
QList<int> indices;
foreach (TreeItem *item, folders[parent_item]) {
Q_ASSERT (item->parent == parent_item);
indices << parent_item->children.indexOf(item);
}
qSort(indices);
int begin = indices.takeLast();
int end = begin;
do {
if (!indices.isEmpty() && begin - 1 == indices.last()) {
begin = indices.takeLast();
if (!indices.isEmpty())
continue;
}
beginRemoveRows(parent_index, begin, end);
auto it = parent_item->children.begin();
parent_item->children.erase(it + begin, it + end + 1);
endRemoveRows();
if (!indices.isEmpty()) {
begin = indices.takeLast();
end = begin;
}
}
while (!indices.isEmpty());
}
qDeleteAll(found);
}
#ifndef FOLDERLISTMODEL_H
#define FOLDERLISTMODEL_H
#include <QAbstractItemModel>
#include <qmfclient/qmailid.h>
class FolderListModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit FolderListModel(QObject *parent = 0);
virtual ~FolderListModel();
void setAccountId(const QMailAccountId &id);
QMailFolderId idFromIndex(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
private slots:
void onAccountsRemoved(const QMailAccountIdList &);
void onFoldersAdded(const QMailFolderIdList &);
void onFoldersUpdated(const QMailFolderIdList &);
void onFoldersRemoved(const QMailFolderIdList &);
private:
struct TreeItem *mRootItem;
};
#endif // FOLDERLISTMODEL_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment