/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: caitemmodel.cpp
*
*/
#include <HbIcon>
#include "caitemmodel.h"
#include "caitemmodel_p.h"
#include "canotifier.h"
#include "canotifierfilter.h"
#include "caclienttest_global.h"
// Constants
const QSize defaultIconSize(30, 30);
// ======== MEMBER FUNCTIONS ========
/*!
* \class CaItemModel
*
* \brief This class provides model containing CaEntry class objects.
*
* To create instance of CaItemModel object, you need to pass CaQuery
* object in constructor. CaQuery should describe items that you want
* to have in a model.
*
* \example
* \code
*
* CaQuery query;
* query.setFlagsOn(VisibleEntryFlag);
* query.setParentId(collectionId);
* CaItemModel* model = new CaItemModel(query, this);
*
* \endcode
*/
/*!
Constructor.
\param query query describing entries that should be present in a model.
\param parent parent of a model
*/
CaItemModel::CaItemModel(const CaQuery &query, QObject *parent) :
QAbstractItemModel(parent), m_d(new CaItemModelPrivate(query, this))
{
}
/*!
Destructor
*/
CaItemModel::~CaItemModel()
{
delete m_d;
}
/*!
Returns number of columns
\param parent not used
\retval 1
\code
...
// to get number of columns in a model
int columns = model->columnCount();
...
\endcode
*/
int CaItemModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
//model keeps entries in a column 0 and a parent entry in a column 1
//parent entry is treated as an invisible root item,
//so column count is always 1
return 1;
}
/*!
Returns number of rows
\param parent not used
\retval number of rows
\code
...
// to get number of rows in a model
int rows = model->rowCount();
...
\endcode
*/
int CaItemModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_d->rowCount();
}
/*!
Returns QModelIndex of an item
\param row row in which an item is placed
\param column not used
\param parent not used
\retval index of item in model
\code
...
// to get model index of an item
QModelIndex modelIndex = model->index(5);
...
\endcode
*/
QModelIndex CaItemModel::index(int row, int column,
const QModelIndex &parent) const
{
Q_UNUSED(column);
Q_UNUSED(parent);
return m_d->index(row);
}
/*!
Returns parent item
\param child index (ignored)
\retval parent index. in case of CaItemModel it is always QModelIndex()
\code
...
// to get model index of a parent of an item
QModelIndex parentModelIndex = model->parent(childModelIndex);
...
\endcode
*/
QModelIndex CaItemModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index);
return QModelIndex();
}
/*!
Returns root item model index
\retval root item model index
\code
...
// to get model index of a root item
QModelIndex rootIndex = model->root();
...
\endcode
*/
QModelIndex CaItemModel::root() const
{
return m_d->root();
}
/*!
Returns appropiate model's data
\param index model index
\param role which data role to return
\retval models data
\code
...
// to get data for model item
QVariant = model->data(itemModelIndex, Qt::DisplayRole);
...
\endcode
*/
QVariant CaItemModel::data(const QModelIndex &index, int role) const
{
return m_d->data(index, role);
}
/*!
Disables or enables auto-update feature of the model
\param autoUpdate true to enable autoupdate, false to disable
\code
...
// to enable model auto update
model->setAutoUpdate(true);
...
\endcode
*/
void CaItemModel::setAutoUpdate(bool autoUpdate)
{
m_d->setAutoUpdate(autoUpdate);
}
/*!
Disables or enables secondline feature of the model
\param secondLine enable or disable second line
\code
...
// to enable model second line visibility
model->setSecondLineVisibility(true);
...
\endcode
*/
void CaItemModel::setSecondLineVisibility(bool secondLineVisible)
{
m_d->setSecondLineVisibility(secondLineVisible);
}
/*!
Gets second line visibility attribute
\retrun second line visibility attribute
\code
...
// to check second line visibility attribute
bool visibility = model->secondLineVisibility();
...
\endcode
*/
bool CaItemModel::secondLineVisibility() const
{
return m_d->secondLineVisibility();
}
/*!
Returns auto update status
\retval true if autoupdate is on, false if not
\code
...
// to check auto update attribute
bool autoUpdate = model->isAutoUpdate();
...
\endcode
*/
bool CaItemModel::isAutoUpdate() const
{
return m_d->notifierExists();
}
/*!
Method for setting sorting order on model
\param sortAttribute sort attribute (by name, timestamp, ect...)
\param sortOrder sort order (ascending, descending)
\code
...
// to set sort order in a model
model->setSort(NameSortAttribute, Qt::Ascending);
...
\endcode
*/
void CaItemModel::setSort(SortAttribute sortAttribute,
Qt::SortOrder sortOrder)
{
m_d->setSort(sortAttribute, sortOrder);
}
/*!
Method for setting icon size
\param size icon size to display
\code
...
// to set an icon size in a model
QSize iconSize(50,50);
model->setIconSize(iconSize);
...
\endcode
*/
void CaItemModel::setIconSize(const QSize &size)
{
m_d->setIconSize(size);
}
/*!
Method for getting icon size
\param size icon size to display
*/
QSize CaItemModel::getIconSize() const
{
return m_d->getIconSize();
}
/*!
Updates model with fresh entries
\code
...
// to refresh a model
model->updateModel();
...
\endcode
*/
void CaItemModel::updateModel()
{
m_d->updateModel();
}
/*!
Sets parent and fetch children items from a storage
\param parentId id of a collection
\code
...
// to set a new parent id
int newParentId = 10;
model->setParentId(newParentId);
...
\endcode
*/
void CaItemModel::setParentId(int parentId)
{
m_d->setParentId(parentId);
}
/*!
Sets flags to mQuery which should be enabled.
This method does not update current model - only mQuery member.
Now to do this setParentId have to be invoke.
\param onFlags flags.
\code
...
// to set a flags enabled
model->setFlagsOn(RemovableEntryFlag);
...
\endcode
*/
void CaItemModel::setFlagsOn(const EntryFlags &onFlags)
{
m_d->setFlagsOn(onFlags);
}
/*!
Sets flags to mQuery which should be disabled.
This method does not update current model - only mQuery member.
Now to do this setParentId have to be invoke.
\param offFlags flags.
\code
...
// to set a flags disabled
model->setFlagsOff(RemovableEntryFlag);
...
\endcode
*/
void CaItemModel::setFlagsOff(const EntryFlags &offFlags)
{
m_d->setFlagsOff(offFlags);
}
/*!
Returns an entry from model
\param index of entry in model
\retval pointer to an entry
\code
...
// to get an entry from a model
CaEntry* entry = model->entry(entryModelIndex);
...
delete entry;
\endcode
*/
QSharedPointer<CaEntry> CaItemModel::entry(const QModelIndex &index) const
{
return m_d->entry(index);
}
/*!
Constructor
\param query needed to create model
\param pointer to public implementation object connected
*/
CaItemModelPrivate::CaItemModelPrivate(const CaQuery &query,
CaItemModel *itemModelPublic) :
QObject(), m_q(itemModelPublic), mParentEntry(), mQuery(query),
mService(CaService::instance()), mEntries(mService), mNotifier(NULL),
mSize(defaultIconSize), mSecondLineVisibility(true)
{
updateModel();
setAutoUpdate(true);
}
/*!
Destructor
*/
CaItemModelPrivate::~CaItemModelPrivate()
{
mEntries.clear();
delete mNotifier;
}
/*!
Returns count of rows in model
\retval number of rows
*/
int CaItemModelPrivate::rowCount() const
{
return mEntries.count();
}
/*!
Returns QModelIndex of an item
\param row row
\retval QModelIndex of item in model
*/
QModelIndex CaItemModelPrivate::index(int row)
{
if ((row >= 0) && (row < mEntries.count())) {
return m_q->createIndex(row, 0);
} else {
return QModelIndex();
}
}
/*!
Returns appropiate model's data
\param modelIndex model index
\param role which data role to return
\retval models data as QVariant
*/
QVariant CaItemModelPrivate::data(const QModelIndex &modelIndex,
int role) const
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::data");
QVariant variant;
if (modelIndex.isValid()) {
switch (role) {
case Qt::DisplayRole:
variant = displayRole(modelIndex);
break;
case Qt::DecorationRole:
variant = QVariant(HbIcon(entry(modelIndex)->makeIcon(mSize)));
break;
case CaItemModel::IdRole:
variant = QVariant(entry(modelIndex)->id());
break;
case CaItemModel::TypeRole:
variant = QVariant(entry(modelIndex)->entryTypeName());
break;
case CaItemModel::FlagsRole:
variant = qVariantFromValue(entry(modelIndex)->flags());
break;
case CaItemModel::TextRole:
variant = QVariant(entry(modelIndex)->text());
break;
case CaItemModel::FullTextRole:
variant = QVariant(entry(modelIndex)->text() + QString(" ")
+ entry(modelIndex)->description());
break;
default:
variant = QVariant(QVariant::Invalid);
}
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::data");
return variant;
}
/*!
Disables or enables auto-update feature of the model
\param autoUpdate (HsMenuAutoUpdate)
*/
void CaItemModelPrivate::setAutoUpdate(bool autoUpdate)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::setAutoUpdate");
if (autoUpdate) {
if (!mNotifier) {
CaNotifierFilter filter(mQuery);
mNotifier = mService->createNotifier(filter);
connectSlots();
}
} else {
disconnectSlots();
delete mNotifier;
mNotifier = NULL;
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::setAutoUpdate");
}
/*!
Method for setting sorting order on model
(probably will be moved to MenuService)
\param sortOrder sorting order (SortAttribute)
*/
void CaItemModelPrivate::setSort(SortAttribute sortAttribute,
Qt::SortOrder sortOrder)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::setSort");
mQuery.setSort(sortAttribute, sortOrder);
updateLayout();
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::setSort");
}
/*!
Method for setting icon size
\param size icon size to display
*/
void CaItemModelPrivate::setIconSize(const QSize &size)
{
if (mSize == size)
return;
m_q->layoutAboutToBeChanged();
mSize = size;
m_q->layoutChanged();
}
/*!
Method for getting icon size
\retval icon size to display
*/
QSize CaItemModelPrivate::getIconSize() const
{
return mSize;
}
/*!
Gets index of the parent item
\retval QModelIndex representing root item
*/
QModelIndex CaItemModelPrivate::root()
{
if (mQuery.parentId()) {
return m_q->createIndex(0, 1);
} else {
return QModelIndex();
}
}
/*!
Returns an entry from model
\param modelIndex index of entry in model
\retval pointer to an entry
*/
QSharedPointer<CaEntry> CaItemModelPrivate::entry(const QModelIndex &modelIndex) const
{
if (modelIndex.column() == 1) {
return mParentEntry;
} else {
return mEntries.at(modelIndex.row());
}
}
/*!
Disables or enables secondline feature of the model
\param secondLine disables or enables second line
*/
void CaItemModelPrivate::setSecondLineVisibility(bool secondLineVisibility)
{
if (mSecondLineVisibility == secondLineVisibility)
return;
m_q->layoutAboutToBeChanged();
mSecondLineVisibility = secondLineVisibility;
m_q->layoutChanged();
}
/*!
Gets second line visibility attribute
\retrun second line visibility attribute
*/
bool CaItemModelPrivate::secondLineVisibility() const
{
return mSecondLineVisibility;
}
/*!
Returns proper display role for item
\param modelIndex item index
\retval QVariant containing display role
*/
QVariant CaItemModelPrivate::displayRole(const QModelIndex &modelIndex) const
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::displayRole")
QVariant result;
if (mSecondLineVisibility) {
if (entry(modelIndex)->description().isEmpty()) {
result = entry(modelIndex)->text();
} else {
QList<QVariant> text;
text << entry(modelIndex)->text();
text << entry(modelIndex)->description();
result = QVariant(text);
}
} else {
result = entry(modelIndex)->text();
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::displayRole")
return result;
}
/*!
Sets parent
\param parentId
*/
void CaItemModelPrivate::setParentId(int parentId)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::setParentId");
mQuery.setParentId(parentId);
if (mNotifier) {
delete mNotifier;
mNotifier = mService->createNotifier(CaNotifierFilter(mQuery));
reconnectSlots();
}
updateModel();
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::setParentId");
}
/*!
Sets flags to mQuery which should be enabled.
\param onFlags flags.
*/
void CaItemModelPrivate::setFlagsOn(const EntryFlags &onFlags)
{
mQuery.setFlagsOn(onFlags);
}
/*!
Sets flags to mQuery which should be disabled.
\param offFlags flags.
\code
...
// to set a new parent id
model->setFlagsOff(RemovableEntryFlag);
...
\endcode
*/
void CaItemModelPrivate::setFlagsOff(const EntryFlags &offFlags)
{
mQuery.setFlagsOff(offFlags);
}
/*!
Checks if notifier exists
\retval true if notifier exists otherwise false
*/
bool CaItemModelPrivate::notifierExists() const
{
if (mNotifier) {
return true;
}
return false;
}
/*!
Updates model with fresh entries and resets model
*/
void CaItemModelPrivate::updateModel()
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::updateModel");
mEntries.reloadEntries(mQuery);
updateParentEntry();
m_q->reset();
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::updateModel");
}
/*!
Updates parent entry
*/
void CaItemModelPrivate::updateParentEntry()
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::updateParentEntry");
if (mQuery.parentId()) {
mParentEntry = mService->getEntry(mQuery.parentId());
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::updateParentEntry");
}
/*!
Updates model item with fresh data
\param id id of item to update
*/
void CaItemModelPrivate::updateItemData(int id)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::updateItemData");
mEntries.updateEntry(id);
QList<int> ids = mService->getEntryIds(mQuery);
if (mEntries.indexOf(id) >= 0
&& ids.indexOf(id) == mEntries.indexOf(id)) {
emit m_q->dataChanged(index(mEntries.indexOf(id)), index(
mEntries.indexOf(id)));
} else if (mParentEntry && id == mParentEntry->id()) {
updateParentEntry();
m_q->reset();
} else {
updateModel();
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::updateItemData");
}
/*!
Adds new item to model
\param id id of item to add
*/
void CaItemModelPrivate::addItem(int id)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::addItem");
int row = itemRow(id);
//we use beginInsertRows and endInsertRows to emit proper signal
//(see Qt documentation of QAbstractItemModel)
if (mEntries.indexOf(id) < 0 && row >= 0) {
if (row > mEntries.count()) {
m_q->beginInsertRows(QModelIndex(), mEntries.count(), mEntries.count());
mEntries.insert(mEntries.count(), id);
} else {
m_q->beginInsertRows(QModelIndex(), row , row);
mEntries.insert(row, id);
}
m_q->endInsertRows();
} else if (row == -1) {
//we udpade whole model because we do not know parent collecion for given item
updateModel();
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::addItem");
}
/*!
Adds new item block to model
Use in cases when inserting / appending an adjacent block of items
\param itemsList list of adjacent items
*/
void CaItemModelPrivate::addItemBlock(const QList<int> &itemsList)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::addItemBlock");
if (!itemsList.isEmpty()) {
int firstRow = itemRow(itemsList.first());
int lastRow = itemRow(itemsList.last());
m_q->beginInsertRows(QModelIndex(), firstRow, lastRow);
for (int i = 0; i < itemsList.count(); ++i) {
mEntries.insert(firstRow + i, itemsList.at(i));
}
m_q->endInsertRows();
emit m_q->scrollTo(firstRow, QAbstractItemView::PositionAtTop);
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::addItemBlock");
}
/*!
Adds new items to model
\param itemsList current items list
*/
void CaItemModelPrivate::handleAddItems(const QList<int> &itemsList)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::handleAddItems");
const int oldItemCount(mEntries.count());
if (oldItemCount) {
const int newItemCount(itemsList.count());
if (newItemCount == oldItemCount) {
// count is the same - check if item order changed
if (itemsList == mEntries.orderedIdList()) {
// assume that if the order has not changed
// it had to be the secondary lines
updateModel();
} else {
updateLayout();
}
} else {
int i = 0;
QList<int> oldList = mEntries.orderedIdList();
//we loop through items to find first added
while (i < oldList.count()) {
if (oldList[i] != itemsList[i]) {
oldList.takeAt(i);
} else {
++i;
}
}
updateModel();
//i is the index of first added item
emit m_q->scrollTo(i, QAbstractItemView::PositionAtTop);
}
} else {
// items added to an empty list - add all as a single block
addItemBlock(itemsList);
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::handleAddItems");
}
/*!
Gets index/row for new item
\param id of item to add
\retval row in model list for new item to insert
*/
int CaItemModelPrivate::itemRow(int id)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::itemRow");
QList<int> ids = mService->getEntryIds(mQuery);
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::itemRow");
return ids.indexOf(id);
}
/*!
Removes item from model
\param id of item to remove
*/
void CaItemModelPrivate::removeItem(int id)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::removeItem");
int row = mEntries.indexOf(id);
if (row >= 0) {
m_q->beginRemoveRows(QModelIndex(), mEntries.indexOf(id),
mEntries.indexOf(id));
mEntries.remove(id);
m_q->endRemoveRows();
} else {
updateModel();
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::removeItem");
}
/*!
Removes missing items from model
\param itemsList current items list
*/
void CaItemModelPrivate::removeItems(const QList<int> &itemsList)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::removeItems");
int i = 0;
for (i = 0; i < itemsList.count(); i++) {
if (itemsList[i] != mEntries[i]) {
removeItem(mEntries[i]);
}
}
while (i < mEntries.count()) {
removeItem(mEntries[i]);
i++;
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::removeItems");
}
/*!
Layout update
NOTE: this method should be called only if the entries get rearranged
and do not change their contents!
*/
void CaItemModelPrivate::updateLayout()
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::updateLayout");
m_q->layoutAboutToBeChanged();
// get the ID list from before the update
QList<int> oldOrderedIdList = mEntries.orderedIdList();
// do the update, the entries should only get rearranged
mEntries.updateEntries(mQuery);
// get the new ID list after the entries got rearranged
QList<int> newOrderedIdList = mEntries.orderedIdList();
// this list will contain the new position indices
QList<int> newPositionsList;
int entry;
foreach (entry, oldOrderedIdList) {
newPositionsList << newOrderedIdList.indexOf(entry);
}
// now check which items in the previous persistent index list changed
// their positions, make new indices for them and store in the new
// persistent index list
QModelIndexList oldPersistentIndexList = m_q->persistentIndexList();
QModelIndexList newPersistentIndexList;
QModelIndex index;
foreach (index, oldPersistentIndexList) {
newPersistentIndexList <<
m_q->createIndex(
newPositionsList.at(index.row()), 0, index.internalPointer());
}
m_q->changePersistentIndexList(
oldPersistentIndexList, newPersistentIndexList);
m_q->layoutChanged();
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::updateLayout");
}
/*!
Connects slots
*/
void CaItemModelPrivate::connectSlots()
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::connectSlots");
connect(mNotifier, SIGNAL(entryChanged(int,ChangeType)),
this, SLOT(updateModelItem(int,ChangeType)));
if (mQuery.parentId() > 0) {
connect(mNotifier, SIGNAL(groupContentChanged(int)),
this, SLOT(updateModelContent(int)));
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::connectSlots");
}
/*!
Disconnects slots
*/
void CaItemModelPrivate::disconnectSlots()
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::disconnectSlots");
disconnect(mNotifier, SIGNAL(entryChanged(int,ChangeType)),
this, SLOT(updateModelItem(int,ChangeType)));
if (mQuery.parentId() > 0) {
disconnect(mNotifier, SIGNAL(groupContentChanged(int)),
this, SLOT(updateModelContent(int)));
}
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::disconnectSlots");
}
/*!
Reconnects slots
*/
void CaItemModelPrivate::reconnectSlots()
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::reconnectSlots");
disconnectSlots();
connectSlots();
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::reconnectSlots");
}
/*!
Updates model with fresh entries
\param id of item to handle
\param changeType change type
*/
void CaItemModelPrivate::updateModelItem(int id, ChangeType changeType)
{
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::updateModelItem");
int previousCount = rowCount();
switch (changeType) {
case AddChangeType:
addItem(id);
break;
case RemoveChangeType:
removeItem(id);
break;
default:
updateItemData(id);
break;
}
emitEmpty(previousCount);
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::updateModelItem");
}
/*!
Updates model with fresh entries
\param id of parent
*/
void CaItemModelPrivate::updateModelContent(int id)
{
Q_UNUSED(id);
int previousCount = rowCount();
CACLIENTTEST_FUNC_ENTRY("CaItemModelPrivate::updateModelContent");
QList<int> ids = mService->getEntryIds(mQuery);
if (ids.count() >= mEntries.count()) {
handleAddItems(ids);
} else {
removeItems(ids);
}
emitEmpty(previousCount);
CACLIENTTEST_FUNC_EXIT("CaItemModelPrivate::updateModelContent");
}
/*!
Emits empty signal if model state was changed
\param id of parent
*/
void CaItemModelPrivate::emitEmpty(int previousCount)
{
if ( previousCount && !rowCount()) {
emit m_q->empty(true);
}
if ( !previousCount && rowCount()) {
emit m_q->empty(false);
}
}