--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/symhelp/helpmodel/src/HLPMODEL.CPP Tue Jan 26 15:15:23 2010 +0200
@@ -0,0 +1,1714 @@
+// Copyright (c) 1999-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:
+//
+
+#include "HLPMODEL.H"
+
+// System includes
+#include <coehelp.h>
+#include <txtrich.h>
+#include <bautils.h>
+
+// User includes
+#include "hlppict.h"
+#include "HLPSRCH.H"
+#include "hlppanic.h"
+#include "Hlpsqlconsts.h"
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "hlpmodel_internal.h"
+#endif
+// For searching for help files
+_LIT(KHlpFileSpec, "*.h*");
+_LIT(KDefaultHelpExtension, ".hlp");
+
+_LIT(KHlpFileSearchPath, "\\Resource\\Help\\");
+
+#ifdef __SHOW_LOADING_INFO__
+_LIT(KFormat1, "\n%S %d: %c:%S%S");
+_LIT(KFormat2, "\n%S %d: %c:%S%S");
+_LIT(KCompareNewListWithMasterList, "compare master list");
+_LIT(KDeleteEntry, "deleting");
+_LIT(KGetNearestLanguageFile, "getting nearest languuage file for");
+_LIT(KAddNewEntryToMasterList, "adding to master list");
+_LIT(KLoadMasterList, "Loading");
+_LIT(KFoundEntry, "Found entry during dir scan");
+_LIT(KDuplicateEntry, "entry discarded as duplicate");
+_LIT(KAddNewEntry, "entry added to list");
+#endif
+
+
+// Typedefs
+typedef CArrayPtrFlat<CHlpFileEntry> CHlpFileList;
+
+// Constants
+const TInt KHlpModelNumericValueSize = 32;
+
+// Average number of pictures per Help Topic
+const TInt KAverageNumberOfPicturesInHelpTopic = 3;
+
+#define UNUSED_VAR(a) a = a
+//
+// ----> CHlpFileEntry (header)
+//
+
+class CHlpFileEntry : public CBase
+ {
+
+public:
+ static CHlpFileEntry* NewLC(TDriveUnit aDrive, const TDesC& aFile);
+
+ inline TDriveUnit Drive() const { return iDrive; }
+ inline const TDesC& FileName() const { return iFile; }
+ inline const TDesC& Name() const { return iName; }
+ void GetFullNameAndPath(TDes& aName) const;
+
+private:
+ CHlpFileEntry(TDriveUnit aDrive, const TDesC& aFile);
+
+private:
+ TName iName;
+ TName iFile;
+ TDriveUnit iDrive;
+ };
+
+
+
+//
+// ----> Static utility function (source)
+//
+
+//#define __SHOW_LOADING_INFO__
+
+#ifdef __SHOW_LOADING_INFO__
+static void PrintEntryL(const TDesC& aPrompt, const CHlpFileEntry& aEntry, TInt aNumber = -1)
+ {
+ TFileName pFileName(aEntry.FileName());
+ TChar driveLetter = '?';
+ RFs::DriveToChar(aEntry.Drive(), driveLetter);
+
+ HBufC* buf = HBufC::NewLC(aPrompt.Length() + pFileName.Length() + KHlpFileSearchPath().Length() + 40);
+ TDes pBuf(buf->Des());
+
+
+ if (aNumber >= KErrNone)
+ pBuf.Format(KFormat1, &aPrompt, aNumber, driveLetter, &KHlpFileSearchPath(), &pFileName);
+ else
+ pBuf.Format(KFormat2, &aPrompt, driveLetter, &KHlpFileSearchPath(), &pFileName);
+
+ RDebug::Print(pBuf);
+
+ CleanupStack::PopAndDestroy();
+ }
+#define __PRINT_FILE(aPrompt, aEntry) (PrintEntryL(aPrompt, aEntry))
+#define __PRINT_FILE_NO(aPrompt, aEntry, aNumber) (PrintEntryL(aPrompt, aEntry, aNumber))
+#else
+#define __PRINT_FILE(aPrompt, aEntry)
+#define __PRINT_FILE_NO(aPrompt, aEntry, aNumber)
+#endif
+
+
+
+
+//
+// ----> CHlpFileEntry (source)
+//
+CHlpFileEntry::CHlpFileEntry(TDriveUnit aDrive, const TDesC& aFile)
+:iFile(aFile)
+,iDrive(aDrive)
+ {
+ TParsePtrC parser(iFile);
+ iName.Copy(parser.Name());
+ }
+
+CHlpFileEntry* CHlpFileEntry::NewLC(TDriveUnit aDrive, const TDesC& aFile)
+ {
+ CHlpFileEntry* self = new(ELeave) CHlpFileEntry(aDrive, aFile);
+ CleanupStack::PushL(self);
+ return self;
+ }
+
+
+//
+//
+//
+void CHlpFileEntry::GetFullNameAndPath(TDes& aName) const
+ {
+ TChar driveLetter = '?';
+ RFs::DriveToChar(Drive(), driveLetter);
+ aName.Zero();
+ aName.Append(driveLetter);
+ aName.Append(':');
+ aName.Append(KHlpFileSearchPath);
+ aName.Append(FileName());
+ }
+
+
+//
+// ----> CHlpModel
+//
+
+CHlpModel::CHlpModel(RFs& aFs, MHlpModelObserver& aObserver)
+: iFsSession(aFs), iObserver(&aObserver)
+ {
+ }
+
+EXPORT_C CHlpModel::~CHlpModel()
+/** Destructor. */
+ {
+ if (iDatabases)
+ iDatabases->ResetAndDestroy();
+
+ NotifyHelpModelDestructionToPictures();
+ if (iPictures)
+ iPictures->Reset();
+
+ delete iDatabases;
+ delete iPictures;
+ delete iSearch;
+ delete iCriterion;
+ delete iZoomFactors;
+ }
+
+void CHlpModel::NotifyHelpModelDestructionToPictures()
+ {
+ if (!iPictures)
+ return;
+ const TInt count = iPictures->Count();
+ for (TInt i=0; i<count; i++)
+ {
+ CHlpPicture& picture = *iPictures->At(i);
+ picture.HandleHelpModelDestruction();
+ }
+ }
+
+
+void CHlpModel::ConstructL()
+ {
+ iDatabases = new(ELeave) CHlpDatabases(2);
+ iPictures = new(ELeave) CArrayPtrFlat<CHlpPicture>(KAverageNumberOfPicturesInHelpTopic);
+ iSearch = CHlpSQLSearch::NewL(*this);
+
+ // There must always be 3 default zoom ratios present in the array...
+ iZoomFactors = new(ELeave) CArrayFixFlat<TInt>(3);
+ iZoomFactors->AppendL(KHlpModelZoomFactorSmall);
+ iZoomFactors->AppendL(KHlpModelZoomFactorMedium);
+ iZoomFactors->AppendL(KHlpModelZoomFactorLarge);
+ }
+
+EXPORT_C CHlpModel* CHlpModel::NewL(RFs& aFs, MHlpModelObserver* aObserver)
+/** Allocates and creates a help model object.
+
+@param aFs Open file server handle
+@param aObserver Client callback interface to handle messages from the help
+model
+@return New help model object */
+ {
+ CHlpModel* self = CHlpModel::NewLC(aFs, aObserver);
+ CleanupStack::Pop();
+ return self;
+ }
+
+EXPORT_C CHlpModel* CHlpModel::NewLC(RFs& aFs, MHlpModelObserver* aObserver)
+/** Allocates and creates a help model object, leaving the object on the cleanup
+stack.
+
+@param aFs Open file server handle
+@param aObserver Client callback interface to handle messages from the help
+model
+@return New help model object */
+ {
+ CHlpModel* self = new(ELeave) CHlpModel(aFs, *aObserver);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+
+//
+//
+//
+
+EXPORT_C void CHlpModel::OpenL()
+/** Opens all the help files in \\Resource\\Help. */
+ {
+ CHlpFileList* masterList = new(ELeave) CHlpFileList(5);
+ CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfCHlpFileEntry,masterList));
+
+ TInt i = EDriveY;
+ FOREVER
+ {
+ // Make sure we go from Y -> A, then do Z last
+ if (i < EDriveA)
+ i = EDriveZ;
+
+ if (DiskPresent(i))
+ {
+ // This generates a list for a specific directory on the specified drive.
+ CHlpFileList* list = BuildListForDriveLC(TDriveUnit(i), iFsSession);
+
+ TInt newListCount = list->Count();
+ if(newListCount)
+ {
+ // compare the new list with our master list to check for duplicates
+ TInt masterCount = masterList->Count();
+ for(TInt j=masterCount-1; j>=0; j--)
+ {
+ CHlpFileEntry* entry = masterList->At(j);
+ __PRINT_FILE_NO(KCompareNewListWithMasterList, *entry, j);
+
+ newListCount = list->Count();
+ for(TInt k=newListCount-1; k>=0; k--)
+ {
+ CHlpFileEntry* newEntry = list->At(k);
+
+ if (entry->FileName().CompareF(newEntry->FileName()) == 0)
+ {
+ // Names same, so nearest language file already in master list
+ __PRINT_FILE_NO(KDeleteEntry, *newEntry, k);
+ delete newEntry;
+ list->Delete(k);
+ }
+ }
+ }
+ }
+
+ // At this point, anything that is left in the new list should
+ // have it's nearest language file added to the master list...
+ newListCount = list->Count();
+ for(TInt k=newListCount-1; k>=0; k--)
+ {
+ //get nearest language file
+ __PRINT_FILE_NO(KGetNearestLanguageFile, *list->At(k), k);
+
+ TChar driveLetter = '?';
+ RFs::DriveToChar(list->At(k)->Drive(), driveLetter);
+
+ TFileName fileName;
+ fileName.Append(driveLetter);
+ fileName.Append(':');
+ fileName.Append(KHlpFileSearchPath);
+ fileName.Append(list->At(k)->Name());
+ fileName.Append(KDefaultHelpExtension);
+
+ BaflUtils::NearestLanguageFile(iFsSession, fileName);
+
+ //get drive and filename+extension of nearest language file
+ TParsePtrC parser(fileName);
+ if (!parser.ExtPresent())
+ User::Leave(KErrCorrupt);
+ TPtrC drive=parser.Drive();
+ TPtrC name=parser.NameAndExt();
+
+ //create new CHlpFileEntry
+ CHlpFileEntry* entry = CHlpFileEntry::NewLC(drive, name);
+ __PRINT_FILE(KAddNewEntryToMasterList, *entry);
+
+ //add to master list
+ masterList->AppendL(entry);
+ CleanupStack::Pop(entry);
+ }
+
+ list->ResetAndDestroy();
+ CleanupStack::PopAndDestroy(list);
+ }
+
+ if (i-- == EDriveZ)
+ break;
+ }
+ // The code below validates the masterList of help files and filters out unnecessary files based on the criteria specified below -
+ // i) In case of two files - abc.hlp and abc.h01 - present in the master list,since abc.h01 is the latest version, it
+ // will be picked. In other words abc.hlp will be removed from the list.
+ // ii) if two files for different language code exist, both should remain in masterList
+ // iii) if the system language is changed, the file as per the new language should be retained and
+ // default file with .hlp extension should be removed from the masterList
+ TLanguage language;
+ language = User::Language(); //gets the default language
+ TInt languageCode;
+ languageCode = language; // integer equivalent of the language code. For example, ELangEnglish is 01
+ for(TInt i=masterList->Count()-1; i >= 0; i--)
+ {
+ CHlpFileEntry* newEntry = masterList->At(i);
+ TPtrC ptrFile (newEntry->FileName());
+ TBufC<8> fileExtension(ptrFile.Right(2));
+ TLex lex(fileExtension);
+ TInt lexIntValue;
+ lex.Val(lexIntValue);
+ TParse parser;
+ TBool hlpFlag = ETrue;
+ User::LeaveIfError(parser.Set(ptrFile, NULL, NULL));
+
+ if(lexIntValue != language)
+ {
+ // Get the filename with language code in file extension
+ TBuf<256> fileName(newEntry->Name());
+ TBuf<10> name;
+ name.Format(_L(".h%02d"),languageCode);
+ fileName.Append(name);
+
+ // Get the filename with the .hlp extension
+ TBuf<256> defaultFileName(newEntry->Name());
+ TBuf<10> defaultName;
+ defaultName.Format(_L(".hlp"));
+ defaultFileName.Append(defaultName);
+
+ //Check the file against all the files in the list
+ for(TInt j = masterList->Count()-1; j >=0 ; j--)
+ {
+ CHlpFileEntry* entry = masterList->At(j);
+ // If two files with same name exist, delete one from the masterList
+ // and reset hlpFlag to EFalse
+ if(entry->FileName().CompareF(fileName)==0)
+ {
+ masterList->Delete(i);
+ hlpFlag = EFalse;
+ break;
+ }
+ }
+ if( (hlpFlag) && (parser.Ext().CompareF(KDefaultHelpExtension)!=0))
+ {
+ for(TInt k = masterList->Count()-1; k >=0 ; k--)
+ {
+ CHlpFileEntry* entry = masterList->At(k);
+ if(entry->FileName().CompareF(defaultFileName)==0)
+ {
+ masterList->Delete(i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Load the master list
+ TFileName file;
+ TInt count = masterList->Count();
+
+ for (i=0; i<count; i++)
+ {
+ masterList->At(i)->GetFullNameAndPath(file);
+ __PRINT_FILE_NO(KLoadMasterList, *masterList->At(i), i);
+
+ // This will leave with KErrArgument if it's a bad file
+ TRAPD(error, OpenFileL(file));
+ UNUSED_VAR(error); // used to suppress build warnings
+ }
+
+ CleanupStack::PopAndDestroy(masterList);
+ }
+
+TBool CHlpModel::DiskPresent(TInt aDrive) const
+ {
+ TDriveList list;
+ if (iFsSession.DriveList(list) < KErrNone)
+ return EFalse;
+
+ TDriveInfo info;
+ TInt error = iFsSession.Drive(info, aDrive);
+ if (error < KErrNone)
+ return EFalse;
+
+ return (list[aDrive] && info.iType != EMediaNotPresent);
+ }
+
+void CHlpModel::ResetAndDestroyArrayOfCHlpFileEntry(TAny* aObject)
+ {
+ CArrayPtr<CHlpFileEntry>* array=REINTERPRET_CAST(CArrayPtr<CHlpFileEntry>*,aObject);
+ if (array)
+ array->ResetAndDestroy();
+ delete array;
+ }
+
+CHlpFileList* CHlpModel::BuildListForDriveLC(TDriveUnit aDrive, RFs& aFsSession) const
+//
+// Generate a list of help files for the specified drive
+//
+ {
+ CHlpFileList* list = new(ELeave) CHlpFileList(5);
+ CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfCHlpFileEntry,list));
+
+ // Generate the folder spec to search in
+ TFileName searchSpec;
+ TChar driveLetter;
+ User::LeaveIfError(RFs::DriveToChar(aDrive, driveLetter));
+ searchSpec.Append(driveLetter);
+ searchSpec.Append(':');
+ searchSpec.Append(KHlpFileSearchPath);
+ searchSpec.Append(KHlpFileSpec);
+
+ CDir* dirList;
+
+ TFindFile finder(aFsSession);
+ TInt ret = finder.FindWildByPath(searchSpec, NULL, dirList);
+ if (ret < KErrNone)
+ {
+ if (ret == KErrNotFound)
+ return list;
+ else
+ User::Leave(ret);
+ }
+ CleanupStack::PushL(dirList);
+
+ // Add files to help file list
+ TInt dirCount = dirList->Count();
+ for(TInt i=0; i<dirCount; i++)
+ {
+ CHlpFileEntry* newEntry = CHlpFileEntry::NewLC(aDrive, (*dirList)[i].iName);
+ __PRINT_FILE_NO(KFoundEntry, *newEntry, i);
+
+ //check if new file already in the list
+ TBool foundMatch=EFalse;
+ TInt existingEntryCount=list->Count();
+ for(TInt j=existingEntryCount-1; j>=0; j--)
+ {
+ if(list->At(j)->Name().CompareF(newEntry->Name())==0)
+ {
+ __PRINT_FILE_NO(KDuplicateEntry, *newEntry, i);
+ foundMatch=ETrue;
+ }
+ }
+
+ if(!foundMatch)
+ {
+ __PRINT_FILE_NO(KAddNewEntry, *newEntry, i);
+ list->AppendL(newEntry);
+ CleanupStack::Pop(newEntry);
+ }
+ else
+ CleanupStack::PopAndDestroy(newEntry);
+ }
+ CleanupStack::PopAndDestroy(dirList);
+
+ return list;
+ }
+
+EXPORT_C void CHlpModel::CloseL()
+/** Closes all open help files. */
+ {
+ const TInt KDatabaseCount = DatabaseCount();
+ for (TInt i=0; i<KDatabaseCount; i++)
+ iDatabases->At(i)->Close();
+ iDatabases->ResetAndDestroy();
+ }
+
+
+//
+//
+//
+
+/**
+Opens a specified help file.
+
+@param aFileName Help file to open
+*/
+EXPORT_C void CHlpModel::OpenFileL(const TDesC& aFileName)
+ {
+ TEntry entry;
+ User::LeaveIfError(iFsSession.Entry(aFileName, entry));
+ if (entry.IsDir())
+ {
+ return; // don't try and open directories as help files ;)
+ }
+
+ TParsePtrC parser(aFileName);
+ if (parser.Path() != KHlpFileSearchPath)
+ { // given file is outside \\Resource\\Help\\ directory.
+ User::Leave(KErrArgument);
+ }
+
+ if (!(entry[0] == KPermanentFileStoreLayoutUid && entry[1] == KUidHlpApp))
+ {
+ User::Leave(KErrArgument);
+ }
+
+ CHlpDatabase* newDatabase = CHlpDatabase::NewLC(iFsSession, aFileName);
+ iDatabases->AppendL(newDatabase);
+ CleanupStack::Pop(newDatabase);
+ }
+
+EXPORT_C void CHlpModel::CloseFileL(const TDesC& aFileName)
+/** Closes a specified help file.
+
+@param aFileName Help file to close */
+ {
+ const TInt KDatabaseCount = DatabaseCount();
+ for (TInt i=0; i<KDatabaseCount; i++)
+ {
+ CHlpDatabase* database = iDatabases->At(i);
+ if (database->FileName() == aFileName)
+ {
+ delete database;
+ iDatabases->Delete(i);
+ return;
+ }
+ }
+ User::Leave(KErrNotFound);
+ }
+
+//
+//
+//
+
+EXPORT_C void CHlpModel::ContextSearchL(TCoeHelpContext& aContext)
+/** Searches for a topic with the specified help context.
+
+A successful search generates an ETopicAvailable event. The topic can then
+be retrieved using LoadTopicL().
+
+An unsuccessful search generates an ETopicNotFound event.
+
+@param aContext Help context to search for */
+ {
+ // Set up ready for search
+ ResetReadyForSearch();
+ SetSearchType(EContextSearch);
+
+ // See if we can find the specified context in any of the meta
+ // data that each database contains. This reduces the time taken to
+ // perform a search, as only the database which actually contains the
+ // correct information actually has the SQL executed on it (major time saver).
+ TInt db = MatchUidL(aContext.iMajor);
+ if (db >= KErrNone)
+ {
+ // Search the specified database for the context. If this context is
+ // found then the view will be prepared ready for the caller to extract
+ // the topic.
+ iCurrentDb = db;
+ TInt searchResult = CurrentDatabase()->ContextSearchL(aContext.iContext);
+ ReportEventToObserverL(searchResult);
+ }
+ else
+ {
+ ReportEventToObserverL(ETopicNotFound);
+ }
+ }
+
+EXPORT_C void CHlpModel::CategoryUIDSearchL(TUid aCategoryUID)
+/** Searches for topics with the specified help category UID.
+
+A successful search generates an ETopicListAvailable event. The list can then
+be retrieved using LoadListL().
+
+An unsuccessful search generates an ETopicListNoneFound event.
+
+@param aCategoryUID Category UID to search for */
+ {
+ // Set up ready for search
+ ResetReadyForSearch();
+ SetSearchType(ETopicListForCategoryUID);
+
+ // See if we can find the specified category Uid in any of the meta
+ // data that each database contains. This reduces the time taken to
+ // perform a search, as only the database which actually contains the
+ // correct information actually has the SQL executed on it (major time saver).
+ TInt db=MatchUidL(aCategoryUID);
+ if (db >= KErrNone)
+ {
+ iTransientCategoryUid = aCategoryUID;
+ iCurrentDb=db;
+
+ TBuf<32> buf;
+ buf.Num(STATIC_CAST(TUint, aCategoryUID.iUid));
+ DoSearchL(ETopicListForCategoryUID, buf);
+
+ // Reset the category uid after we've done the search (used in
+ // debug invariant state check)
+ iTransientCategoryUid = KNullUid;
+ }
+ else
+ {
+ ReportEventToObserverL(ETopicNotFound);
+ }
+ }
+
+EXPORT_C void CHlpModel::TopicSearchL(const CHlpItem& aHelpItem)
+/** Searches for a topic for the specified help item.
+
+A successful search generates an ETopicAvailable event. The topic can then
+be retrieved using LoadTopicL().
+
+An unsuccessful search generates an ETopicNotFound event.
+
+@param aHelpItem Help item to search for */
+ {
+ // aHelpItem contains the information required for a speedy retrieval
+ // of the specified topic from the help model. Using it's topic Id (iId)
+ // category uid (CategoryUid()) and help file uid (HelpFileUid()) we
+ // can run the search SQL on exactly the right database file, for exactly
+ // the right category, and exactly the right topic.
+
+ // if already searching, dont start another search
+ if(iFound && CurrentSearchType()==ETopicIdSearch)
+ return;
+
+ // Set up ready for search
+ ResetReadyForSearch();
+ SetSearchType(ETopicIdSearch);
+
+ TInt count = DatabaseCount();
+ for(TInt i=0; i<count; i++)
+ {
+ CHlpDatabase* database = iDatabases->At(i);
+ if (database->HelpFileUid() == aHelpItem.HelpFileUid())
+ {
+ // Run the SQL on this database.
+ iCurrentDb = i;
+ TInt result = database->TopicIdSearchL(aHelpItem.CategoryUid(), STATIC_CAST(TUint, aHelpItem.iId));
+ iFound = (result == ETopicAvailable);
+ ReportEventToObserverL(result);
+ return;
+ }
+ }
+ ReportEventToObserverL(ETopicNotFound);
+ }
+
+EXPORT_C void CHlpModel::IndexSearchL(const CHlpItem& aHelpItem)
+/** Searches for index entries for the specified help item.
+
+A successful search generates an ETopicListAvailable event. The list can then
+be retrieved using LoadListL().
+
+An unsuccessful search generates an ETopicListNoneFound event.
+
+@param aHelpItem Help item to search for */
+ {
+ // Set up ready for search
+ ResetReadyForSearch();
+ SetSearchType(ETopicIdSearch);
+
+ TInt count = DatabaseCount();
+ for(TInt i=0; i<count; i++)
+ {
+ CHlpDatabase* database = iDatabases->At(i);
+ if (database->HelpFileUid() == aHelpItem.HelpFileUid())
+ {
+ // Run the SQL on this database.
+ iCurrentDb = i;
+
+ TBuf<KHlpModelNumericValueSize> buf;
+ buf.Num(STATIC_CAST(TUint, aHelpItem.iId));
+ DoSearchL(EIndexSearch, buf);
+
+ return;
+ }
+ }
+ ReportEventToObserverL(EIndexSearchListNoneFound);
+ }
+
+//
+//
+//
+
+EXPORT_C void CHlpModel::SearchL(TInt aType, TUint32 aId)
+/** Searches using a specified type of search with a numeric criterion.
+
+@param aType Type of search. This is a search type (EIndexList etc.) enum
+value.
+@param aId Numeric search criterion */
+ {
+ if (aType == ETopicListForCategoryUID)
+ iTransientCategoryUid = TUid::Uid(aId);
+
+ TBuf<KHlpModelNumericValueSize> buf;
+ buf.Num(STATIC_CAST(TUint, aId));
+ SearchL(aType, buf);
+ }
+
+EXPORT_C void CHlpModel::SearchL(TInt aType, HBufC* aCriterion)
+/** Searches using a specified type of search, allowing a NULL seach criterion.
+
+@param aType Type of search. This is a search type (EIndexList etc.) enum
+value.
+@param aCriterion String search criterion. This can be NULL if the search type
+requires no search criterion. */
+ {
+ // Kludge to remain source compatible in App Engines 5.2
+ // This function should be removed.
+ if (aCriterion)
+ SearchL(aType, *aCriterion);
+ else
+ SearchL(aType, KNullDesC);
+ }
+
+EXPORT_C void CHlpModel::SearchL(TInt aType, const TDesC& aCriterion)
+//
+// This function is the base of all the SearchL(...) methods, i.e. it is callled
+// by SearchL(TInt, HBufC*) and also SearchL(TInt, TUint32)
+//
+/** Searches using a specified type of search with a string search criterion.
+
+Note that the full text search, EFullTextSearch, is asynchronous.
+
+@param aType Type of search. This is a search type (EIndexList etc.) enum
+value.
+@param aCriterion String search criterion */
+ {
+ // Set up ready for search
+ ResetReadyForSearch();
+
+ // Do the actual search itself.
+ DoSearchL(aType, aCriterion);
+ }
+
+EXPORT_C TInt CHlpModel::CancelSearch()
+/** Cancels a full text search.
+
+@return KErrNone if successful, KErrArgument if a search is not in progress */
+ {
+ iSearch->CancelEvaluator();
+
+ if (CurrentSearchType() != EFullTextSearch)
+ return KErrArgument;
+
+ return KErrNone;
+ }
+
+//
+//
+//
+
+EXPORT_C void CHlpModel::LoadTopicL(CRichText& aRichText, TDes& aTitle)
+/** Gets the current help topic text and title.
+
+The function assumes that a successful search has already been performed.
+
+@param aRichText On return, the help topic text
+@param aTitle On return, the help topic title */
+ {
+ // Fetch the rich text
+ LoadTopicL(aRichText);
+
+ // Next get the title
+ RDbView& view = CurrentDatabase()->View();
+ __ASSERT_ALWAYS(view.AtRow(), Panic(EHlpNoRowAtCursor));
+ CDbColSet* colset = view.ColSetL();
+ TDbColNo topicTitle = colset->ColNo(KSQLTopicTitleColumn);
+ delete colset;
+
+ view.FirstL();
+ view.GetL();
+ aTitle = view.ColDes(topicTitle);
+ }
+
+EXPORT_C void CHlpModel::LoadTopicL(CRichText& aRichText)
+/** Gets the current help topic text.
+
+The function assumes that a successful search has already been performed.
+
+@param aRichText On return, the help topic text */
+ {
+ RDbView& view = CurrentDatabase()->View();
+ __ASSERT_ALWAYS(view.CountL(), Panic(EHlpTopicNoRowsInView));
+
+ CDbColSet* colset = view.ColSetL();
+ TDbColNo topicCol = colset->ColNo(KSQLTopicTextColumn);
+ TDbColNo markupCol = colset->ColNo(KSQLTopicMarkupColumn);
+ delete colset;
+ view.FirstL();
+ view.GetL();
+
+ aRichText.Reset();
+ TInt len = view.ColLength(topicCol);
+ HBufC* buf = HBufC::NewLC(len);
+ TPtr pBuf(buf->Des());
+
+ RDbColReadStream stream;
+ stream.OpenLC(view, topicCol);
+ stream.ReadL(pBuf, len);
+ aRichText.InsertL(0, pBuf);
+ CleanupStack::PopAndDestroy(2); // stream & buf
+
+ if (!view.IsColNull(markupCol))
+ {
+ RDbColReadStream blob;
+ blob.OpenL(view, markupCol);
+
+ aRichText.SetPictureFactory(this, this);
+ CEmbeddedStore* embeddedStore = CEmbeddedStore::FromLC(blob);
+ iCurrentRichTextStore = embeddedStore;
+
+ RStoreReadStream readStream;
+ readStream.OpenLC(*embeddedStore, embeddedStore->Root());
+ aRichText.InternalizeMarkupDataL(readStream);
+ aRichText.DetachFromStoreL(CPicture::EDetachFull);
+ CleanupStack::PopAndDestroy(2); // embeddedStore, readStream
+ aRichText.SetPictureFactory(NULL, NULL);
+ iCurrentRichTextStore = NULL;
+ }
+ }
+
+EXPORT_C void CHlpModel::LoadTopicL(CHlpTopic* aTopic)
+/** Gets the current help topic.
+
+The function assumes that a successful search has already been performed.
+
+@param aTopic On return, the help topic. This is caller allocated. */
+ {
+ __ASSERT_ALWAYS(aTopic, Panic(EHlpNoTopic));
+
+ LoadTopicL(*(aTopic->TopicText()));
+
+ RDbView& view = CurrentDatabase()->View();
+ CDbColSet* colset = view.ColSetL();
+ TDbColNo topicTitle = colset->ColNo(KSQLTopicTitleColumn);
+ TDbColNo topicId = colset->ColNo(KSQLTopicIdColumn);
+ TDbColNo category = colset->ColNo(KSQLCategoryColumn);
+ delete colset;
+
+ // Populate the topic's
+ aTopic->iTopicTitle = view.ColDes(topicTitle);
+ aTopic->iTopicId = view.ColUint32(topicId);
+ aTopic->iCategory = view.ColDes(category);
+ }
+
+EXPORT_C void CHlpModel::LoadListL(CHlpList* aList)
+/** Gets the current help list.
+
+The function assumes that a successful search has already been performed.
+
+@param aList On return, the help list. This is caller allocated. */
+ {
+ __ASSERT_ALWAYS(aList, Panic(EHlpNoHelpList));
+
+ // Clear all entries in the list
+ aList->Reset();
+
+ // This loop provides the mechanism by which we iterate through every database
+ // in the help model. We query each database in turn to find out a) if it has
+ // been searched in the first place (not every search results in *every* help
+ // file's database view being initialised) and b) if it actually contains any
+ // results.
+ const TInt KDatabaseCount = DatabaseCount();
+ for (TInt i=0; i < KDatabaseCount; i++)
+ {
+ // Get a help database pointer and it's view.
+ CHlpDatabase* currentDatabase = iDatabases->At(i);
+ RDbView& view = currentDatabase->View();
+
+ // Does this database meet the criteria described above (points a & b)?
+ if (!currentDatabase->ViewReady() || !view.CountL())
+ continue; // Doesn't meet criteria, so skip this database
+
+ // Criteria satisifed, so look up the column id's we're interested in.
+ CDbColSet* colset = view.ColSetL();
+ TDbColNo colTitle = colset->ColNo(KSQLTopicTitleColumn);
+ TDbColNo colTopicId = colset->ColNo(KSQLTopicIdColumn);
+ TDbColNo colCategoryId = colset->ColNo(KSQLCategoryUidColumn);
+ TDbColNo colIndex = colset->ColNo(KSQLIndexColumn);
+ TDbColNo colIndexId = colset->ColNo(KSQLIndexIdColumn);
+ delete colset;
+
+ // Each topic is associated with a particular category. This information
+ // is cached with the help item so that we can ensure we restore the
+ // topic relating to the category searched (topic id's are not unique, they
+ // are assigned in increasing numerical order by the help compiler, on a
+ // category basis. Category ids, however, are unique, hence we need to store
+ // both in order to be able to restore the correct topic text).
+ view.FirstL();
+ view.GetL();
+
+ // Iterate through each row in this database's view, and store each entry
+ // as a help item in the list passed as a parameter to this function.
+ FOREVER
+ {
+ CHlpItem* item = NULL;
+
+ // This bit loads different content into the help item depending on the last performed
+ // search type.
+ switch (iSearchType)
+ {
+ case ECategoryList:
+ case ETopicListForCategory:
+ case ETopicListForCategoryUID:
+ item = CHlpItem::NewLC(view.ColDes(colTitle), view.ColUint32(colTopicId), TUid::Uid(view.ColUint(colCategoryId)), currentDatabase->HelpFileUid());
+ break;
+ case EIndexList:
+ item = CHlpItem::NewLC(view.ColDes(colIndex), view.ColUint32(colIndexId), currentDatabase->HelpFileUid());
+ break;
+ case EQuickSearch:
+ case EFullTextSearch:
+ case EIndexSearch:
+ item = CHlpItem::NewLC(view.ColDes(colTitle), view.ColUint32(colTopicId), TUid::Uid(view.ColUint(colCategoryId)), currentDatabase->HelpFileUid());
+ break;
+ default:
+ User::Leave(KErrNotSupported);
+ }
+
+ aList->AppendL(item);
+ CleanupStack::Pop(item);
+ view.NextL();
+ if (view.AtEnd())
+ break;
+ view.GetL();
+ }
+ }
+ }
+
+EXPORT_C void CHlpModel::CategoryListL(CDesCArray* aList)
+/** Populates a list of available help categories.
+
+This can be called without needing to perform a prior search.
+
+@param aList On return, the list of available help categories. This is caller
+allocated. */
+ {
+ __ASSERT_ALWAYS(aList, Panic(EHlpNoCategoryList));
+
+ const TInt count = DatabaseCount();
+ for(TInt i=0; i<count; i++)
+ iDatabases->At(i)->AppendCategoryListL(*aList);
+ aList->Sort();
+ }
+
+//
+//
+//
+
+EXPORT_C void CHlpModel::SetZoomSizeL(THlpZoomState aState)
+/** Sets the zoom size to use for help text and pictures.
+
+@param aState Zoom size */
+ {
+ iZoomSize = aState;
+ const TInt count = iPictures->Count();
+ for (TInt i=0; i<count; i++)
+ {
+ // Fetch a picture from the array
+ CHlpPicture* picture = iPictures->At(i);
+ picture->HandleZoomChangedL(iZoomSize);
+ }
+ }
+
+EXPORT_C THlpZoomState CHlpModel::ZoomSize() const
+/** Gets the zoom size used for help text and pictures.
+
+@return Zoom size */
+ {
+ return iZoomSize;
+ }
+
+/* Sets the iZoomFactors array to the appropriate
+zoom factor value, depending on the current zoom state
+as dictated by the aZoomState argument variable.
+
+The hlpmodel's THlpZoomState enumerated type is related to
+the AppUI's TZoomStates enumerated type. This relation is required
+so that the correct pixels to twips ratio can be used in the
+CHlpPicture::GetOriginalSizeInTwips() method.
+
+Error Condition : None.
+@param aZoomState A zoom size.
+@param aFactor The zoom factor that corresponds to the current zoom size.
+@pre None.
+@post The iZoomFactors data member has been populated with the appropriate
+zoom factor values. */
+EXPORT_C void CHlpModel::SetZoomFactors(THlpZoomState aZoomState, TInt aFactor)
+/** Sets a zoom factor for a logical zoom size.
+
+@param aZoomState Logical zoom size for which to set the factor.
+@param aFactor Zoom factor. For example, 2 specifies double size. */
+ {
+ __ASSERT_ALWAYS(iZoomFactors->Count() == 3, Panic(EHlpNotEnoughZoomRatios));
+ //
+ switch(aZoomState)
+ {
+ /*
+ * If zoom state is large, assign zoom factor 750
+ * to index 0 of 'iZoomFactors'
+ */
+ case EHlpZoomStateSmall:
+ iZoomFactors->At(EHlpZoomStateSmall) = aFactor;
+ break;
+
+ default:
+ /*
+ * If zoom state is large, assign zoom factor 1000
+ * to index 1 of 'iZoomFactors'
+ */
+ case EHlpZoomStateMedium:
+ iZoomFactors->At(EHlpZoomStateMedium) = aFactor;
+ break;
+ /*
+ * If zoom state is large, assign zoom factor 1250
+ * to index 2 of 'iZoomFactors'
+ */
+ case EHlpZoomStateLarge:
+ iZoomFactors->At(EHlpZoomStateLarge) = aFactor;
+ break;
+ }
+ }
+
+/*
+Accesses the iZoomFactors data member, and returns
+the current zoom factor, depending on the value of
+the current zoom state, which is to be used for the
+pixel scaling of a picture in CHlpPicture::GetOriginalSizeInTwips().
+Error Condition : None
+
+@return The zoom factor, corresponding to the current zoom size.
+@pre None.
+@post Current zoom factor is returned. */
+TInt CHlpModel::CurrentZoomFactor() const
+ {
+ __ASSERT_ALWAYS(iZoomFactors->Count() == 3, Panic(EHlpNotEnoughZoomRatios));
+ //
+ switch (iZoomSize)
+ {
+ case EHlpZoomStateSmall:
+ return iZoomFactors->At(EHlpZoomStateSmall);
+ default:
+ case EHlpZoomStateMedium:
+ return iZoomFactors->At(EHlpZoomStateMedium);
+ case EHlpZoomStateLarge:
+ return iZoomFactors->At(EHlpZoomStateLarge);
+ }
+ }
+
+/* Removes a CHlpPicture from the iPictures array.
+
+Error Condition : None
+
+@param aHelpPicture A pointer to a CHlpPicture object.
+@pre A valid CHlpPicture is passed to the function.
+@post CHlpPicture's that are no longer displayed have been removed
+from the iPictures data member. */
+void CHlpModel::RemoveHelpPicture(CHlpPicture* aHelpPicture)
+ {
+ if (iPictures != 0)
+ {
+ const TInt count = iPictures->Count();
+ __ASSERT_DEBUG(count > 0, User::Invariant());
+ //
+ for(TInt i=0; i<count; i++)
+ {
+ if (aHelpPicture == iPictures->At(i))
+ {
+ iPictures->Delete(i);
+ return;
+ }
+ }
+ __ASSERT_DEBUG(EFalse, Panic(EHlpUnlocatedHelpPicture));
+ }
+ }
+
+
+//
+//
+//
+
+/* This is a mixin callback required to restore pictures
+from the help model database into the topic rich text.
+Because pictures are stored in their own picture table within the
+help database (this means that the same picture may be used many
+times within the rich text, but will only actually appear once
+in the database - a major space saver) the restoration function
+must have a primed view so that it knows which database to use
+as the source picture table.
+
+@param aHdr A reference to a picture header.
+@param aDeferredPictureStore A stream store where the pictures are kept.
+@pre A valid picture database must exist, so that the stream store can relate to.
+@post A TInt pointer (index) to the picture table is inserted
+in the rich text to indicate which picture needs restoring.
+The CHlpPicture looks up this index in the picture table. */
+void CHlpModel::NewPictureL(TPictureHeader& aHeader, const CStreamStore& aDeferredPictureStore) const
+ {
+ if (aHeader.iPictureType != KUidHelpImage)
+ User::Leave(KErrNotSupported);
+ if (!aHeader.iPicture.IsId())
+ User::Leave(KErrBadHandle);
+
+ TStreamId id = aHeader.iPicture.AsId();
+ CHlpPicture* picture = CHlpPicture::NewLC(aDeferredPictureStore, id, *CurrentDatabase(), *const_cast<CHlpModel*>(this));
+ aHeader.iPicture = picture;
+
+ // Add picture to the picture array. We need to do this so that we can update the picture
+ // when the zoom size is changed by the UI.
+ iPictures->AppendL(picture);
+
+ CleanupStack::Pop(picture);
+ }
+
+EXPORT_C const CStreamStore& CHlpModel::StreamStoreL(TInt /*aPos*/) const
+/** Gets the current rich text store.
+
+@param aPos Unused
+@return Current rich text store */
+ {
+ __ASSERT_ALWAYS(iCurrentRichTextStore, Panic(EHlpNoPictureStore));
+ return *iCurrentRichTextStore;
+ }
+
+//
+//
+//
+
+EXPORT_C TInt CHlpModel::MatchUidL(TUid aUid)
+/** Searches the open help databases for the specified topic UID.
+
+@param aUid Topic UID to search for
+@return Index of the database if the item was found, or KErrNotFound if not */
+ {
+ const TInt KDatabaseCount = DatabaseCount();
+ for (TInt i=0; i<KDatabaseCount; i++)
+ {
+ if (iDatabases->At(i)->MatchUidL(aUid))
+ return i;
+ }
+ return KErrNotFound;
+ }
+
+EXPORT_C void CHlpModel::SetObserver(MHlpModelObserver* aObserver)
+/** Sets the client callback interface.
+
+@param aObserver Client callback interface */
+ {
+ iObserver = aObserver;
+ }
+
+//
+//
+//
+
+void CHlpModel::DoSearchL(TInt aType, const TDesC& aCriterion)
+ {
+ // Initialise the member data with the type of search we are about to perform,
+ // and also setup the search criteria which is needed to peform multiple searches
+ // across databases.
+ SetSearchType(aType);
+ SetCriterionL(aCriterion);
+
+ if (!DatabaseCount())
+ {
+ ReportEventToObserverL(ETopicNotFound);
+ return;
+ }
+
+ // Prepare the searcher with the database to search
+ iSearch->SetDatabase(*CurrentDatabase());
+
+ // Indicate that this database is being searched, and therefore it is guaranteed not
+ // to have a null view
+ CurrentDatabase()->SetViewReady(ETrue);
+
+ // Get the searcher to actually do the search, which includes
+ // building the necessary SQL statement and then running the SQL on the
+ // correct table.
+ iSearch->SearchL(aType, *iCriterion);
+ }
+
+void CHlpModel::DoNextSearchL()
+//
+// This function is used to perform an incremental search (mirroring an inner join which
+// EPOC DBMS doesn't support) across multiple databases.
+//
+ {
+ if (iCurrentDb < DatabaseCount()-1)
+ {
+ // If we're peforming a category Uid-based search, then
+ // we only run the sql seach on the databases that actually
+ // hold meta data on the specified category.
+ iCurrentDb++;
+ if (CurrentSearchType() == ETopicListForCategoryUID)
+ {
+ __ASSERT_DEBUG(iTransientCategoryUid != KNullUid, Panic(EHlpFault));
+ TInt numberOfMatches = 0;
+ do
+ {
+ if (CurrentDatabase()->MatchUidL(iTransientCategoryUid))
+ {
+ // This database does have meta data on the specified category,
+ // so it's worth running the check
+ DoSearchL(CurrentSearchType(), *iCriterion);
+ ++numberOfMatches;
+ }
+ }
+ while (++iCurrentDb < DatabaseCount());
+ if (numberOfMatches == 0)
+ ReportEventToObserverL(ESearchComplete);
+ }
+ else
+ {
+ // Doesn't matter what type of search it is, we still
+ // run the SQL :(
+ DoSearchL(CurrentSearchType(), *iCriterion);
+ }
+ }
+ else
+ {
+ ReportEventToObserverL(ESearchComplete);
+ }
+ }
+
+void CHlpModel::ResetReadyForSearch()
+//
+// This function is called regardless of search type - it prepares all the necessary
+// variables ready for a search.
+//
+ {
+ // Set to the first database
+ iCurrentDb=0;
+
+ // Indicate that we've currently not found any results
+ iFound=EFalse;
+
+ // Reset the database views to 'not yet ready'
+ ResetViews();
+ }
+
+void CHlpModel::ResetViews()
+//
+// Go through each database and reset the "view ready" flag to EFalse
+// to indicate that this particular database's view has not yet been primed.
+//
+ {
+ const TInt count = DatabaseCount();
+ for(TInt i=0; i<count; i++)
+ iDatabases->At(i)->SetViewReady(EFalse); // not ready
+ }
+
+void CHlpModel::SetCriterionL(const TDesC& aCriterion)
+//
+// Updates the internal iCriterion pointer to contain the new
+// criteria for searching.
+//
+ {
+ HBufC* newCriteria = aCriterion.AllocL();
+ delete iCriterion;
+ iCriterion = newCriteria;
+ }
+
+RDbView* CHlpModel::CurrentView() const
+ {
+ return &(CurrentDatabase()->View());
+ }
+
+//
+//
+//
+
+void CHlpModel::HandleDbEventL(TInt aEvent)
+//
+// Called by the SQL searcher. This function routes responses from the searcher
+// to the model observer, and performs and recursive searching that is required.
+//
+ {
+ switch(aEvent)
+ {
+ case ENoRecordsFound:
+ // No records were found for this search. If a context search was requested,
+ // we indicate that nothing was found and let the observer of the database
+ // perform any action. Otherwise, we search the next database in turn.
+ if (CurrentSearchType() != ETopicIdSearch &&
+ CurrentSearchType() != EContextSearch &&
+ CurrentSearchType() != EIndexSearch
+ )
+ DoNextSearchL();
+ else
+ ReportEventToObserverL(ENoRecordsFound);
+ break;
+ case ESearchComplete:
+ // Indicate that at least some matching critera was found and then
+ // either end the search, or search the next database.
+ iFound=ETrue;
+ if (CurrentSearchType() != ETopicIdSearch &&
+ CurrentSearchType() != EContextSearch &&
+ CurrentSearchType() != EIndexSearch
+ )
+ DoNextSearchL();
+ else
+ ReportEventToObserverL(ESearchComplete);
+ break;
+ case ESearchInProgress:
+ // Indicate to the observer that a search is in progress
+ ReportEventToObserverL(ESearchInProgress);
+ break;
+ case EHlpSearchCancelled:
+ // Indicate to the observer that a search was cancelled
+ ReportEventToObserverL(EHlpSearchCancelled);
+ break;
+ default:
+ __ASSERT_DEBUG(EFalse, Panic(EHlpFault));
+ break;
+ }
+ }
+
+void CHlpModel::ReportEventToObserverL(TInt aEvent)
+ {
+ if (!iObserver)
+ return; // can't do anything without an observer
+
+ switch(aEvent)
+ {
+ case ESearchInProgress:
+ iObserver->HandleModelEventL(EModelSearchInProgress);
+ return;
+ case EHlpSearchCancelled:
+ iObserver->HandleModelEventL(EHlpSearchCancelled);
+ return;
+ default: // Keep GCC happy
+ break;
+ }
+
+ switch(CurrentSearchType())
+ {
+ case EIndexList:
+ iObserver->HandleModelEventL((iFound?EIndexListAvailable:EIndexListNoneFound));
+ break;
+ case ECategoryList:
+ iObserver->HandleModelEventL((iFound?ECategoryListAvailable:ECategoryListNoneFound));
+ break;
+ case ETopicListForCategory:
+ case ETopicListForCategoryUID:
+ iObserver->HandleModelEventL((iFound?ETopicListAvailable:ETopicListNoneFound));
+ break;
+ case EContextSearch:
+ iObserver->HandleModelEventL(aEvent);
+ break;
+ case EIndexSearch:
+ iObserver->HandleModelEventL((iFound?EIndexSearchListAvailable:EIndexSearchListNoneFound));
+ break;
+ case EQuickSearch:
+ case EFullTextSearch:
+ iObserver->HandleModelEventL((iFound?ESearchListAvailable:ESearchListNoneFound));
+ break;
+ case ETopicIdSearch:
+ iObserver->HandleModelEventL((iFound?ETopicAvailable:ETopicNotFound));
+ // event has been reported, reset now.
+ iFound=EFalse;
+ break;
+ default:
+ __ASSERT_DEBUG(EFalse, Panic(EHlpFault));
+ break;
+ }
+ }
+
+
+
+
+
+
+
+
+//
+// ----> CHlpList
+//
+
+EXPORT_C CHlpList::~CHlpList()
+/** Destructor. */
+ {
+ if(iList)
+ iList->ResetAndDestroy();
+ delete iList;
+ }
+
+void CHlpList::ConstructL()
+ {
+ iList = new(ELeave) CArrayPtrFlat<CHlpItem>(2);
+ }
+
+EXPORT_C CHlpList* CHlpList::NewL()
+/** Allocates and creates a new help list object.
+
+@return New help list object */
+ {
+ CHlpList* self = CHlpList::NewLC();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+EXPORT_C CHlpList* CHlpList::NewLC()
+/** Allocates and creates a new help list object, leaving the object on the cleanup
+stack.
+
+@return New help list object */
+ {
+ CHlpList* self = new(ELeave) CHlpList;
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+EXPORT_C TInt CHlpList::MdcaCount() const
+/** Gets the number of items in the list.
+
+@return Number of items in the list */
+ {
+ return iList->Count();
+ }
+
+EXPORT_C TPtrC CHlpList::MdcaPoint(TInt aIndex) const
+/** Gets the title of the item at the specified index.
+
+@param aIndex Item index
+@return Title of the item */
+ {
+ return TPtrC(iList->At(aIndex)->iTitle->Des());
+ }
+
+EXPORT_C TUint32 CHlpList::At(TInt aIndex) const
+/** Gets the topic ID of the item at the specified index.
+
+@param aIndex Item index
+@return Topic ID */
+ {
+ return iList->At(aIndex)->iId;
+ }
+
+EXPORT_C void CHlpList::Reset()
+/** Resets the list. */
+ {
+ iList->ResetAndDestroy();
+ }
+
+EXPORT_C CHlpItem* CHlpList::Item(TInt aIndex) const
+/** Gets the item at the specified index.
+
+@param aIndex Item index
+@return Item */
+ {
+ return iList->At(aIndex);
+ }
+
+EXPORT_C TInt CHlpList::Find(TUint32 aId)
+/** Searches the list for a specified item ID.
+
+@param aId Item ID
+@return Item index, or KErrNotFound if not found */
+ {
+ CHlpItem* item = new CHlpItem(aId);
+ if (!item)
+ return KErrNoMemory;
+
+ TKeyArrayFix key(_FOFF(CHlpItem, iId), ECmpTUint32);
+ TInt pos;
+ TInt result = iList->Find(item, key, pos);
+ delete item;
+
+ if (!result)
+ return pos;
+ else
+ return KErrNotFound;
+ }
+
+EXPORT_C void CHlpList::AppendL(CHlpItem* aItem)
+/** Appends an item to the list.
+
+@param aItem Item to add */
+ {
+ __ASSERT_ALWAYS(aItem, Panic(EHlpNoItem));
+ iList->AppendL(aItem);
+ }
+
+
+
+
+
+//
+// ----> CHlpItem - representing an individual item in the help file
+//
+
+CHlpItem::CHlpItem(TUint32 aId)
+: iId(aId), iCategoryUid(KNullUid), iHelpFileUid(KNullUid)
+ {
+ }
+
+CHlpItem::CHlpItem(TUint32 aId, TUid aHelpFileUid)
+: iId(aId), iCategoryUid(KNullUid), iHelpFileUid(aHelpFileUid)
+ {
+ }
+
+CHlpItem::CHlpItem(TUint32 aId, TUid aCategoryId, TUid aHelpFileUid)
+: iId(aId), iCategoryUid(aCategoryId), iHelpFileUid(aHelpFileUid)
+ {
+ }
+
+EXPORT_C CHlpItem::~CHlpItem()
+/** Destructor. */
+ {
+ delete iTitle;
+ }
+
+void CHlpItem::ConstructL(const TDesC& aTitle)
+ {
+ iTitle = aTitle.AllocL();
+ }
+
+CHlpItem* CHlpItem::NewL(const TDesC& aTitle, TUint32 aId, TUid aCategoryId, TUid aHelpFileUid)
+/** Allocates and creates a new help item object.
+
+@param aTitle Item title
+@param aId Item ID
+@param aCategoryId Category ID
+@param aHelpFileUid Help file UID
+@return New help item object */
+ {
+ CHlpItem* self = NewLC(aTitle, aId, aCategoryId, aHelpFileUid);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CHlpItem* CHlpItem::NewLC(const TDesC& aTitle, TUint32 aId, TUid aCategoryId, TUid aHelpFileUid)
+/** Allocates and creates a new help item object, leaving the object on the cleanup
+stack.
+
+@param aTitle Item title
+@param aId Item ID
+@param aCategoryId Category ID
+@param aHelpFileUid Help file UID
+@return New help item object */
+ {
+ CHlpItem* self = new(ELeave) CHlpItem(aId, aCategoryId, aHelpFileUid);
+ CleanupStack::PushL(self);
+ self->ConstructL(aTitle);
+ return self;
+ }
+
+CHlpItem* CHlpItem::NewLC(const TDesC& aTitle, TUint32 aId, TUid aHelpFileUid)
+/** Allocates and creates a new help item object, leaving the object on the cleanup
+stack.
+
+This overload does not specify a category UID.
+
+@param aTitle Item title
+@param aId Item ID
+@param aHelpFileUid Help file UID
+@return New help item object */
+ {
+ CHlpItem* self = new(ELeave) CHlpItem(aId, aHelpFileUid);
+ CleanupStack::PushL(self);
+ self->ConstructL(aTitle);
+ return self;
+ }
+
+
+
+
+
+
+//
+// ----> CHlpTopic
+//
+
+EXPORT_C CHlpTopic::~CHlpTopic()
+ {
+ delete iTopicText;
+ delete iGlobalCharFormatLayer;
+ delete iGlobalParaFormatLayer;
+ }
+
+void CHlpTopic::ConstructL()
+ {
+ // Create the necessary formatting layers for the rich text object.
+ iGlobalParaFormatLayer = CParaFormatLayer::NewL();
+ iGlobalCharFormatLayer = CCharFormatLayer::NewL();
+ iTopicText = CRichText::NewL(iGlobalParaFormatLayer, iGlobalCharFormatLayer);
+ }
+
+EXPORT_C CHlpTopic* CHlpTopic::NewL()
+/** Allocates and creates a new help topic object.
+
+@return New help topic object */
+ {
+ CHlpTopic* self = CHlpTopic::NewLC();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+EXPORT_C CHlpTopic* CHlpTopic::NewLC()
+/** Allocates and creates a new help topic object, leaving the object on the cleanup
+stack.
+
+@return New help topic object */
+ {
+ CHlpTopic* self = new(ELeave) CHlpTopic();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+EXPORT_C void CHlpTopic::RestoreL(RDbView* aView)
+/** Restores the object from a database view.
+
+This only restores text and markup, not pictures. Help application authors
+should use CHlpModel::LoadTopicL() instead.
+
+@param aView Database view */
+ {
+ // NOTE: This function does not restore pictures because it does not know
+ // which help model database to use as the source for the picture table.
+ // Use CHlpModel::LoadTopicL(...) instead
+ __ASSERT_ALWAYS(aView, Panic(EHlpNoView));
+ __ASSERT_ALWAYS(aView->AtRow(), Panic(EHlpNoRowAtCursor));
+
+ aView->FirstL();
+ CDbColSet* colset = aView->ColSetL();
+ TDbColNo topicCol = colset->ColNo(KSQLTopicTextColumn);
+ TDbColNo markupCol = colset->ColNo(KSQLTopicMarkupColumn);
+ TDbColNo titleCol = colset->ColNo(KSQLTopicTitleColumn);
+ TDbColNo catCol = colset->ColNo(KSQLCategoryColumn);
+ TDbColNo idCol = colset->ColNo(KSQLTopicIdColumn);
+ delete colset;
+
+ aView->GetL();
+
+ TInt len = aView->ColLength(topicCol);
+ HBufC* buf=HBufC::NewLC(len);
+ TPtr pBuf(buf->Des());
+
+ RDbColReadStream stream;
+ stream.OpenLC(*aView, topicCol);
+ stream.ReadL(pBuf, len);
+ iTopicText->Reset();
+ iTopicText->InsertL(0, *buf);
+ CleanupStack::PopAndDestroy(2); // stream, buf
+
+ if (!aView->IsColNull(markupCol))
+ {
+ RDbColReadStream blob;
+ blob.OpenL(*aView, markupCol);
+ CEmbeddedStore* embeddedStore = CEmbeddedStore::FromLC(blob);
+ RStoreReadStream readStream;
+ readStream.OpenLC(*embeddedStore, embeddedStore->Root());
+ iTopicText->InternalizeMarkupDataL(readStream);
+ CleanupStack::PopAndDestroy(2); // embeddedStore, readStream
+ }
+
+ iCategory.Append(aView->ColDes(catCol));
+ iTopicTitle.Append(aView->ColDes(titleCol));
+ iTopicId=aView->ColUint32(idCol);
+ }
+
+EXPORT_C CRichText* CHlpTopic::TopicText()
+/** Gets the topic text.
+
+@return Topic text */
+ {
+ return iTopicText;
+ }
+
+EXPORT_C TDesC& CHlpTopic::TopicTitle()
+/** Gets the topic title.
+
+@return Topic title */
+ {
+ return iTopicTitle;
+ }
+
+EXPORT_C TDesC& CHlpTopic::Category()
+/** Gets the topic category.
+
+@return Topic category */
+ {
+ return iCategory;
+ }