diff -r 000000000000 -r 2f259fa3e83a commonuis/CommonUi/src/ItemFinder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commonuis/CommonUi/src/ItemFinder.cpp Tue Feb 02 01:00:49 2010 +0200 @@ -0,0 +1,1140 @@ +/* +* Copyright (c) 2002-2007 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: Plugin utility used for automatic tags ( http, email, phone numbers, URI schemes ) +* syntax highlighting in text viewers +* +* +*/ + + +// INCLUDES +#include +#include // for CRichText +#include "ItemFinder.h" +#include +#include +#include +#include // for AknUtils +#include + +// scheme recog +#include +#include + +#include +#include + +// CONSTANTS +const TInt KMINTAGLENGTH(5); +_LIT( KHTTPPREFIX, "http://"); +_LIT( RTSP, "rtsp"); +_LIT( KTELPREFIX, "tel:"); + +const TInt KItemFinderMinDigitsToFind = 5; +#define SCHEME_INTERFACE_DEF TUid( TUid::Uid(0x101f8534) ) // we don't want header dependency to browser utilities + +NONSHARABLE_CLASS(CSchemeResolver): public CBase + { +public: + CSchemeResolver(){}; + static CSchemeResolver* NewL(); + inline CArrayPtrFlat& SupportedSchemes(){ return *iSupportedArray; }; + ~CSchemeResolver(); +private: + void ConstructL(); + CArrayPtrFlat* iSupportedArray; + RImplInfoPtrArray iEcomInfoArray; + }; + +CSchemeResolver* CSchemeResolver::NewL() + { + CSchemeResolver* me = new (ELeave) CSchemeResolver(); + CleanupStack::PushL( me ); + me->ConstructL(); + CleanupStack::Pop(); + return me; + } + +void CSchemeResolver::ConstructL() + { + //perform plugin scan + iSupportedArray = new (ELeave) CArrayPtrFlat( 5 ); + REComSession::ListImplementationsL( SCHEME_INTERFACE_DEF, iEcomInfoArray ); + for ( TInt i = 0; i < iEcomInfoArray.Count() ; i++ ) + { + HBufC* data = HBufC::NewLC( iEcomInfoArray[ i ]->DataType().Length() ); + data->Des().Copy( iEcomInfoArray[ i ]->DataType() ); + iSupportedArray->AppendL( data ); + CleanupStack::Pop(); + } + iEcomInfoArray.ResetAndDestroy(); // we don't need this anymore + } + +CSchemeResolver::~CSchemeResolver() + { + iEcomInfoArray.ResetAndDestroy(); + if (iSupportedArray) + { + iSupportedArray->ResetAndDestroy(); + } + delete iSupportedArray; + } + +NONSHARABLE_CLASS(CItemFinderAsyncParser): public CActive + { +public: + static CItemFinderAsyncParser* NewL( CEikRichTextEditor* aEditor, TInt* aFirstVisible, TInt aMinDigitsToFind ); + void SetCallbackObserver( CItemFinder* aObserver ){ iObserver = aObserver; }; + ~CItemFinderAsyncParser(); + void RunL(); + void DoCancel(); + CFindItemEngine* iFIE; + CArrayPtrFlat* iMarkedItems; +private: + CItemFinderAsyncParser( CEikRichTextEditor* aEditor, TInt* aFirstVisible, TInt aMinDigitsToFind ); + CEikRichTextEditor* iEditor; + TInt& iFirstVisible; + CItemFinder* iObserver; // not owned + TInt iMinDigitsToFind; +}; + +CItemFinderAsyncParser::CItemFinderAsyncParser( CEikRichTextEditor* aEditor, TInt* aFirstVisible, TInt aMinDigitsToFind ) + :CActive( CActive::EPriorityIdle ), iEditor( aEditor ), iFirstVisible( *aFirstVisible ), iMinDigitsToFind( aMinDigitsToFind ) + { + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptr = &iStatus; + User::RequestComplete( ptr, KErrNone );// RunL will be called when everything with higher priority has completed + } + +CItemFinderAsyncParser* CItemFinderAsyncParser::NewL( CEikRichTextEditor* aEditor, TInt* aFirstVisible, TInt aMinDigitsToFind ) + { + return new (ELeave) CItemFinderAsyncParser( aEditor, aFirstVisible, aMinDigitsToFind ); + } + +CItemFinderAsyncParser::~CItemFinderAsyncParser() + { + Cancel(); // does nothing + delete iFIE; + if ( iMarkedItems ) + { + iMarkedItems->ResetAndDestroy(); + } + delete iMarkedItems; + } + +void CItemFinderAsyncParser::RunL() + { + TInt fullLength = Min( iEditor->TextLength(), 0xffff ); + const TDesC& text = iEditor->Text()->Read( 0, fullLength ); + TInt searchAll = CFindItemEngine::EFindItemSearchURLBin + |CFindItemEngine::EFindItemSearchMailAddressBin + |CFindItemEngine::EFindItemSearchPhoneNumberBin + |CFindItemEngine::EFindItemSearchScheme; + if ( !iFIE ) + { + iFIE = CFindItemEngine::NewL( text, (CFindItemEngine::TFindItemSearchCase)searchAll, iMinDigitsToFind ); + } + else + { + iFIE->DoNewSearchL( text, (CFindItemEngine::TFindItemSearchCase)searchAll, iMinDigitsToFind ); + } + delete iMarkedItems; + iMarkedItems = 0; + iMarkedItems = new ( ELeave ) CArrayPtrFlat( 10 ); + iEditor->RefreshParsersL(); + if ( iObserver && iObserver->CallbackToExtObserver() == EFalse ) + { + iEditor->SetCursorPosL( 0, EFalse ); // need to rollback in order to make sure that line will remain same + if ( iFirstVisible > 0 ) + { + iEditor->SetCursorPosL( iFirstVisible, EFalse ); // Magic, parsing forces screen to first item (?) + } + iObserver->NextItemOrScrollL( iFirstVisible ? CItemFinder::EInit : CItemFinder::EInitDown ); + } + } + +void CItemFinderAsyncParser::DoCancel() + { + } + +EXPORT_C CItemFinder::CFindItemExt::~CFindItemExt() + { + if ( iItemDescriptor ) + delete iItemDescriptor; + } + +CItemFinder::~CItemFinder() + { + CRichText::DeactivateParser(this); + if ( iCurrentItemExt ) + { + delete iCurrentItemExt; + } + if ( iExternalLinks ) + { + iExternalLinks->ResetAndDestroy(); + delete iExternalLinks; + } + if ( iAsyncParser ) + { + delete iAsyncParser; + } + if ( iSchemeResolver ) + { + delete iSchemeResolver; + } + } + +CItemFinder::CItemFinder( TInt aFindFlags ): iFlags( aFindFlags ) + {} + +void CItemFinder::ConstructL() + { + iMinDigitsToFind = KItemFinderMinDigitsToFind; // default + iCurrentItemExt = new (ELeave) CFindItemExt(); + CRichText::ActivateParserL(this); + iSchemeResolver = CSchemeResolver::NewL(); + iMinDigitsToFind = GetMinDigitsToFindL(); // variated. + } + +TInt CItemFinder::GetMinDigitsToFindL() + { + TInt err = KErrNone; + CRepository* variation = CRepository::NewL( KCRUidCommonUi ); + TInt value; + err = variation->Get( KCuiMinDigitsToFind, value ); + delete variation; + if ( err != KErrNone ) + { + value = KItemFinderMinDigitsToFind; + } + return value; + } + +EXPORT_C CItemFinder* CItemFinder::NewL( TInt aFindFlags ) + { + CItemFinder* me = new (ELeave) CItemFinder( aFindFlags ); + CleanupStack::PushL( me ); + me->ConstructL(); + CleanupStack::Pop(); + return me; + } + +EXPORT_C TBool CItemFinder::NextItemOrScrollL( TFindDirection aDirection ) + { + TBool allowScroll = ETrue; + if ( !iEditor || !(*iEditor) ) + { + return EFalse; + } + if ( aDirection == EInit || aDirection == EInitUp || aDirection == EInitDown ) + { + if ( aDirection == EInitUp ) + { + iPreviousDirection = ENextUp; + iCurrentStart = iCurrentEnd = (*iEditor)->TextLength() - 1; + if ( iCurrentStart > 0 ) + { + (*iEditor)->SetCursorPosL( iCurrentEnd, EFalse ); + if ( iAsyncParser && iAsyncParser->iMarkedItems && iAsyncParser->iMarkedItems->Count() > 0 ) + // Check manually if the last item ends to last character in editor + // as CRichText can't handle this correctly + { + if ( iCurrentStart <= iAsyncParser->iMarkedItems->At( iAsyncParser->iMarkedItems->Count() - 1 )->iEnd ) + { + ResolveAndSetItemTypeL( iAsyncParser->iMarkedItems->At( iAsyncParser->iMarkedItems->Count() - 1 )->iStart ); + } + } + } + } + else if ( aDirection == EInitDown ) + { + iPreviousDirection = ENextDown; + (*iEditor)->SetCursorPosL(0,EFalse); + ResolveAndSetItemTypeL(0); + } + iFirstVisible = (*iEditor)->TextLayout()->FirstDocPosFullyInBand(); + (*iEditor)->TextView()->SetCursorVisibilityL( TCursor::EFCursorInvisible, TCursor::EFCursorInvisible ); + if ( iPreviousDirection == 0 )// just initialising + { + (*iEditor)->SetCursorPosL( iFirstVisible, EFalse ); + return EFalse; + } + // Search first tag on screen + // and set the cursor but if none found, don't scroll + allowScroll=EFalse; + aDirection = iPreviousDirection; + // check if we didn't scroll full display but only part of it + TPoint dummy; + if ( iCurrentStart != iCurrentEnd && (*iEditor)->TextLayout()->PosInBand( iCurrentStart, dummy ) ) + { + (*iEditor)->SetCursorPosL( iCurrentStart, EFalse ); + if ( (*iEditor)->TextLayout()->PosInBand( iCurrentEnd, dummy ) ) + { + // old selection should remain. basically this should be + // the case only when tag was partly visible in previous screen + (*iEditor)->SetSelectionL( iCurrentStart, iCurrentEnd ); + return ETrue; + } + } + else if ( iPreviousDirection == ENextDown ) + { // to ensure correct scrolling of items + (*iEditor)->SetCursorPosL( iFirstVisible, EFalse ); + } + } + TBool ret = EFalse; + TInt nextItem = KErrNotFound; + // Scroll cursor over tag or update page. + iPreviousDirection = aDirection; + TInt cursorPos = (*iEditor)->CursorPos(); + if ( aDirection == ENextDown ) + { + if ( (*iEditor)->SelectionLength() ) + { + cursorPos = iCurrentEnd; + } + if ( cursorPos == 0 ) + { + ResolveAndSetItemTypeL( 0 ); + if ( iCurrentEnd != iCurrentStart ) + { + TPoint pos; + if ( ((*iEditor)->TextLayout()->PosInBand( iCurrentEnd, pos ) ) ) // tag does not continue over screen + { + NextItemOrScrollL( EInit ); // will set the selection + return ETrue; + } + } + } + nextItem = (*iEditor)->RichText()->PositionOfNextTag( cursorPos, this ); + if ( nextItem != KErrNotFound ) + { // check whether the tag is on visible screen + TPoint dummy; + if ( !(*iEditor)->TextLayout()->PosInBand( nextItem, dummy ) ) + { + nextItem = KErrNotFound; + } + } + if ( allowScroll && nextItem == KErrNotFound ) + { + (*iEditor)->MoveDisplayL( TCursorPosition::EFPageDown ); + if ( (*iEditor)->TextLayout()->FirstDocPosFullyInBand() != iFirstVisible ) + {// If keypress was effective. + ret = ETrue; + NextItemOrScrollL( EInit ); + } + } + } + else + { + if ( (*iEditor)->SelectionLength() ) + { + cursorPos = iCurrentStart; + } + nextItem = (*iEditor)->RichText()->PositionOfPrevTag( cursorPos, this ); + /* if nextItem is KErrNotFound can mean cursorPos is in the middel of an valid item. + * so if cursorPos is in the middle of an vaild item, make sure that we adjust it to + * end of the item so that PositionOfPrevTag works fine + */ + if( nextItem == KErrNotFound ) + { + if ( cursorPos > 0 ) + { + cursorPos -= 1; + TInt temp = (*iEditor)->RichText()->PositionOfNextTag( cursorPos, this ); + if( temp != KErrNotFound ) + { + cursorPos = temp; + } + else + { + cursorPos = ( *iEditor )->TextLength(); + } + } + nextItem = ( *iEditor )->RichText()->PositionOfPrevTag( cursorPos, this ); + } + if ( nextItem >=0 && nextItem < iFirstVisible ) + { + nextItem = KErrNotFound; + } + if ( allowScroll && nextItem == KErrNotFound ) + { + (*iEditor)->MoveDisplayL( TCursorPosition::EFPageUp ); + TInt currPos = (*iEditor)->TextLayout()->FirstDocPosFullyInBand(); + if ( currPos != iFirstVisible ) + {// If keypress was effective. + ret = ETrue; + NextItemOrScrollL( EInit ); + } + } + } + if ( nextItem != KErrNotFound || !allowScroll ) + { + // Check !allowScroll for the case display was scrolled but previous item is still visible + // and no new tag found, just make the selection visible again + ret = ETrue; + if ( nextItem != KErrNotFound ) + { + iCurrentStart = nextItem; + ResolveAndSetItemTypeL(); + } + if ( iCurrentItemExt->iItemType != ENoneSelected ) + { + TPoint pos; + if ( ( !(*iEditor)->TextLayout()->PosInBand( iCurrentEnd ,pos ) ) ) // tag continues over screen + { + // lets check if we can scroll to show whole tag + // if not, we'll just show what can be fitted to screen with hl + (*iEditor)->TextLayout()->DocPosToXyPosL( iFirstVisible, pos ); + TRect lineRect; + TPoint tagStart; + (*iEditor)->TextLayout()->GetLineRect( pos.iY, lineRect ); + (*iEditor)->TextLayout()->DocPosToXyPosL( iCurrentStart, tagStart ); + if (!lineRect.Contains(tagStart) && allowScroll ) + { + (*iEditor)->MoveDisplayL( TCursorPosition::EFPageDown ); + while ( !(*iEditor)->TextLayout()->PosInBand( iCurrentStart, pos ) ) + { + (*iEditor)->MoveDisplayL( TCursorPosition::EFLineUp ); + } + NextItemOrScrollL(EInit); + } + else if ( !lineRect.Contains( tagStart ) ) + { + // we are not allowed to scroll, dont show highligh + iCurrentStart = iCurrentEnd = iFirstVisible; + ResetCurrentItem(); + return ret; + } + } + else if ( nextItem == KErrNotFound ) + { + // if the current end visible,and next item invisible, no select + iCurrentStart = iCurrentEnd = iFirstVisible; + ResetCurrentItem(); + return ret; + } + } + if ( iCurrentStart < iCurrentEnd ) + { + (*iEditor)->SetSelectionL( iCurrentStart, iCurrentEnd ); + } + else + { + ResetCurrentItem(); // no item selected + } + } + return ret; + } + +EXPORT_C void CItemFinder::SetFindModeL( TInt aFindFlags ) + { + if ( iFlags != aFindFlags ) + { + iFlags = aFindFlags; + if ( iEditor && *iEditor ) + { + User::LeaveIfError ( RefreshEditor() ); + } + } + if ( !aFindFlags && iEditor && *iEditor ) + { + if (!iExternalObserver) // do not align if no control to scrolling + { + iCurrentEnd = iCurrentStart; + iFirstVisible = (*iEditor)->TextLayout()->FirstDocPosFullyInBand(); + (*iEditor)->SetCursorPosL( iFirstVisible, EFalse ); + } + ResetCurrentItem(); + } + } + +EXPORT_C void CItemFinder::SetEditor( CEikRichTextEditor** aEditor ) + { + iEditor=aEditor; + if ( aEditor && *aEditor && (*aEditor)->TextLength() ) + { + TRAP_IGNORE( (*aEditor)->SetHighlightStyleL( EEikEdwinHighlightLink ) ); + if ( iAsyncParser ) + { + // We are changing the editor used, strange. + delete iAsyncParser; + iAsyncParser = 0; + } + iMarkedArrayComplete = EFalse; + TRAPD( err, iAsyncParser = CItemFinderAsyncParser::NewL( *iEditor, &iFirstVisible, iMinDigitsToFind ) ); // can't signal failure to client. + if ( !err ) + { + iAsyncParser->SetCallbackObserver( this ); + } + } + } + +// From MParser +EXPORT_C TBool CItemFinder::ParseThisText( + const CRichText& aTextObj, + TBool /*aAllowBack*/, + TInt aStartScan, + TInt aScanLength, + TInt& aStartTag, + TInt& aTagLength ) + { + TBool ret = EFalse; + if ( iEditor && *iEditor && (*iEditor)->RichText() != &aTextObj ) + { + return ret; + } + if ( !iAsyncParser || iAsyncParser->IsActive() || !iAsyncParser->iMarkedItems ) + { + return ret; // Asynch parser not ready + } + TInt lastInRange = aStartScan + aScanLength; + aStartTag = lastInRange; + CFindItemEngine::SFoundItem item = { 0, 0 }; + HBufC* tag = 0; + TInt engineFlags = EngineFlags(); + // Find (next) external item. + CFindItemExt* externalItem = NULL; + if ( iExternalLinks ) + { + TInt externalIndex = 0; + while ( externalIndex < iExternalLinks->Count() ) + { + CFindItemExt* tmp = iExternalLinks->At( externalIndex ); + if ( tmp && ( tmp->iStart >= aStartScan )) + { + externalItem = tmp; + switch( externalItem->iItemType ) + { + case EUrlAddress: + item.iItemType = CFindItemEngine::EFindItemSearchURLBin; + break; + case EUriScheme: + item.iItemType = CFindItemEngine::EFindItemSearchScheme; + break; + default: // only EUrlAddress and EUriScheme supported + item.iItemType = (CFindItemEngine::TFindItemSearchCase)0; + break; + } + if(item.iItemType&engineFlags) + { + tag = externalItem->iItemDescriptor->Des().Alloc(); // Null if failed. + item.iStartPos = externalItem->iStart; + item.iLength = externalItem->iEnd - item.iStartPos + 1; + break; + } + } + externalIndex++; + } + } + // Find (next) internal item. + iAsyncParser->iFIE->ResetPosition(); + CFindItemEngine::SFoundItem internalItem = { 0, 0 }; + if ( iAsyncParser->iFIE->Item( internalItem ) ) + { + do + { + if ( ( internalItem.iStartPos >= aStartScan ) && ( internalItem.iItemType & engineFlags ) ) + { + CFindItemEngine::SFoundItem scrollItem = { 0, 0 }; + while ( iAsyncParser->iFIE->NextItem( scrollItem ) ) // Find longest overridden item. + { + if ( scrollItem.iStartPos == internalItem.iStartPos ) + { + if ( internalItem.iLength == scrollItem.iLength ) + { + if ( internalItem.iItemType & CFindItemEngine::EFindItemSearchScheme ) // Check if valid scheme. + { + if ( !SchemeSupported( (*iEditor)->Text()->Read( internalItem.iStartPos, internalItem.iLength ) ) ) + { + internalItem = scrollItem; + } + } + } + else if ( ( scrollItem.iItemType & engineFlags ) && ( internalItem.iLength < scrollItem.iLength ) ) + { + internalItem = scrollItem; + } + else if ( ( scrollItem.iItemType & CFindItemEngine::EFindItemSearchURLBin ) + && ( internalItem.iLength > scrollItem.iLength ) + && !SchemeSupported( (*iEditor)->Text()->Read( internalItem.iStartPos, internalItem.iLength ) ) ) + { + // this is because URL is included in URI scheme + internalItem = scrollItem; + } + } + else + { + iAsyncParser->iFIE->PrevItem(scrollItem); + break; + } + } + if ( internalItem.iItemType & CFindItemEngine::EFindItemSearchScheme ) // Check if valid scheme. + { + if ( SchemeSupported( (*iEditor)->Text()->Read( internalItem.iStartPos, internalItem.iLength ) ) ) + { + break; + } + } + else if ( internalItem.iLength >= MinLengthForItemType( internalItem.iItemType ) ) // Check if valid length. + { + break; + } + } + } while ( iAsyncParser->iFIE->NextItem( internalItem ) ); + } + + // Use internal item as item. + TBool internalUsed( EFalse ); + if ( ( internalItem.iLength >0 ) && (internalItem.iStartPos>=aStartScan) &&( internalItem.iItemType&engineFlags )&& ( !externalItem || ( internalItem.iStartPos + internalItem.iLength - 1 ) < externalItem->iStart ) ) + { + item = internalItem; + delete tag; // Allocated for external link. + tag = (*iEditor)->Text()->Read( item.iStartPos, item.iLength ).Alloc(); // Null if failed. + internalUsed = ETrue; + } + // Add item as a marked item. + if ( item.iStartPos >= aStartScan && ( item.iStartPos + item.iLength ) <= lastInRange && + item.iItemType & engineFlags && + ( ( internalUsed && item.iLength >= MinLengthForItemType( item.iItemType ) ) || !internalUsed ) && + tag ) + { + TBool schemeNotSupported( EFalse ); + if ( item.iItemType & CFindItemEngine::EFindItemSearchScheme ) // Check if scheme valid. + { + if ( !SchemeSupported( tag->Des() ) ) + { + schemeNotSupported = ETrue; + ret = EFalse; + } + } + if ( !schemeNotSupported ) // Add if valid item + { + aStartTag = item.iStartPos; + aTagLength = item.iLength; + TInt err = KErrNone; + if ( !iMarkedArrayComplete ) + { + // Resolve type and tag. + TItemType type = ENoneSelected; + switch( item.iItemType ) + { + case CFindItemEngine::EFindItemSearchURLBin: + type = EUrlAddress; + break; + case CFindItemEngine::EFindItemSearchMailAddressBin: + type = EEmailAddress; + break; + case CFindItemEngine::EFindItemSearchPhoneNumberBin: + type = EPhoneNumber; + break; + case CFindItemEngine::EFindItemSearchScheme: + { + type = EUriScheme; + const TDesC& pref = tag->Des().Left( 4 ); + if ( pref.CompareF( KHTTPPREFIX().Left( 4 ) ) == 0 || pref.CompareF( RTSP ) == 0 ) + { + type = EUrlAddress; + } + } + break; + default: + break; + } + CFindItemExt* newItem = NULL; + TRAP( err, // Non-leaving method + { + newItem = new (ELeave) CFindItemExt(); + newItem->iItemType = type; + newItem->iItemDescriptor = tag->Des().AllocL(); + newItem->iStart = aStartTag; + newItem->iEnd = aStartTag + aTagLength - 1; + iAsyncParser->iMarkedItems->AppendL( newItem ); // Ownership changed. + newItem = NULL; + } ); + delete newItem; + } + if ( !err ) + { + ret = ETrue; + } + } + } + delete tag; + if ( !ret || ( ( aStartTag + aTagLength ) == (*iEditor)->TextLength() ) ) // iMarkedList complete + { + iMarkedArrayComplete = ETrue; + } + return ret; + } + +TInt CItemFinder::MinLengthForItemType( TInt aItemType ) + { + TInt length = KMINTAGLENGTH; // by default + if ( aItemType == CFindItemEngine::EFindItemSearchPhoneNumberBin ) + { + length = iMinDigitsToFind; // For phone numbers + } + return length; + } + +EXPORT_C const TDesC& CItemFinder::CreateDoItText( + const CRichText& /*aTextObj*/, + TInt /*aStartText*/, + TInt /*aLength*/) + { + return KNullDesC(); + } + +EXPORT_C void CItemFinder::ActivateThisTextL( + const CRichText& /*aTextObj*/, + TInt /*aStartText*/, + TInt /*aLength*/) + { + // We don't need this + } + +EXPORT_C TBool CItemFinder::ReformatOnRecognise() const + { + return ETrue; + } + +EXPORT_C TBool CItemFinder::ReformatOnRollover() const + { + return EFalse; // Rollover formating causes too much "unwanted" parsing. + } + +EXPORT_C void CItemFinder::GetRecogniseFormat( TCharFormat& aFormat ) + { + aFormat.iFontPresentation.iUnderline = EUnderlineOn; + TRgb color( KRgbBlue ); + AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), color, KAknsIIDQsnHighlightColors,EAknsCIQsnHighlightColorsCG3 ); + aFormat.iFontPresentation.iTextColor = color; + } + +EXPORT_C void CItemFinder::GetRolloverFormat( TCharFormat& /*aFormat*/ ) + { + } + +EXPORT_C TBool CItemFinder::ConfirmCursorOverTag( + const CRichText& /*aTextObj*/, + TInt /*aTagStart*/, + TInt /*aTagLen*/, + TInt /*aCurPos*/) + { + return ETrue; + } + +EXPORT_C void CItemFinder::Release() + { + iEditor = 0; // This makes parser to ignore practically all request from outside + } + +EXPORT_C void CItemFinder::MParser_Reserved_2() + { + } + +EXPORT_C void CItemFinder::ResetCurrentItem() + { + if ( iCurrentItemExt ) + { + iCurrentItemExt->iItemType = ENoneSelected; + delete iCurrentItemExt->iItemDescriptor; + iCurrentItemExt->iItemDescriptor = 0; + iCurrentItemExt->iStart = 0; + iCurrentItemExt->iEnd = 0; + } + } + +EXPORT_C TInt CItemFinder::PositionOfNextItem( TFindDirection aDirection ) + { + if ( iAsyncParser && !iAsyncParser->IsActive() ) // exists only when editor is set + { + // Going down + if ( aDirection != ENextUp ) + { + if ( (*iEditor)->CursorPos() == 0 && // check if the first char starts the tag + iCurrentItemExt->iItemType == ENoneSelected && + iAsyncParser->iMarkedItems && + iAsyncParser->iMarkedItems->Count() > 0 && + iAsyncParser->iMarkedItems->At(0)->iStart == 0 ) + { + return 0; + } + return (*iEditor)->RichText()->PositionOfNextTag( (*iEditor)->CursorPos(), this ); + } + else + { + return (*iEditor)->RichText()->PositionOfPrevTag( (*iEditor)->CursorPos(), this ); + } + } + return KErrNotFound; + } + +EXPORT_C void CItemFinder::ResolveAndSetItemTypeL( TInt aStart ) // defaults to KErrNotFound + { + if ( aStart != KErrNotFound ) + { + iCurrentStart = aStart; + } + ResetCurrentItem(); // Reset item. + if ( !iAsyncParser || iAsyncParser->IsActive() ) // Text not parsed + { + return; + } + iCurrentEnd = iCurrentStart; + TInt count = iAsyncParser->iMarkedItems->Count(); + for ( TInt i = 0; i < count; i++ ) // Set item. + { + if ( iAsyncParser->iMarkedItems->At( i )->iStart == iCurrentStart ) + { + iCurrentItemExt->iStart = iAsyncParser->iMarkedItems->At( i )->iStart; + iCurrentStart = iCurrentItemExt->iStart; + iCurrentItemExt->iEnd = iAsyncParser->iMarkedItems->At( i )->iEnd; + iCurrentEnd = iCurrentItemExt->iEnd + 1; + if ( iAsyncParser->iMarkedItems->At( i )->iItemDescriptor ) + { + delete iCurrentItemExt->iItemDescriptor; + iCurrentItemExt->iItemDescriptor = 0; + iCurrentItemExt->iItemDescriptor = iAsyncParser->iMarkedItems->At( i )->iItemDescriptor->AllocL(); + } + DoResolveAndSetItemTypeL( iAsyncParser->iMarkedItems->At( i ) ); + break; + } + } + } + +EXPORT_C void CItemFinder::AddObserver( MItemFinderObserver& aObserver ) + { + iExternalObserver = &aObserver; + } + +// Utilities +TInt CItemFinder::EngineFlags() + { + TInt fieFlags( 0 ); + if ( iFlags & EUrlAddress ) + { + fieFlags |= CFindItemEngine::EFindItemSearchURLBin; + } + if ( iFlags & EEmailAddress ) + { + fieFlags |= CFindItemEngine::EFindItemSearchMailAddressBin; + } + if ( iFlags & EPhoneNumber ) + { + fieFlags |= CFindItemEngine::EFindItemSearchPhoneNumberBin; + } + if ( iFlags & EUrlAddress || iFlags & EUriScheme ) + { + fieFlags |= CFindItemEngine::EFindItemSearchScheme; + } + return fieFlags; + } + +void CItemFinder::InsertPrefixToUrl() + { + if ( iCurrentItemExt && iCurrentItemExt->iItemType == EUrlAddress ) + { + const TDesC& pref = iCurrentItemExt->iItemDescriptor->Des().Left( 4 ); + if ( pref.CompareF( KHTTPPREFIX().Left( 4 ) ) == 0 || pref.CompareF( RTSP ) == 0 ) + { + return; // prefix already exists. + } + HBufC* tmp = iCurrentItemExt->iItemDescriptor->ReAlloc( iCurrentItemExt->iItemDescriptor->Length() + KHTTPPREFIX().Length() ); + if ( tmp ) + { + iCurrentItemExt->iItemDescriptor = tmp; + iCurrentItemExt->iItemDescriptor->Des().Insert( 0, KHTTPPREFIX ); + } + } + } + +TBool CItemFinder::SchemeSupported( const TDesC& aScheme ) + { + if ( aScheme.Left( KTELPREFIX().Length() ).CompareF( KTELPREFIX ) == 0 ) + { + if ( CommonPhoneParser::IsValidPhoneNumber( aScheme.Mid( 4 ), CommonPhoneParser::EPlainPhoneNumber) ) + { + return EFalse; // will be handled as pure phone number later on. + } + } + if ( iFlags & EUrlAddress || iFlags & EUriScheme ) + { + TInt schLength = aScheme.Locate( ':' ); + if ( schLength > 0 ) + { + TInt suppCount = iSchemeResolver->SupportedSchemes().Count(); + for ( TInt i = 0; i < suppCount; i++ ) + { + if ( aScheme.Length() >= schLength && iSchemeResolver->SupportedSchemes().At(i)->Des().CompareF( aScheme.Left( schLength ) ) == 0) + { + return ETrue; + } + } + } + } + return EFalse; + } + +TBool CItemFinder::CallbackToExtObserver() + { + if ( iExternalObserver ) + { + iExternalObserver->HandleParsingComplete(); + return ETrue; + } + return EFalse; + } + +EXPORT_C TBool CItemFinder::ItemWasTappedL( const TPoint aTappedPoint ) + { + if ( !iAsyncParser || iAsyncParser->IsActive() ) + { + return EFalse; + } + TInt pos = 0; + TInt len = (*iEditor)->TextLayout()->PosRangeInBand( pos ); + TRect rect; + TInt i = 0; + while ( i < iAsyncParser->iMarkedItems->Count() ) // Check visible items + { + TInt end = iAsyncParser->iMarkedItems->At( i )->iEnd; + if ( end < pos ) + { + i++; + continue; // Item not visible. + }; + TInt start = iAsyncParser->iMarkedItems->At( i )->iStart; + if ( start >= ( pos + len ) ) + { + break; // Item not visible. + } + TInt lastLine = (*iEditor)->TextLayout()->GetLineNumber( end ); + TInt nextLine = 0; + TInt lastPos = start; + do // Check all rects of one item. + { + rect = (*iEditor)->TextLayout()->GetLineRectL( start, end ); + if ( rect.Contains( aTappedPoint ) ) // Item rect tapped. + { + iFirstVisible = (*iEditor)->TextLayout()->FirstDocPosFullyInBand(); + ResetCurrentItem(); + iCurrentItemExt->iStart = iAsyncParser->iMarkedItems->At( i )->iStart; + iCurrentStart = iCurrentItemExt->iStart; + iCurrentItemExt->iEnd = iAsyncParser->iMarkedItems->At( i )->iEnd; + iCurrentEnd = iCurrentItemExt->iEnd + 1; + DoResolveAndSetItemTypeL( iAsyncParser->iMarkedItems->At( i ) ); + (*iEditor)->SetSelectionL( iCurrentEnd, iCurrentStart ); + return ETrue; // Hit + } + TPoint midPoint( rect.iBr.iX, ( rect.iBr.iY + rect.iTl.iY ) / 2 ); + lastPos = (*iEditor)->TextLayout()->XyPosToDocPosL( midPoint ); + start = lastPos + 1; + nextLine = (*iEditor)->TextLayout()->GetLineNumber( lastPos ); + } while ( nextLine != lastLine ); + i++; + } + return EFalse; // No hit + } + +void CItemFinder::DoResolveAndSetItemTypeL( const CFindItemExt* aItem ) + { + if ( aItem ) + { + iCurrentItemExt->iItemType = aItem->iItemType; + if ( aItem->iItemType != ENoneSelected && iCurrentEnd >= iCurrentStart ) + { + if ( !iCurrentItemExt->iItemDescriptor ) + { + iCurrentItemExt->iItemDescriptor = aItem->iItemDescriptor->AllocL(); + } + if ( iCurrentItemExt->iItemType == EUrlAddress ) + { + InsertPrefixToUrl(); // old url types need prefix in order to work w/ schemehandler + } + if ( iCurrentItemExt->iItemType == EUriScheme ) + { + // some schemes (ie. "old ones") have special handling + const TDesC& pref = iCurrentItemExt->iItemDescriptor->Des().Left( 4 ); + if ( pref.CompareF( KHTTPPREFIX().Left( 4 ) ) == 0 || pref.CompareF( RTSP ) == 0 ) + { + iCurrentItemExt->iItemType = EUrlAddress; + } + } + } + } + } + +EXPORT_C TInt CItemFinder::SetExternalLinks( CArrayPtrFlat* aLinks ) + { + TInt error = KErrNone; + if ( iExternalLinks ) // Remove existing links. + { + iExternalLinks->ResetAndDestroy(); + delete iExternalLinks; + iExternalLinks = 0; + } + // Add an external link validated by engine + TInt linkCount = aLinks ? aLinks->Count() : 0; + if ( linkCount > 0 ) + { + iExternalLinks = new CArrayPtrFlat( linkCount ); + if ( !iExternalLinks ) // Error: no memory. + { + return EFalse; + } + CFindItemEngine* fie = NULL; // For validation. + TInt linkIndex = 0; + do + { + CFindItemExt* link = aLinks->At( linkIndex++ ); + TInt flag = 0; + switch ( link->iItemType ) + { + case EUrlAddress: + flag = CFindItemEngine::EFindItemSearchURLBin; + break; + case EUriScheme: + if ( !SchemeSupported( link->iItemDescriptor->Des() ) ) + { + continue; + } + flag = CFindItemEngine::EFindItemSearchScheme; + break; + default: // Ignore unsupported types. + continue; + } + if ( !fie ) + { + TRAP( error, fie = CFindItemEngine::NewL( link->iItemDescriptor->Des(), (CFindItemEngine::TFindItemSearchCase)flag ) ); + } + else + { + TRAP( error, fie->DoNewSearchL( link->iItemDescriptor->Des(), (CFindItemEngine::TFindItemSearchCase)flag ) ); + } + if ( error != KErrNone ) // Error: find engine fails. + { + iExternalLinks->ResetAndDestroy(); + delete iExternalLinks; + iExternalLinks = 0; + delete fie; + return EFalse; + } + if ( fie->ItemCount() ) // Link was found. + { + CFindItemEngine::SFoundItem item = fie->ItemArray()->At( 0 ); + const TDesC& text = link->iItemDescriptor->Des().Mid( item.iStartPos, item.iLength ); + if ( link->iItemDescriptor->Des().CompareF( text ) == 0 ) + { + CFindItemExt* newItem = 0; + newItem = new CFindItemExt(); + error = KErrNoMemory; + if ( newItem ) + { + newItem->iItemDescriptor = link->iItemDescriptor->Alloc(); + if ( newItem->iItemDescriptor ) + { + newItem->iItemType = link->iItemType; + newItem->iStart = link->iStart; + newItem->iEnd = link->iEnd; + error = KErrNone; + TRAP( error, iExternalLinks->AppendL( newItem ) ); + } + } + if ( error != KErrNone ) // Error: cannot set ext. links. + { + iExternalLinks->ResetAndDestroy(); + delete iExternalLinks; + iExternalLinks = 0; + delete newItem->iItemDescriptor; + delete newItem; + delete fie; + return EFalse; + } + } + } + } + while ( linkIndex < linkCount ); + delete fie; + } + error = RefreshEditor(); // With ext. links. + if ( error != KErrNone ) + { + iExternalLinks->ResetAndDestroy(); + delete iExternalLinks; + iExternalLinks = 0; + (void)RefreshEditor(); + } + return error; + } + +EXPORT_C CItemFinder::CFindItemExt& CItemFinder::CurrentItemExt() + { + return *iCurrentItemExt; + } + +EXPORT_C TPtrC CItemFinder::CurrentSelection() + { + if ( iEditor && *iEditor ) // if editor used. + { + return (*iEditor)->Text()->Read( (*iEditor)->Selection().LowerPos(), (*iEditor)->Selection().Length() ); + } + else + { + HBufC* buffer = CurrentItemExt().iItemDescriptor; + if ( buffer ) + { + return *buffer; + } + else + { + return KNullDesC(); + } + } + } + +TInt CItemFinder::RefreshEditor() + { + TInt error = KErrNone; + TRAP( error, // Non-leaving method. + { + if ( iAsyncParser ) + { + iFirstVisible = (*iEditor)->TextLayout()->FirstDocPosFullyInBand(); + iMarkedArrayComplete = EFalse; + if ( iAsyncParser->iMarkedItems ) + { + iAsyncParser->iMarkedItems->ResetAndDestroy(); + } + (*iEditor)->RefreshParsersL(); + (*iEditor)->SetCursorPosL( 0, EFalse ); + ResolveAndSetItemTypeL( iFirstVisible ); + (*iEditor)->SetCursorPosL( iFirstVisible, EFalse ); + iPreviousDirection = ENextDown; + NextItemOrScrollL( EInit ); + } + else if ( (*iEditor)->TextLength() ) + { + iMarkedArrayComplete = EFalse; + iAsyncParser = CItemFinderAsyncParser::NewL( *iEditor, &iFirstVisible, iMinDigitsToFind ); + iAsyncParser->SetCallbackObserver( this ); + } + } ); // TRAP + return error; + } + +// End of File