|
1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Private data and helper classes used by class CntCache. |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <e32base.h> |
|
19 #include <s32mem.h> |
|
20 #include <e32std.h> |
|
21 |
|
22 #include <xqutils.h> |
|
23 #include <QEvent> |
|
24 #include <QFile> |
|
25 #include <QDir> |
|
26 #include <hbapplication.h> |
|
27 #include <hbstringutil.h> |
|
28 |
|
29 #include <cntdb.h> |
|
30 #include <cntuids.h> |
|
31 #include <cntdebug.h> |
|
32 |
|
33 #include "cntnamefetcher.h" |
|
34 |
|
35 // constants used when fetching names from CntSrv |
|
36 #define KCntSearchResultList 99 |
|
37 #define KCntOpenDataBase 100 |
|
38 _LIT(KCntServerExe, "CNTSRV.EXE"); |
|
39 _LIT(KCntServerName, "CNTSRV"); |
|
40 const TInt KAsyncMessageSlots = 6; |
|
41 const TInt KCntServerMajorVersionNumber=1; |
|
42 const TInt KCntServerMinorVersionNumber=1; |
|
43 const TInt KCntServerBuildVersionNumber=1; |
|
44 static const QEvent::Type CntAsynchOperation = QEvent::User; |
|
45 |
|
46 // constants used for file cache |
|
47 static const QString cacheFolder = "20022EF9"; |
|
48 static const QString cacheFilename = "contactcache.dat"; |
|
49 |
|
50 /*! |
|
51 Internal class used by CntSrvConnection to issues requests to CntSrv. |
|
52 */ |
|
53 class CntSrvSession : public RSessionBase |
|
54 { |
|
55 public: |
|
56 CntSrvSession() { mConnected = false; } |
|
57 ~CntSrvSession() { RHandleBase::Close(); } |
|
58 void executeSqlQueryL(const TDesC &sqlQuery, QList<CntNameCacheItem *> &names, CntNameOrder nameFormat, int sizeHintKB); |
|
59 |
|
60 private: |
|
61 void connectCntSrvL(); |
|
62 |
|
63 private: |
|
64 bool mConnected; |
|
65 }; |
|
66 |
|
67 CntSrvConnection::CntSrvConnection() |
|
68 : mSession(NULL), |
|
69 mIsAsynchronous(false) |
|
70 { |
|
71 } |
|
72 |
|
73 CntSrvConnection::~CntSrvConnection() |
|
74 { |
|
75 disconnect(); |
|
76 |
|
77 if (mThread.isRunning()) { |
|
78 mThread.quit(); |
|
79 mThread.wait(); |
|
80 } |
|
81 |
|
82 delete mSession; |
|
83 |
|
84 mNames.clear(); |
|
85 } |
|
86 |
|
87 void CntSrvConnection::setAsynchronous() |
|
88 { |
|
89 mIsAsynchronous = true; |
|
90 mThread.start(); |
|
91 moveToThread(&mThread); |
|
92 } |
|
93 |
|
94 bool CntSrvConnection::executeSqlQuery(const QString &sqlQuery, CntNameOrder nameFormat, int sizeHintKB) |
|
95 { |
|
96 CNT_ENTRY |
|
97 |
|
98 if (!mSession) { |
|
99 mSession = new CntSrvSession(); |
|
100 } |
|
101 |
|
102 if (mIsAsynchronous) { |
|
103 mSqlQuery = sqlQuery; |
|
104 mNameFormat = nameFormat; |
|
105 mSizeHintKB = sizeHintKB; |
|
106 HbApplication::instance()->postEvent(this, new QEvent(CntAsynchOperation)); |
|
107 } else { |
|
108 mNames.clear(); |
|
109 TPtrC queryPtr(sqlQuery.utf16(), sqlQuery.length()); |
|
110 TRAPD(err, mSession->executeSqlQueryL(queryPtr, mNames, nameFormat, sizeHintKB)); |
|
111 if (err != KErrNone) { |
|
112 qDeleteAll(mNames); |
|
113 mNames.clear(); |
|
114 CNT_EXIT |
|
115 return false; |
|
116 } |
|
117 } |
|
118 |
|
119 CNT_EXIT |
|
120 |
|
121 return true; |
|
122 } |
|
123 |
|
124 bool CntSrvConnection::event(QEvent *event) |
|
125 { |
|
126 if (event->type() == CntAsynchOperation) { |
|
127 CNT_ENTRY |
|
128 |
|
129 mNames.clear(); |
|
130 TPtrC ptr(mSqlQuery.utf16(), mSqlQuery.length()); |
|
131 TRAPD(err, mSession->executeSqlQueryL(ptr, mNames, mNameFormat, mSizeHintKB)); |
|
132 if (err != KErrNone) { |
|
133 qDeleteAll(mNames); |
|
134 mNames.clear(); |
|
135 } |
|
136 emit namesRead(); |
|
137 qStableSort(mNames.begin(), mNames.end(), CntNameFetcher::compareNames); |
|
138 delete mSession; |
|
139 mSession = NULL; |
|
140 emit namesSorted(); |
|
141 |
|
142 CNT_EXIT |
|
143 |
|
144 return true; |
|
145 } |
|
146 |
|
147 return QObject::event(event); |
|
148 } |
|
149 |
|
150 /*! |
|
151 Executes a special SQL query: the first column must be the contact id and |
|
152 the subsequent columns must be varchar fields. |
|
153 |
|
154 \param sqlQuery the SQL to execute |
|
155 \param names the list where the results will be stored |
|
156 \param nameFormat the format the names should be stored in |
|
157 \param sizeHintKB the expected size of the buffer needed to fit the results; a too |
|
158 small value will effectively double the fetch time, since the |
|
159 buffer is then resized and the data refetched a second time |
|
160 */ |
|
161 void CntSrvSession::executeSqlQueryL(const TDesC& sqlQuery, QList<CntNameCacheItem*> &names, CntNameOrder nameFormat, int sizeHintKB) |
|
162 { |
|
163 int listSize = 0; |
|
164 |
|
165 // read the ids and names from the database |
|
166 if (!mConnected) { |
|
167 connectCntSrvL(); |
|
168 } |
|
169 |
|
170 // allocate tmeporary buffer |
|
171 TInt bufferSize = sizeHintKB * 1024; |
|
172 CBufFlat* buffer = CBufFlat::NewL(256); |
|
173 CleanupStack::PushL(buffer); |
|
174 |
|
175 // try to fetch the results, if the fetch fails with |
|
176 // a positive value, it means the buffer was too small |
|
177 // in this case the buffer is resized and the results |
|
178 // are fetched again |
|
179 for (TInt tries = 0; tries < 2 && bufferSize > 0; ++tries) { |
|
180 buffer->ResizeL(bufferSize); |
|
181 TPtr8 bufferPtr = buffer->Ptr(0); |
|
182 TIpcArgs args; |
|
183 args.Set(0, &bufferPtr); |
|
184 args.Set(1, &sqlQuery); |
|
185 bufferSize = SendReceive(KCntSearchResultList, args); |
|
186 CNT_LOG_ARGS("buffer size =" << bufferSize) |
|
187 User::LeaveIfError(bufferSize); |
|
188 } |
|
189 |
|
190 // store the formatted names into the list |
|
191 RBufReadStream readStream; |
|
192 TInt id; |
|
193 TBuf<256> firstName; |
|
194 TBuf<256> lastName; |
|
195 |
|
196 readStream.Open(*buffer); |
|
197 for (int i = 0; (id = readStream.ReadInt32L()) != 0; ++i) { |
|
198 readStream >> firstName; |
|
199 readStream >> lastName; |
|
200 CntNameCacheItem* item = new (ELeave) CntNameCacheItem( |
|
201 id, |
|
202 QString::fromUtf16(firstName.Ptr(), firstName.Length()), |
|
203 QString::fromUtf16(lastName.Ptr(), lastName.Length()), |
|
204 nameFormat); |
|
205 if (i >= listSize - 1) { |
|
206 // if the list is runnning out of space, resize it; |
|
207 // initial size is 1000 and after that it doubles |
|
208 // every time it runs out of space |
|
209 if (listSize == 0) { |
|
210 listSize = 1000; |
|
211 } else { |
|
212 listSize *= 2; |
|
213 } |
|
214 QT_TRY { |
|
215 names.reserve(listSize); |
|
216 } QT_CATCH (...) { |
|
217 // clean up and return |
|
218 CleanupStack::PopAndDestroy(buffer); |
|
219 qDeleteAll(names); |
|
220 names.clear(); |
|
221 return; |
|
222 } |
|
223 } |
|
224 names.append(item); |
|
225 } |
|
226 |
|
227 CleanupStack::PopAndDestroy(buffer); |
|
228 } |
|
229 |
|
230 /*! |
|
231 Connect to / create a contacts server session. |
|
232 */ |
|
233 void CntSrvSession::connectCntSrvL() |
|
234 { |
|
235 // Assume the server is already running and attempt to create a session |
|
236 // with a maximum of KAsyncMessageSlots message slots. |
|
237 TInt err = CreateSession(KCntServerName, |
|
238 TVersion(KCntServerMajorVersionNumber, KCntServerMinorVersionNumber, KCntServerBuildVersionNumber), |
|
239 KAsyncMessageSlots); |
|
240 |
|
241 // Server is not running |
|
242 if (err == KErrNotFound) { |
|
243 // Use the RProcess API to start the server. |
|
244 RProcess server; |
|
245 User::LeaveIfError(server.Create(KCntServerExe, KNullDesC)); |
|
246 |
|
247 // Enforce server to be at system default priority EPriorityForeground |
|
248 server.SetPriority(EPriorityForeground); |
|
249 |
|
250 // Synchronize with the server. |
|
251 TRequestStatus reqStatus; |
|
252 server.Rendezvous(reqStatus); |
|
253 server.Resume(); |
|
254 |
|
255 // Server will call the reciprocal static synchronization call. |
|
256 User::WaitForRequest(reqStatus); |
|
257 server.Close(); |
|
258 User::LeaveIfError(reqStatus.Int()); |
|
259 |
|
260 // Create the server session. |
|
261 User::LeaveIfError(CreateSession(KCntServerName, |
|
262 TVersion(KCntServerMajorVersionNumber, KCntServerMinorVersionNumber, KCntServerBuildVersionNumber), |
|
263 KAsyncMessageSlots)); |
|
264 } else { |
|
265 User::LeaveIfError(err); |
|
266 } |
|
267 |
|
268 TIpcArgs args; |
|
269 args.Set(0, &KNullDesC); |
|
270 User::LeaveIfError(SendReceive(KCntOpenDataBase, args)); |
|
271 |
|
272 mConnected = true; |
|
273 } |
|
274 |
|
275 /*! |
|
276 Creates a CntNameFetcher object. |
|
277 */ |
|
278 CntNameFetcher::CntNameFetcher() |
|
279 : mDbConnection(NULL), |
|
280 mAsynchDbConnection(NULL), |
|
281 mSettingsManager(NULL), |
|
282 mNameFormatSetting(NULL), |
|
283 mBufferSizeEstimate(0) |
|
284 { |
|
285 CNT_ENTRY |
|
286 |
|
287 // get name format setting and listen to changes |
|
288 mSettingsManager = new XQSettingsManager(); |
|
289 mNameFormatSetting = new XQSettingsKey(XQSettingsKey::TargetCentralRepository, KCRCntSettings.iUid, KCntNameOrdering); |
|
290 mNameFormat = static_cast<CntNameOrder>(mSettingsManager->readItemValue(*mNameFormatSetting, XQSettingsManager::TypeInt).toInt()); |
|
291 mSettingsManager->startMonitoring(*mNameFormatSetting, XQSettingsManager::TypeInt); |
|
292 connect(mSettingsManager, SIGNAL(valueChanged(const XQSettingsKey&, const QVariant&)), this, SLOT(setNameFormat(const XQSettingsKey&, const QVariant&))); |
|
293 |
|
294 // connect to contacts server |
|
295 mDbConnection = new CntSrvConnection(); |
|
296 |
|
297 CNT_EXIT |
|
298 } |
|
299 |
|
300 /*! |
|
301 Destroys a CntNameFetcher object. |
|
302 */ |
|
303 CntNameFetcher::~CntNameFetcher() |
|
304 { |
|
305 CNT_ENTRY |
|
306 |
|
307 delete mSettingsManager; |
|
308 delete mNameFormatSetting; |
|
309 delete mDbConnection; |
|
310 delete mAsynchDbConnection; |
|
311 |
|
312 CNT_EXIT |
|
313 } |
|
314 |
|
315 /*! |
|
316 Reads names from the file cache. |
|
317 |
|
318 \return true if the names were read successfully from the cache file |
|
319 |
|
320 */ |
|
321 bool CntNameFetcher::readNamesFromCache(QList<CntNameCacheItem*> &names) |
|
322 { |
|
323 CNT_ENTRY |
|
324 |
|
325 bool success = true; |
|
326 quint32 itemCount; |
|
327 quint32 nameFormat; |
|
328 |
|
329 QFile cacheFile(XQUtils::phoneMemoryRootPath() + cacheFolder + "\\" + cacheFilename); |
|
330 if (!cacheFile.open(QIODevice::ReadOnly)) { |
|
331 return false; |
|
332 } |
|
333 |
|
334 QDataStream in(&cacheFile); |
|
335 |
|
336 mBufferSizeEstimate = 0; |
|
337 QT_TRY { |
|
338 // read header: nr of items, name format |
|
339 in >> itemCount; |
|
340 in >> nameFormat; |
|
341 names.reserve(itemCount); |
|
342 |
|
343 // populate list with names |
|
344 while (itemCount-- > 0) { |
|
345 CntNameCacheItem *item = CntNameCacheItem::internalize(in, (CntNameOrder) nameFormat); |
|
346 names.append(item); |
|
347 mBufferSizeEstimate += 4 + 2 * item->name().length(); |
|
348 } |
|
349 } QT_CATCH (...) { |
|
350 qDeleteAll(names); |
|
351 names.clear(); |
|
352 success = false; |
|
353 } |
|
354 |
|
355 cacheFile.close(); |
|
356 |
|
357 CNT_EXIT |
|
358 |
|
359 return success; |
|
360 } |
|
361 |
|
362 /*! |
|
363 Write names to the file cache. |
|
364 */ |
|
365 bool CntNameFetcher::writeNamesToCache(const QList<CntNameCacheItem*> &names) const |
|
366 { |
|
367 CNT_ENTRY |
|
368 |
|
369 bool success = true; |
|
370 |
|
371 // create folder for cache file if it does not already exist |
|
372 QString path = XQUtils::phoneMemoryRootPath() + cacheFolder; |
|
373 if (!QDir(path).exists()) { |
|
374 QDir dir(XQUtils::phoneMemoryRootPath()); |
|
375 if (!dir.mkdir(cacheFolder)) { |
|
376 CNT_EXIT_ARGS("failed to create folder: " << path) |
|
377 return false; |
|
378 } |
|
379 |
|
380 // have to use native Symbian code to make the dir hidden |
|
381 RFs fs; |
|
382 fs.Connect(); |
|
383 TPtrC pathPtr(path.utf16(), path.length()); |
|
384 if (fs.SetAtt(pathPtr, KEntryAttHidden, 0) != KErrNone) { |
|
385 fs.Close(); |
|
386 return false; |
|
387 } |
|
388 fs.Close(); |
|
389 } |
|
390 |
|
391 // open cache file for writing |
|
392 QFile cacheFile(XQUtils::phoneMemoryRootPath() + cacheFolder + "\\" + cacheFilename); |
|
393 if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { |
|
394 CNT_EXIT_ARGS("failed to create file") |
|
395 return false; |
|
396 } |
|
397 QDataStream out(&cacheFile); |
|
398 |
|
399 // write the names to the cache file |
|
400 QT_TRY { |
|
401 // write header |
|
402 out << names.size(); |
|
403 out << (quint32) mNameFormat; |
|
404 |
|
405 // write list with names |
|
406 foreach (CntNameCacheItem* name, names) { |
|
407 name->externalize(out); |
|
408 } |
|
409 } QT_CATCH (...) { |
|
410 success = false; |
|
411 } |
|
412 |
|
413 cacheFile.close(); |
|
414 |
|
415 CNT_EXIT |
|
416 |
|
417 return success; |
|
418 } |
|
419 |
|
420 /*! |
|
421 Reads the name of one contact from the contact database synchronously. |
|
422 |
|
423 \param contactId the id of the contact |
|
424 */ |
|
425 CntNameCacheItem* CntNameFetcher::readOneName(QContactLocalId contactId) const |
|
426 { |
|
427 CNT_ENTRY |
|
428 |
|
429 QString sqlQuery = QString("SELECT contact_id, first_name, last_name FROM contact WHERE (type_flags>>24)<=1 AND contact_id=%1").arg(contactId); |
|
430 mDbConnection->executeSqlQuery(sqlQuery, mNameFormat, 2); |
|
431 |
|
432 if (mDbConnection->names().size() == 0) { |
|
433 return NULL; |
|
434 } |
|
435 |
|
436 CNT_EXIT |
|
437 |
|
438 return mDbConnection->names().at(0); |
|
439 } |
|
440 |
|
441 /*! |
|
442 Reads the names of all contacts from the contact database asynchronously. |
|
443 */ |
|
444 void CntNameFetcher::readAllNamesAsynch() |
|
445 { |
|
446 CNT_ENTRY |
|
447 |
|
448 if (mAsynchDbConnection != NULL) { |
|
449 // an asynch fetch is already in progress, so no need to start a new one |
|
450 return; |
|
451 } |
|
452 |
|
453 if (mBufferSizeEstimate == 0) { |
|
454 mBufferSizeEstimate = 240 * 1024; |
|
455 } |
|
456 |
|
457 CNT_LOG_ARGS("buffer size =" << mBufferSizeEstimate) |
|
458 |
|
459 mAsynchDbConnection = new CntSrvConnection(); |
|
460 mAsynchDbConnection->setAsynchronous(); |
|
461 connect(mAsynchDbConnection, SIGNAL(namesRead()), this, SIGNAL(databaseAccessComplete())); |
|
462 connect(mAsynchDbConnection, SIGNAL(namesSorted()), this, SLOT(sendCompletionSignal())); |
|
463 mAsynchDbConnection->executeSqlQuery("SELECT contact_id, first_name, last_name FROM contact WHERE (type_flags>>24)<=1", mNameFormat, 16 + mBufferSizeEstimate / 1024); |
|
464 |
|
465 CNT_EXIT |
|
466 } |
|
467 |
|
468 /*! |
|
469 Sorts the names quickly and in a locale aware manner. |
|
470 */ |
|
471 void CntNameFetcher::sortNames(QList<CntNameCacheItem *> &names) const |
|
472 { |
|
473 CNT_ENTRY |
|
474 |
|
475 qStableSort(names.begin(), names.end(), CntNameFetcher::compareNames); |
|
476 |
|
477 CNT_EXIT |
|
478 } |
|
479 |
|
480 /*! |
|
481 Compares a pair of contact names and returns true if the first |
|
482 one should be presented before the second one in a list. This |
|
483 static function is used e.g. when sorting lists of names. |
|
484 */ |
|
485 bool CntNameFetcher::compareNames(const CntNameCacheItem* a, const CntNameCacheItem* b) |
|
486 { |
|
487 QString aName = a->name(); |
|
488 QString bName = b->name(); |
|
489 |
|
490 if (aName.isEmpty()) { |
|
491 return false; |
|
492 } else if (bName.isEmpty()) { |
|
493 return true; |
|
494 } |
|
495 |
|
496 return (HbStringUtil::compareC(aName, bName) < 0); |
|
497 } |
|
498 |
|
499 /*! |
|
500 Notifies clients that the name format has changed. This function is called by the framework |
|
501 if the name format settings is changed, see the constructor. |
|
502 */ |
|
503 void CntNameFetcher::setNameFormat(const XQSettingsKey &/*key*/, const QVariant &value) |
|
504 { |
|
505 CNT_ENTRY |
|
506 |
|
507 bool ok = false; |
|
508 CntNameOrder newNameFormat = static_cast<CntNameOrder>(value.toInt(&ok)); |
|
509 if (ok && newNameFormat != mNameFormat) { |
|
510 mNameFormat = newNameFormat; |
|
511 emit nameFormatChanged(mNameFormat); |
|
512 } |
|
513 |
|
514 CNT_EXIT |
|
515 } |
|
516 |
|
517 /*! |
|
518 Emits the results of a completed asynch database operation. |
|
519 */ |
|
520 void CntNameFetcher::sendCompletionSignal() |
|
521 { |
|
522 CNT_ENTRY |
|
523 |
|
524 emit namesAvailable(mAsynchDbConnection->names()); |
|
525 |
|
526 delete mAsynchDbConnection; |
|
527 mAsynchDbConnection = NULL; |
|
528 |
|
529 CNT_EXIT |
|
530 } |
|
531 |
|
532 /*! |
|
533 Creates a CntNameCacheItem object. |
|
534 */ |
|
535 CntNameCacheItem::CntNameCacheItem(QContactLocalId id, const QString& firstName, const QString& lastName, CntNameOrder nameFormat) |
|
536 { |
|
537 mContactId = id; |
|
538 setFormattedName(firstName, lastName, nameFormat); |
|
539 } |
|
540 |
|
541 /*! |
|
542 Destroys a CntNameCacheItem object. |
|
543 */ |
|
544 CntNameCacheItem::~CntNameCacheItem() |
|
545 { |
|
546 } |
|
547 |
|
548 /*! |
|
549 Changes the format used to present the name. |
|
550 */ |
|
551 void CntNameCacheItem::setNameFormat(CntNameOrder newFormat) |
|
552 { |
|
553 QString firstName = mName.mid(mFirstNamePosition&0xffff, mFirstNamePosition>>16); |
|
554 QString lastName = mName.mid(mLastNamePosition&0xffff, mLastNamePosition>>16); |
|
555 setFormattedName(firstName, lastName, newFormat); |
|
556 } |
|
557 |
|
558 /*! |
|
559 Copies the contents of the other cache item to this one. |
|
560 */ |
|
561 void CntNameCacheItem::operator=(const CntNameCacheItem &other) |
|
562 { |
|
563 mContactId = other.mContactId; |
|
564 mFirstNamePosition = other.mFirstNamePosition; |
|
565 mLastNamePosition = other.mLastNamePosition; |
|
566 mName = other.mName; |
|
567 } |
|
568 |
|
569 /*! |
|
570 Externalizes a CntNameCacheItem object. |
|
571 */ |
|
572 void CntNameCacheItem::externalize(QDataStream &stream) |
|
573 { |
|
574 stream << mContactId; |
|
575 stream << mFirstNamePosition; |
|
576 stream << mLastNamePosition; |
|
577 stream << mName; |
|
578 } |
|
579 |
|
580 /*! |
|
581 Internalizes a CntNameCacheItem object. |
|
582 */ |
|
583 CntNameCacheItem* CntNameCacheItem::internalize(QDataStream &stream, CntNameOrder nameFormat) |
|
584 { |
|
585 quint32 id; |
|
586 quint32 firstNamePosition; |
|
587 quint32 lastNamePosition; |
|
588 QString name; |
|
589 |
|
590 stream >> id; |
|
591 stream >> firstNamePosition; |
|
592 stream >> lastNamePosition; |
|
593 stream >> name; |
|
594 |
|
595 QString firstName = name.mid(firstNamePosition&0xffff, firstNamePosition>>16); |
|
596 QString lastName = name.mid(lastNamePosition&0xffff, lastNamePosition>>16); |
|
597 |
|
598 return new CntNameCacheItem(id, firstName, lastName, nameFormat); |
|
599 } |
|
600 |
|
601 /*! |
|
602 Sets the formatted name and positions of the first name and last name, |
|
603 according to the name format in the parameter. |
|
604 */ |
|
605 void CntNameCacheItem::setFormattedName(const QString& firstName, const QString& lastName, CntNameOrder nameFormat) |
|
606 { |
|
607 int firstNameLength = firstName.length(); |
|
608 int lastNameLength = lastName.length(); |
|
609 |
|
610 if (lastNameLength == 0) { |
|
611 mName = firstName; |
|
612 mFirstNamePosition = firstNameLength << 16; |
|
613 mLastNamePosition = 0; |
|
614 } else if (firstNameLength == 0) { |
|
615 mName = lastName; |
|
616 mFirstNamePosition = 0; |
|
617 mLastNamePosition = lastNameLength << 16; |
|
618 } else { |
|
619 if (nameFormat == CntOrderLastFirst) { |
|
620 mName = lastName + " " + firstName; |
|
621 mFirstNamePosition = (firstNameLength << 16) | (lastNameLength + 1); |
|
622 mLastNamePosition = (lastNameLength << 16); |
|
623 } else if (nameFormat == CntOrderLastCommaFirst) { |
|
624 mName = lastName + ", " + firstName; |
|
625 mFirstNamePosition = (firstNameLength << 16) | (lastNameLength + 2); |
|
626 mLastNamePosition = (lastNameLength << 16); |
|
627 } else { |
|
628 mName = firstName + " " + lastName; |
|
629 mFirstNamePosition = (firstNameLength << 16); |
|
630 mLastNamePosition = (lastNameLength << 16) | (firstNameLength + 1); |
|
631 } |
|
632 } |
|
633 } |
|
634 |
|
635 QString CntNameCacheItem::firstName() const |
|
636 { |
|
637 return mName.mid(mFirstNamePosition&0xffff, mFirstNamePosition>>16); |
|
638 } |
|
639 |
|
640 QString CntNameCacheItem::lastName() const |
|
641 { |
|
642 return mName.mid(mLastNamePosition&0xffff, mLastNamePosition>>16); |
|
643 } |