/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "cntsymbiansorterdbms.h"
#include "cntsymbiantransformerror.h"
#include <cntdb.h>
#include <cntdef.h>
#include <cntfield.h>
#include "cnttransformcontact.h"
typedef QList<QContactLocalId> QContactLocalIdList;
/* ... The macros changed names */
#if QT_VERSION < QT_VERSION_CHECK(4, 6, 0)
#define QT_TRAP_THROWING QT_TRANSLATE_SYMBIAN_LEAVE_TO_EXCEPTION
#define QT_TRYCATCH_LEAVING QT_TRANSLATE_EXCEPTION_TO_SYMBIAN_LEAVE
#endif
// NOTE: There is a bug with RVCT compiler which causes the local stack
// variable to corrupt if the called function leaves. As a workaround we are
// reserving the objects from heap so it will not get corrupted.
// This of course applies only to those stack variables which are passed to
// the called function or the return value of the function is placed to the
// variable.
CntSymbianSorterDbms::CntSymbianSorterDbms(CContactDatabase& contactDatabase,CntTransformContact& transformContact):
m_contactDatabase(contactDatabase),
m_transformContact(transformContact)
{
}
CntSymbianSorterDbms::~CntSymbianSorterDbms()
{
}
QList<QContactLocalId> CntSymbianSorterDbms::contacts(
const QList<QContactSortOrder>& sortOrders,
QContactManager::Error* error)
{
// Create an empty list
// See QT_TRYCATCH_LEAVING note at the begginning of this file
QContactLocalIdList *ids = new QContactLocalIdList();
// Attempt to read from database, leaving the list empty if
// there was a problem
TRAPD(err, QT_TRYCATCH_LEAVING(*ids = contactsL(sortOrders)));
CntSymbianTransformError::transformError(err, error);
return *QScopedPointer<QContactLocalIdList>(ids);
}
QList<QContactLocalId> CntSymbianSorterDbms::sort(
QList<QContactLocalId> contactIds,
const QList<QContactSortOrder>& sortOrders,
QContactManager::Error* error)
{
// Create an empty list
// See QT_TRYCATCH_LEAVING note at the begginning of this file
QContactLocalIdList *ids = new QContactLocalIdList();
// Attempt to read from database, leaving the list empty if
// there was a problem
TRAPD(err, QT_TRYCATCH_LEAVING(*ids = sortL(contactIds,sortOrders)));
CntSymbianTransformError::transformError(err, error);
return *QScopedPointer<QContactLocalIdList>(ids);
}
bool CntSymbianSorterDbms::sortOrderSupported(const QList<QContactSortOrder>& sortOrders)
{
foreach(const QContactSortOrder& s, sortOrders ) {
// Find uids for sortings
QList<TUid> fieldTypeUids = m_transformContact.supportedSortingFieldTypes(s.detailDefinitionName(), s.detailFieldName());
if( fieldTypeUids.count() == 0 )
return false;
// Only blanks last supported
if( s.blankPolicy() != QContactSortOrder::BlanksLast )
return false;
// Always case sensitive
if( s.caseSensitivity() != Qt::CaseSensitive )
return false;
#ifndef SYMBIAN_BACKEND_USE_SQLITE
// NOTE:
// Seems that there is a bug in cntmodel which causes that sorting
// is working correctly only if the direction is the same for all
// sort orders.
if( s.direction() != sortOrders[0].direction() )
return false;
#endif
}
return true;
}
QList<QContactLocalId> CntSymbianSorterDbms::contactsL(const QList<QContactSortOrder>& sortOrders) const
{
// Populate the ID array, returns the coontact ids + group ids
TTime epoch(0);
CContactIdArray *ids = m_contactDatabase.ContactsChangedSinceL(epoch);
CleanupStack::PushL(ids);
// Remove templates from the list
CContactIdArray *templateIds = m_contactDatabase.GetCardTemplateIdListL();
CleanupStack::PushL(templateIds);
for(TInt i(0); i < templateIds->Count(); ++i) {
TContactItemId id = (*templateIds)[i];
TInt index = ids->Find(id);
if(index > KErrNotFound)
ids->Remove(index);
}
CleanupStack::PopAndDestroy(templateIds);
// Sort the list
CContactIdArray* sortedIds = sortL(ids, sortOrders);
CleanupStack::PopAndDestroy(ids);
ids = sortedIds;
CleanupStack::PushL(ids);
// Add the contact ids to the returned QList
QList<QContactLocalId> qIds;
for (TInt i(0); i < ids->Count(); ++i) {
qIds.append((*ids)[i]);
}
CleanupStack::PopAndDestroy(ids);
return qIds;
}
QList<QContactLocalId> CntSymbianSorterDbms::sortL(const QList<QContactLocalId>& contactIds, const QList<QContactSortOrder>& sortOrders) const
{
CContactIdArray* ids = CContactIdArray::NewLC();
foreach(QContactLocalId id, contactIds)
ids->AddL(id);
CContactIdArray* sortedIds = sortL(ids, sortOrders);
CleanupStack::PopAndDestroy(ids);
CleanupStack::PushL(sortedIds);
QList<QContactLocalId> qSortedIds;
for (int i=0; i<sortedIds->Count(); i++)
qSortedIds.append( (*sortedIds)[i] );
CleanupStack::PopAndDestroy(sortedIds);
return qSortedIds;
}
CContactIdArray* CntSymbianSorterDbms::sortL(const CContactIdArray* contactIds, const QList<QContactSortOrder>& sortOrders) const
{
CArrayFixFlat<CContactDatabase::TSortPref> *sort =
new (ELeave) CArrayFixFlat<CContactDatabase::TSortPref>(5);
CleanupStack::PushL(sort);
// Convert sort orders to TSortPref array
foreach (const QContactSortOrder& s, sortOrders)
{
QList<TUid> fieldTypes = m_transformContact.supportedSortingFieldTypes(s.detailDefinitionName(), s.detailFieldName());
if (fieldTypes.count())
{
foreach(const TUid& fieldType, fieldTypes) {
CContactDatabase::TSortPref pref;
// NOTE: TSortPref sets order to ascending by default
if( s.direction() == Qt::DescendingOrder )
pref.iOrder = CContactDatabase::TSortPref::EDesc;
pref.iFieldType = fieldType;
sort->AppendL(pref);
}
}
else
{
// This should never happen. Sorting should have happened
// the "slow way" at QContactManagerEngine::sortContacts.
User::Leave(KErrNotFound);
}
}
CContactIdArray* sortedIds(0);
// There is a bug in CContactDatabase::SortArrayL, if an empty sort is used
// the function returns all contacts (and groups) instead of the given
// contact ids
if(sortOrders.isEmpty()) {
sortedIds = CContactIdArray::NewL(contactIds);
} else {
sortedIds = m_contactDatabase.SortArrayL(contactIds,sort);
}
CleanupStack::PopAndDestroy(sort);
return sortedIds;
}