diff -r 000000000000 -r 094583676ce7 wvuing/wvuiave/AppSrc/CCARichTextContainer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wvuing/wvuiave/AppSrc/CCARichTextContainer.cpp Thu Dec 17 08:41:52 2009 +0200 @@ -0,0 +1,3117 @@ +/* +* Copyright (c) 2006 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: Rich text container +* +*/ + + + +// INCLUDE FILES +#include "CCARichTextContainer.h" +#include "CCAMessageWrapper.h" +#include "MCATextView.h" +#include "ChatDebugAssert.h" +#include "IMUtils.h" +//#include "IMPSCspAllErrors.h" +#include "IMNoteMapper.h" +#include "ChatDefinitions.h" +#include "ChatDebugPrint.h" +#include "IMUtils.h" +#include "MCAStoredContacts.h" +#include "CCAStorageManagerFactory.h" +#include "CCAAppSettingsSAPExt.h" + +#include "CCAPicture.h" +#include "ccaappui.h" +//#include "ccaengine.h" +#include "MCAConversationMessage.h" +#include "MCAMessageUtils.h" +#include "MCAContentProcessor.h" +#include "CCAContentMessage.h" +#include "CCAMessageExtensionsHandler.h" +#include "impsbuilddefinitions.h" + +#include +#include +#include // CRichtText +#include +#include +#include +#include +#include +#include +#include // resourcereader +#include +#include + +#include +#include +#include + +#include "MCASettingsPC.h" +#include "MCAMsgAddedToRichTxtCtrlObserver.h" + +#include "MCAProcessManager.h" +#include "MCAGroupPC.h" +#include "IMUtils.h" + +#include +#include "TCAChatListBoxLayout.h" + +// The Settings have been moved to Cenrep (also retained in the Resource file), +// so the enums for keys and central repository header is added here +#include "VariantKeys.h" + + +// CONSTANTS +const TInt KChatHighlightColor = 244; +const TInt KPixelsBetweenLines = 8; // pixels between text lines +const TInt KTimeStampMaxLength = 15; // max length of timestamp text +const TInt KUnicodeRLM = 0x200F; // Right-to-Left Mark +const TInt KUnicodeLRM = 0x200E; // Left-to-Right Mark +const TInt KMaxLength = 10; // Max Length for user identity +_LIT( KSeparator, ": " ); + + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::CCARichTextContainer +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CCARichTextContainer::CCARichTextContainer( MCAAppUi& aAppUi, + MCASettingsPC& aSettingsPC, + MCATextView& aTextView, + CCAMessageExtensionsHandler& aExtensionsHandler, + CGulIcon*& aFromMe, + CGulIcon*& aToMe, + CGulIcon*& aUnsupported, + CGulIcon*& aCorrupted, + MGraphicsDeviceMap& aMap, + TBool aScrollOver, + MCAMsgAddedToRichTxtCtrlObserver* aAddMsgObserver + ) + : iAppUi( aAppUi ), + iSettings( aSettingsPC ), + iTextView( aTextView ), + iFromMe( aFromMe ), + iToMe( aToMe ), + iUnsupported( aUnsupported ), + iCorrupted( aCorrupted ), + iItemHighlight( ETrue ), + iHighlightState( ENoHighlight ), + iExtensionsHandler( aExtensionsHandler ), + iMap( aMap ), + iScrollOver( aScrollOver ), + iAddMsgObserver( aAddMsgObserver ) + { + iThumbSize = TSize( 42, 36 ); + iMessagesDeleted = EFalse; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::ConstructL() + { + iAppUi.AddLayoutChangeObserver( this ); + + // setup layout + TAknLayoutId layoutId; + CAknEnv::Static()->GetCurrentLayoutId( layoutId ); + + AknIconUtils::SetSize( iCorrupted->Bitmap(), iThumbSize ); + AknIconUtils::SetSize( iUnsupported->Bitmap(), iThumbSize ); + + // get icon settings + iOwnMsgIcon = IMUtils::IntResourceValueL( + RSC_CHAT_VARIATION_OWN_MSG_ICON ); + + iMsgIcon = IMUtils::IntResourceValueL( + RSC_CHAT_VARIATION_MSG_ICON ); + + CCAAppUi* appUI = static_cast ( CCoeEnv::Static()->AppUi() ); + + //Removed the ownership of iConnUI from this file and made it a reference + //APPUI owns this connection UI instance and shares the same with this component. + // get user-defined colors from SAP settings + CCAAppSettingsSAPExt* SAPExtension = CCAAppSettingsSAPExt::NewLC( appUI->ConnectionUI() ); + TInt ownCol( SAPExtension->IntValueL( KIMOwnMessageColourKey ) ); + TInt otherCol( SAPExtension->IntValueL( KIMOthersMessageColourKey ) ); + CleanupStack::PopAndDestroy( SAPExtension ); + + // Check timestamp setting + iShowTimeStamps = iSettings.GetBoolValuePC( TEnumsPC::EShowTimeStamps, NULL ); + + if ( ownCol != KErrNotFound ) + { + iOwnMsgColorInUse = ETrue; + iOwnMsgColor = ownCol; + } + if ( otherCol != KErrNotFound ) + { + iMsgColorInUse = ETrue; + iMsgColor = otherCol; + } + + // check resource colours + // The default colours settings is present only in the variation resource file. so we use IntResourceValueFromRssL instead of IntResourceValueL + TBool defaultColors = IMUtils::IntResourceValueFromRssL( + RSC_CHAT_DEFAULT_MESSAGE_COLORS ); + iColorWholeMessage = IMUtils::IntResourceValueFromRssL( + RSC_CHAT_COLOR_WHOLE_MESSAGE ); + + if ( !defaultColors && !iMsgColorInUse ) + { + // user-defined color not defined and resource colour in use + TResourceReader reader; + // Since the color settings is not part of Cenrep, the ID is changed to denote the same. + CCoeEnv::Static()->CreateResourceReaderLC( reader, + RSC_CHAT_MESSAGE_COLOR ); + TUint8 red( reader.ReadUint8() ); + TUint8 green( reader.ReadUint8() ); + TUint8 blue( reader.ReadUint8() ); + iMsgColor = TRgb( red, green, blue ); + iMsgColorInUse = ETrue; + CleanupStack::PopAndDestroy(); // reader + } + + if ( !defaultColors && !iOwnMsgColorInUse ) + { + // user-defined color not defined and resource colour in use + TResourceReader reader; + // Since the color settings is not part of Cenrep, the ID is changed to denote the same. + CCoeEnv::Static()->CreateResourceReaderLC( reader, + RSC_CHAT_OWN_MESSAGE_COLOR ); + TUint8 red( reader.ReadUint8() ); + TUint8 green( reader.ReadUint8() ); + TUint8 blue( reader.ReadUint8() ); + iOwnMsgColor = TRgb( red, green, blue ); + iOwnMsgColorInUse = ETrue; + CleanupStack::PopAndDestroy(); // reader + } + + CHAT_DP( D_CHAT_LIT( " msgColor red=%d, green=%d, blue=%d" ), + iMsgColor.Red(), iMsgColor.Green(), iMsgColor.Blue() ); + CHAT_DP( D_CHAT_LIT( " ownMsgColor red=%d, green=%d, blue=%d" ), + iOwnMsgColor.Red(), iOwnMsgColor.Green(), iOwnMsgColor.Blue() ); + + iPrimaryFont = AknLayoutUtils::FontFromId( EAknLogicalFontPrimaryFont, + NULL ); + iSecondaryFont = AknLayoutUtils::FontFromId( EAknLogicalFontPrimarySmallFont, + NULL ); + + CParaFormat paraFormat; + TParaFormatMask paraFormatMask; + paraFormat.iLanguage = CAknEnv::Static()->SettingCache().InputLanguage(); + paraFormatMask.SetAttrib( EAttParaLanguage ); + + paraFormat.iFillColor = KRgbWhite; + paraFormatMask.SetAttrib( EAttFillColor ); + + paraFormat.iLeftMarginInTwips = 100; + paraFormat.iIndentInTwips = -100; + paraFormatMask.SetAttrib( EAttLeftMargin ); + paraFormatMask.SetAttrib( EAttIndent ); + + // Create zoom factor object + TZoomFactor devicemap( CCoeEnv::Static()->ScreenDevice() ); + devicemap.SetZoomFactor( TZoomFactor::EZoomOneToOne ); + + // Get the line hight and font height + TRect mainPaneRect; + TAknLayoutRect lineRect; + TAknLayoutRect layoutRect; + + AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EMainPane, mainPaneRect ); + TAknWindowLineLayout readingPane = AknLayoutScalable_Apps::im_reading_pane( TChatListBoxLayout::EWithoutEditor ); + layoutRect.LayoutRect( mainPaneRect, readingPane ); + + TAknWindowLineLayout lineLayout = AknLayoutScalable_Apps::list_im_single_pane( 0 ); + lineRect.LayoutRect( layoutRect.Rect(), lineLayout ); + TInt lineHeight = lineRect.Rect().Height(); + + TAknLayoutText text; + text.LayoutText( layoutRect.Rect(), + AknLayoutScalable_Apps::list_single_im_pane_t1( 0 ).LayoutLine() ); + + // Set line space + paraFormat.iLineSpacingControl = CParaFormat::ELineSpacingExactlyInTwips; + paraFormatMask.SetAttrib( EAttLineSpacingControl ); + + paraFormat.iLineSpacingInTwips = devicemap.VerticalPixelsToTwips( lineHeight ); + paraFormatMask.SetAttrib( EAttLineSpacing ); + + TCharFormat charFormat; + TCharFormatMask charFormatMask; + charFormat.iFontSpec = iSecondaryFont->FontSpecInTwips(); + // Set font height + charFormat.iFontSpec.iHeight = devicemap.VerticalPixelsToTwips( text.TextRect().Height() ); + charFormatMask.SetAttrib( EAttFontTypeface ); + charFormatMask.SetAttrib( EAttFontHeight ); + + // Text color + TRgb defaultSkinTextColor( KRgbBlack ); + AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), + defaultSkinTextColor, + KAknsIIDQsnTextColors, + EAknsCIQsnTextColorsCG6 ); + + //Store the default masg color + iDefaultMsgColor = defaultSkinTextColor; + + charFormat.iFontPresentation.iTextColor = defaultSkinTextColor; + charFormatMask.SetAttrib( EAttColor ); + + charFormat.iFontPresentation.iPictureAlignment = + TFontPresentation::EAlignTop; + charFormatMask.SetAttrib( EAttFontPictureAlignment ); + + charFormat.iFontSpec.iFontStyle.SetStrokeWeight( EStrokeWeightNormal ); + charFormatMask.SetAttrib( EAttFontStrokeWeight ); + + + iParaFormatLayer = CParaFormatLayer::NewL( ¶Format, paraFormatMask ); + iCharFormatLayer = CCharFormatLayer::NewL( charFormat, charFormatMask ); + + iRichText = CRichText::NewL( iParaFormatLayer, iCharFormatLayer ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CCARichTextContainer* CCARichTextContainer::NewL( + MCAAppUi& aAppUi, + MCASettingsPC& aSettingsPC, + MCATextView& aTextView, + CCAMessageExtensionsHandler& aExtensionsHandler, + CGulIcon*& aFromMe, + CGulIcon*& aToMe, + CGulIcon*& aUnsupported, + CGulIcon*& aCorrupted, + MGraphicsDeviceMap& aMap, + TBool aScrollOver /*= ETrue*/, + MCAMsgAddedToRichTxtCtrlObserver* aAddMsgObserver /*= NULL*/ +) + { + CCARichTextContainer* self = new( ELeave ) CCARichTextContainer( + aAppUi, + aSettingsPC, + aTextView, + aExtensionsHandler, aFromMe, aToMe, + aUnsupported, aCorrupted, + aMap, + aScrollOver, aAddMsgObserver + ); + + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + + +// Destructor +CCARichTextContainer::~CCARichTextContainer() + { + delete iParaFormatLayer; + delete iCharFormatLayer; + delete iRichText; + iMessages.ResetAndDestroy(); + iAppUi.RemoveLayoutChangeObserver( this ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::SelectedMessage +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +MCAConversationMessage* CCARichTextContainer::SelectedMessage() + { + if ( iHighlightState != ENoHighlight ) + { + return &( iMessages[iSelected]->Message() ); + } + else + { + return NULL; + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::SelectedItem +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +HBufC* CCARichTextContainer::SelectedItemL() + { + if ( iHighlightState == EItemSelected ) + { + TCursorSelection curSel( iMessages[iSelected]->Selection() ); + + // calculate item highlight position + TCursorSelection itemSel( + iMessages[iSelected]->Highlights()[iSelectedItem] ); + + TInt messageStart( curSel.LowerPos() ); + curSel.iAnchorPos = itemSel.iAnchorPos + messageStart; + curSel.iCursorPos = itemSel.iCursorPos + messageStart; + + HBufC* text = HBufC::NewMaxL( curSel.Length() ); + TPtr ptr( text->Des() ); + iRichText->Extract( ptr, curSel.LowerPos(), curSel.Length() ); + return text; + } + else + { + return KNullDesC().AllocL(); + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::SelectedItemType +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCARichTextContainer::SelectedItemType() + { + if ( iHighlightState == EItemSelected ) + { + return iMessages[iSelected]->HighlightTypes()[iSelectedItem]; + } + else + { + return KErrNotFound; + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::RichText +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +MLayDoc* CCARichTextContainer::TextLayout() + { + return iRichText; + } + + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::MoveHighlightL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCARichTextContainer::MoveHighlightL( THighlightMovement aDirection ) + { + __CHAT_ASSERT_DEBUG( aDirection == EPrevious || aDirection == ENext ); + TBool up( aDirection == EPrevious ); + TInt messageCount( iMessages.Count() ); + if ( messageCount == 0 ) + { + // no messages, nothing to highlight + DisableHighlightL(); + return KErrNotFound; + } + + __CHAT_ASSERT_DEBUG( iSelected >= 0 && iSelected < messageCount ); + TInt highlightCount( iMessages[iSelected]->Highlights().Count() ); + + if ( !iItemHighlight ) + { + // if item highlighting is not on, clear the item count + highlightCount = 0; + } + + // if message is highlighted, but it doesn't fit to screen + // scroll it line by line + if ( iHighlightState == EMessageTop || iHighlightState == EMessageBottom ) + { + TCursorSelection& sel = iMessages[iSelected]->Selection(); + if ( up ) + { + // up + if ( !iTextView.IsVisible( sel.LowerPos() ) ) + { + // we're scrolling up and top line is not shown, + // so scroll messages down by one line + iTextView.ScrollLinesL( 1 ); + return KErrNone; + } + } + else if ( !iTextView.IsVisible( sel.HigherPos() ) ) + { + // we're scrolling down and bottom line is not shown, + // so scroll messages up by one line + iTextView.ScrollLinesL( -1 ); + return KErrNone; + } + } + + // update highlight position + switch ( iHighlightState ) + { + case ENoHighlight: // no highlight, select last or first message + { + if ( up ) + { + iSelected = messageCount - 1; + + // Get highlight count of prev message + // if item highlighting is on + if ( iItemHighlight ) + { + highlightCount = iMessages[iSelected]->Highlights().Count(); + } + + if ( highlightCount > 0 ) + { + // highlight items inside last message (if any) + iHighlightState = EItemSelected; + iSelectedItem = highlightCount - 1; + } + else + { + // bottom of last message + iHighlightState = EMessageBottom; + } + } + else + { + // top of first message + iHighlightState = EMessageTop; + iSelected = 0; + } + break; + } + case EItemSelected: // highlighting items + { + if ( up ) + { + // up, previous item + --iSelectedItem; + if ( iSelectedItem < 0 ) + { + // end of highlights, focus message + iHighlightState = EMessageTop; + } + } + else + { + // down, next item + ++iSelectedItem; + if ( iSelectedItem >= highlightCount ) + { + // end of highlights + if ( iSelected < messageCount - 1 ) + { + // focus next message + ++iSelected; + iHighlightState = EMessageTop; + } + else if ( iScrollOver ) + { + // going down from last message, + // disable highlight so editor can take the focus + DisableHighlightL(); + return KErrNone; + } + else + { + // Loop back to beginning + iSelected = 0; + iSelectedItem = 0; + iHighlightState = EMessageTop; + } + + } + } + break; + } + case EMessageTop: // highlighting message ("top" of it) + { + if ( up ) + { + // up, highlighting previous message + if ( iSelected > 0 ) + { + // prev + --iSelected; + + // get highlight count of prev message + // if item highlighting is on + if ( iItemHighlight ) + { + highlightCount = iMessages[iSelected]->Highlights().Count(); + } + + if ( highlightCount > 0 ) + { + // highlight items inside prev message (if any) + iHighlightState = EItemSelected; + iSelectedItem = highlightCount - 1; + } + else + { + // highlight whole prev message + iHighlightState = EMessageBottom; + } + } + else if ( iScrollOver ) + { + // going up from first message, + // disable highlight, so editor can take the focus + DisableHighlightL(); + return KErrNone; + } + else + { + // Loop to last message + iSelected = messageCount - 1; + highlightCount = 0; + + // Get highlight count of last message + // if item highlighting is on + if ( iItemHighlight ) + { + highlightCount = + iMessages[iSelected]->Highlights().Count(); + } + + if ( highlightCount > 0 ) + { + // Highlight items inside last message (if any) + iHighlightState = EItemSelected; + iSelectedItem = highlightCount - 1; + } + else + { + // Highlight whole last message + iHighlightState = EMessageBottom; + } + } + } + else + { + // down, highlight items inside this message (if any) + if ( highlightCount > 0 ) + { + iSelectedItem = 0; + iHighlightState = EItemSelected; + } + else if ( iSelected < messageCount - 1 ) + { + // next + ++iSelected; + iHighlightState = EMessageTop; + } + else if ( iScrollOver ) + { + // going down from last message, + // disable highlight, so editor can take the focus + DisableHighlightL(); + return KErrNone; + } + else + { + // Loop back to beginning + iSelected = 0; + iSelectedItem = 0; + iHighlightState = EMessageTop; + } + } + break; + } + case EMessageBottom: // highlighting message ("bottom" of it) + { + if ( up ) + { + // up, highlight items inside this message (if any) + if ( highlightCount > 0 ) + { + iSelectedItem = highlightCount - 1; + iHighlightState = EItemSelected; + } + else if ( iSelected > 0 ) + { + // prev + --iSelected; + + // get highlight count of prev message + // if item highlighting is on + if ( iItemHighlight ) + { + highlightCount = iMessages[iSelected]->Highlights().Count(); + } + + if ( highlightCount > 0 ) + { + // highlight items inside prev message (if any) + iHighlightState = EItemSelected; + iSelectedItem = highlightCount - 1; + } + else + { + iHighlightState = EMessageBottom; + } + } + else if ( iScrollOver ) + { + // going up from first message, + // disable highlight, so editor can take the focus + DisableHighlightL(); + return KErrNone; + } + else + { + // Loop to last message + iSelected = messageCount - 1; + highlightCount = 0; + + // Get highlight count of last message + // if item highlighting is on + if ( iItemHighlight ) + { + highlightCount = + iMessages[iSelected]->Highlights().Count(); + } + + if ( highlightCount > 0 ) + { + // Highlight items inside last message (if any) + iHighlightState = EItemSelected; + iSelectedItem = highlightCount - 1; + } + else + { + // Highlight whole last message + iHighlightState = EMessageBottom; + } + } + } + else + { + // down, highlighting next message + if ( iSelected < messageCount - 1 ) + { + // next + ++iSelected; + iHighlightState = EMessageTop; + } + else if ( iScrollOver ) + { + // going down from last message, + // disable highlight, so editor can take the focus + DisableHighlightL(); + return KErrNone; + } + else + { + // Loop back to beginning + iSelected = 0; + iSelectedItem = 0; + iHighlightState = EMessageTop; + } + } + break; + } + default: + { + __CHAT_ASSERT_DEBUG( EFalse ); + break; + } + } + + // get the selection + TCursorSelection curSel( CurrentSelection() ); + + // remove colors from old highlight + TInt textLen( iRichText->DocumentLength() ); + if ( iPrevious.iAnchorPos < textLen && + iPrevious.iCursorPos <= textLen ) + { + BackColorL( iPrevious, KRgbWhite ); + TextBackColorL( iPrevious, KRgbWhite ); + } + + // and set new one + if ( iHighlightState == EItemSelected ) + { + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + TRgb color; + + TInt error = AknsUtils::GetCachedColor( skin, + color, + KAknsIIDQsnHighlightColors, + EAknsCIQsnHighlightColorsCG2 ); + + if ( !error ) + { + // No error, use skinned background color + TextBackColorL( curSel, color ); + } + else + { + TextBackColorL( curSel, AKN_LAF_COLOR_STATIC( KChatHighlightColor ) ); + } + } + else + { + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + TRgb color; + + TInt error = AknsUtils::GetCachedColor( skin, + color, + KAknsIIDQsnHighlightColors, + EAknsCIQsnHighlightColorsCG2 ); + + if ( !error ) + { + // No error, use skinned background color + BackColorL( curSel, color ); + } + else + { + BackColorL( curSel, AKN_LAF_COLOR_STATIC( KChatHighlightColor ) ); + } + } + + // handle changed format + TCursorSelection changed( Union( iPrevious, curSel ) ); + + iTextView.HandleFormatChangedL( changed ); + + iTextView.ScrollVisibleL( curSel, ETrue ); + + iPrevious = curSel; + return KErrNone; + } + + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::DisableHighlightL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::DisableHighlightL() + { + iHighlightState = ENoHighlight; + + TCursorSelection sel( 0, 0 ); + TInt count( iMessages.Count() ); + if ( count > 0 ) + { + sel = iMessages[count-1]->Selection(); + } + + // remove colors from old highlight (if any) + TInt textLen( iRichText->DocumentLength() ); + if ( iPrevious.iAnchorPos < textLen && + iPrevious.iCursorPos <= textLen ) + { + BackColorL( iPrevious, KRgbWhite ); + TextBackColorL( iPrevious, KRgbWhite ); + iTextView.HandleFormatChangedL( iPrevious ); + } + + // set focus to last message + iTextView.ScrollVisibleL( sel, EFalse ); + } + + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::SetItemHighlight +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::SetItemHighlight( TBool aItemHighlight ) + { + iItemHighlight = aItemHighlight; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::Highlighted +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCARichTextContainer::Highlighted() + { + return iHighlightState != ENoHighlight; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::Highlighted +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCARichTextContainer::HighlightItemAtPosL( TInt aPos ) + { + TInt messageCount = iMessages.Count(); + if ( messageCount == 0 ) + { + // no messages + return KErrNotFound; + } + + // Store old values + TInt oldSelectedMessage = iSelected; + TInt oldSelectedItem = iSelectedItem; + THighlightState oldHighlight = iHighlightState; + + // Find message in position aPos + TBool found = EFalse; + for ( TInt i = 0; i < messageCount; ++i ) + { + TCursorSelection sel = iMessages[ i ]->WholeSelection(); + if ( aPos >= sel.LowerPos() && + aPos <= sel.HigherPos() ) + { + // Found the message + iSelected = i; + iHighlightState = EMessageTop; + found = ETrue; + // stop searching + break; + } + } + + if ( !found ) + { + // not found + return KErrNotFound; + } + + // Check if there are items inside the message that can be highlighted. + if ( iItemHighlight ) + { + TInt hlCount = iMessages[ iSelected ]->Highlights().Count(); + TInt relativePos = + aPos - iMessages[ iSelected ]->WholeSelection().LowerPos(); + + for ( TInt i = 0; i < hlCount; ++i ) + { + TCursorSelection sel = iMessages[ iSelected ]->Highlights()[ i ]; + + // Highlight's selection is relative to message + if ( relativePos >= sel.LowerPos() && + relativePos <= sel.HigherPos() ) + { + // Found an item + iHighlightState = EItemSelected; + iSelectedItem = i; + // stop searching + break; + } + } + } + + // Update UI only if selection has changed + if ( iSelected != oldSelectedMessage || + iSelectedItem != oldSelectedItem || + oldHighlight != iHighlightState ) + { + UpdateSelectionL(); + return KErrNone; + } + // Already highlighted + return KErrAlreadyExists; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::UpdateSelection +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::UpdateSelectionL() + { + // get the selection + TCursorSelection curSel( CurrentSelection() ); + + // remove colors from old highlight + TInt textLen( iRichText->DocumentLength() ); + if ( iPrevious.iAnchorPos < textLen && + iPrevious.iCursorPos <= textLen ) + { + BackColorL( iPrevious, KRgbWhite ); + TextBackColorL( iPrevious, KRgbWhite ); + } + + // and set new one + if ( iHighlightState == EItemSelected ) + { + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + TRgb color; + + TInt error = AknsUtils::GetCachedColor( skin, + color, + KAknsIIDQsnHighlightColors, + EAknsCIQsnHighlightColorsCG2 ); + + if ( !error ) + { + // No error, use skinned background color + TextBackColorL( curSel, color ); + } + else + { + TextBackColorL( curSel, AKN_LAF_COLOR_STATIC( KChatHighlightColor ) ); + } + } + else + { + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + TRgb color; + + TInt error = AknsUtils::GetCachedColor( skin, + color, + KAknsIIDQsnHighlightColors, + EAknsCIQsnHighlightColorsCG2 ); + + if ( !error ) + { + // No error, use skinned background color + BackColorL( curSel, color ); + } + else + { + BackColorL( curSel, AKN_LAF_COLOR_STATIC( KChatHighlightColor ) ); + } + } + + // handle changed format + TCursorSelection changed( Union( iPrevious, curSel ) ); + iTextView.HandleFormatChangedL( changed ); + iTextView.ScrollVisibleL( curSel, ETrue ); + + iPrevious = curSel; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::InsertUserIdentityL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::InsertUserIdentityL( + CCAMessageWrapper& aMessageWrapper ) + { + TInt stampStart = 0; + TCursorSelection timeSel( stampStart, stampStart ); + TInt stampEnd = stampStart; + MCAConversationMessage& message = aMessageWrapper.Message(); + TPtrC sender; + TPtrC recipient; + MCAStoredContacts* contacts = CCAStorageManagerFactory::ContactListInterfaceL(); + if ( message.MessagerType() == TEnumsPC::EMessageSent + && message.MessageType() != TEnumsPC::EMessageSystem ) + { + sender.Set( contacts->OwnStatus().Identification() ); + } + else + { + sender.Set( contacts->Identification( message.Sender() ) ); + } + recipient.Set( contacts->Identification( message.Recipient() ) ); + + if ( message.MessagerType() == TEnumsPC::EMessageSent && + message.MessageType() == TEnumsPC::EMessagePTOP && + iOwnMsgIcon ) + { + if ( sender.Length() >= KMaxLength ) + { + TPtrC aTempPtr; + aTempPtr.Set( sender.Left( 8 ) ); + TBuf<11> aTruncateSender( aTempPtr ); + aTruncateSender.Append( _L( ".. " ) ); + iRichText->InsertL( stampStart, aTruncateSender ); + stampEnd = stampStart + aTruncateSender.Length(); + } + else + { + TBuf<10> aTruncateSender( sender ); + aTruncateSender.Append( KSpace ); + iRichText->InsertL( stampStart, aTruncateSender ); + stampEnd = stampStart + aTruncateSender.Length(); + } + } + else if ( message.MessageType() == TEnumsPC::EMessagePTOP && + message.MessagerType() == TEnumsPC::EMessageReceived && + iMsgIcon ) + { + if ( recipient.Length() >= KMaxLength ) + { + TPtrC aTempPtr; + aTempPtr.Set( recipient.Left( 8 ) ); + TBuf<11> aTruncateRecipient( aTempPtr ); + aTruncateRecipient.Append( _L( ".. " ) ); + iRichText->InsertL( stampStart, aTruncateRecipient ); + stampEnd = stampStart + aTruncateRecipient.Length(); + } + else + { + TBuf<10> aTruncateRecipient( recipient ); + aTruncateRecipient.Append( KSpace ); + //CEikonEnv::Static()->InfoMsg(aTruncateRecipient); + iRichText->InsertL( stampStart, aTruncateRecipient ); + stampEnd = stampStart + aTruncateRecipient.Length(); + } + } + timeSel.SetSelection( stampStart, stampEnd - 1 ); + + // bold the User Identity + if ( timeSel.Length() > 0 ) + { + BoldL( timeSel ); + } + + return timeSel; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::InsertTimeStampL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::InsertTimeStampL( + CCAMessageWrapper& aMessageWrapper ) + { + TInt start = 0; + TCursorSelection timeSel( start, start ); + + if ( !iSettings.GetBoolValuePC( TEnumsPC::EShowTimeStamps, NULL ) || + aMessageWrapper.Message().SystemMessageType() == + TEnumsPC::ESystemMessageDateChange ) + { + // 1. Setting for time stamp is OFF + // 2. Special case: date change don't have time stamp. + return timeSel; + } + + // format time + HBufC* timeStamp = HBufC::NewLC( KTimeStampMaxLength + KSpace().Length() ); + TPtr timePtr( timeStamp->Des() ); + + HBufC* timeStampFormat = NULL; + timeStampFormat = StringLoader::LoadLC( R_QTN_TIME_USUAL_WITH_ZERO ); + + TTime time = aMessageWrapper.Message().TimeStamp(); + TRAPD( err, time.FormatL( timePtr, *timeStampFormat ) ); + if ( err == KErrOverflow ) + { + // Reserved text space was not enough. + // Adjust the KTimeStampMaxLength + __CHAT_ASSERT_DEBUG( EFalse ); + + // in release builds return without timestamp + CleanupStack::PopAndDestroy( 2, timeStamp ); // timeStamp, timeStampFormat + return timeSel; + } + + // Convert numbers to local language + AknTextUtils::LanguageSpecificNumberConversion( timePtr ); + + // Set Alignment of Pm/Am from local setting + // This keeps the am/pm text close to the time. + timePtr.Append( AknLayoutUtils::LayoutMirrored() ? + KUnicodeRLM : KUnicodeLRM ); + + // IM client UI customization, phase 2 + // Based on Variation flag, changes Time Stamp format in conversation view. + // where Time Stamp should appear in brackets. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + // and check dynamic features + if ( requireUserIdentity ) + { + timePtr.Insert( 0, _L( "(" ) ); + timePtr.Append( _L( ")" ) ); + timePtr.Append( KSpace ); + } + else + { + timePtr.Append( KSpace ); + } + + iRichText->InsertL( start, timePtr ); + + CleanupStack::PopAndDestroy( 2, timeStamp ); // timeStamp, timeStampFormat + timeSel.SetSelection( start, timePtr.Length() - 1 ); + + // bold the time + if ( timeSel.Length() > 0 ) + { + BoldL( timeSel ); + } + + return timeSel; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::InsertContentL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCARichTextContainer::InsertContentL( CCAMessageWrapper& aMessageWrapper ) + { + MCAConversationMessage& message = aMessageWrapper.Message(); + + TEnumsPC::TContentType type( message.ContentType() ); + switch ( type ) + { + case TEnumsPC::EContentText: + { + TInt oldLen = iRichText->DocumentLength(); + + // Insert text + TPtrC msg( message.Text() ); + + HBufC* tempMsg = NULL; + + TInt paraBreak = msg.Locate( CEditableText::EParagraphDelimiter ); + // Replace all paragraph delimiters with line breaks + // to keep alingment consistent + if ( paraBreak != KErrNotFound ) + { + tempMsg = msg.AllocLC(); + TPtr ptr( tempMsg->Des() ); + ptr.Zero(); + + ReplaceParaDelimsWithLineBreaks( msg, ptr ); + msg.Set( ptr ); + } + + HBufC* numberConv = NULL; + + // Convert numerals to local language + // 1) date item + if ( aMessageWrapper.Message().SystemMessageType() == + TEnumsPC::ESystemMessageDateChange ) + { + // Don't touch the original message + numberConv = msg.AllocLC(); + TPtr ptr( numberConv->Des() ); + AknTextUtils::LanguageSpecificNumberConversion( ptr ); + msg.Set( ptr ); + } + + iRichText->InsertL( 0, msg ); + + TInt textEnd = msg.Length(); + + // Check for extensions (smileys) + TCursorSelection selection( 0, textEnd ); + TInt bfrConv = iRichText->DocumentLength(); + iExtensionsHandler.ConvertSelectionToExtensionL( *iRichText, + selection ); + + // Move textEnd index if extensions were found + textEnd -= ( bfrConv - iRichText->DocumentLength() ); + + // Add nbs to preserve formatting + iRichText->InsertL( textEnd, + CEditableText::EZeroWidthNoBreakSpace ); + + // If this is system message, it should be bold + if ( message.MessageType() == TEnumsPC::EMessageSystem ) + { + TCursorSelection sel( 0, textEnd ); + BoldL( sel ); + // System messages need different kind of alignment + CParaFormat paraFormat; + TParaFormatMask paraFormatMask; + paraFormat.iLeftMarginInTwips = 0; + paraFormatMask.SetAttrib( EAttLeftMargin ); + iRichText->ApplyParaFormatL( ¶Format, paraFormatMask, + 0, textEnd + 1 ); + } + else + { + // Apply alignment for "normal" messages + CParaFormat paraFormat; + TParaFormatMask paraFormatMask; + paraFormat.iLeftMarginInTwips = 100; + paraFormatMask.SetAttrib( EAttLeftMargin ); + iRichText->ApplyParaFormatL( ¶Format, paraFormatMask, + 0, textEnd + 1 ); + + // Remove bolding from message content + TCharFormat charFormat; + TCharFormatMask charFormatMask; + charFormat.iFontSpec.iFontStyle.SetStrokeWeight( EStrokeWeightNormal ); + charFormatMask.SetAttrib( EAttFontStrokeWeight ); + iRichText->ApplyCharFormatL( charFormat, charFormatMask, + 0, textEnd + 1 ); + } + + // Cleanup + if ( numberConv ) + { + CleanupStack::PopAndDestroy( numberConv ); + } + if ( tempMsg ) + { + CleanupStack::PopAndDestroy( tempMsg ); + } + + return iRichText->DocumentLength() - oldLen; + } + case TEnumsPC::EContentPicture: // Flowthrough + case TEnumsPC::EContentOther: + { + TInt oldLen = iRichText->DocumentLength(); + // insert thumbnail (if it's ready) + AddThumbL( 0, aMessageWrapper ); + return iRichText->DocumentLength() - oldLen; + } + case TEnumsPC::EContentInvalid: + { + break; + } + default: + { + // Unsupported type + __CHAT_ASSERT_DEBUG( false ); + break; + } + } + + return 0; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::InsertNickL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::InsertNickL( + CCAMessageWrapper& aMessageWrapper ) + { + // insert nick + MCAConversationMessage& message = aMessageWrapper.Message(); + + if ( message.FailedMessage() ) + { + return TCursorSelection( 0, 0 ); + } + + TPtrC sender; + TPtrC recipient; + if ( message.MessageType() == TEnumsPC::EMessageGroup || + message.MessageType() == TEnumsPC::EMessageWhisper ) + { + sender.Set( message.Sender() ); + + if ( message.MessagerType() == TEnumsPC::EMessageSent ) + { + recipient.Set( message.Recipient() ); + } + else + { + CCAAppUi* appUI = static_cast ( CCoeEnv::Static()->AppUi() ); + recipient.Set( appUI->GetProcessManager().GetGroupInterface()->ScreenName() ); + } + + } + else + { + MCAStoredContacts* contacts = + CCAStorageManagerFactory::ContactListInterfaceL(); + if ( message.MessagerType() == TEnumsPC::EMessageSent + && message.MessageType() != TEnumsPC::EMessageSystem ) + { + sender.Set( contacts->OwnStatus().Identification() ); + } + else + { + sender.Set( contacts->Identification( message.Sender() ) ); + } + + recipient.Set( contacts->Identification( message.Recipient() ) ); + } + TCursorSelection nickSel( 0, 0 ); + + if ( message.MessagerType() == TEnumsPC::EMessageSent && + message.MessageType() == TEnumsPC::EMessagePTOP && + iOwnMsgIcon ) + { + // this is sent (or failed) p2p message, + // insert "from me" icon and separator + // (if icons are in use) + // IM client UI customization, phase 2 + // Based on Variation flag, changes Nick format in conversation view + // removes arrow sign and append only a seperator. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + // and check dynamic features + if ( !requireUserIdentity ) + { + CCAPicture* pic = new( ELeave ) CCAPicture( iMap, iFromMe ); + TPictureHeader header; + header.iPicture = TSwizzle( pic ); + iRichText->CancelInsertCharFormat(); // to prevent ETEXT 31 panic + iRichText->InsertL( 0, header ); // takes ownership + iRichText->InsertL( 1, KSeparator ); + nickSel.iAnchorPos = 1; + } + else + { + iRichText->InsertL( 0, KSeparator ); + nickSel.iAnchorPos = 0; + } + } + else + { + TInt nickStart = 0; + TInt nickEnd = nickStart; + + if ( message.MessageType() == TEnumsPC::EMessageWhisper ) + { + // this is send/received whisper message (to us), + // insert sender, "to me" icon and separator + iRichText->InsertL( nickStart, sender ); + nickEnd = sender.Length(); + + CCAPicture* pic = NULL; + if ( message.MessagerType() == TEnumsPC::EMessageSent ) + { + pic = new( ELeave )CCAPicture( iMap, + iFromMe ); + } + else + { + pic = new( ELeave )CCAPicture( iMap, + iToMe ); + } + TPictureHeader header; + header.iPicture = TSwizzle( pic ); + iRichText->CancelInsertCharFormat(); // to prevent ETEXT 31 panic + iRichText->InsertL( nickEnd, header ); // takes ownership + + iRichText->InsertL( nickEnd + 1, recipient ); + nickEnd = nickEnd + 1 + recipient.Length(); + + iRichText->InsertL( nickEnd, KSeparator ); + } + else if ( message.MessageType() == TEnumsPC::EMessagePTOP && + message.MessagerType() == TEnumsPC::EMessageReceived && + iMsgIcon ) + { + // received p2p message and we want to display icon + // insert "to me" icon and separator + // IM client UI customization, phase 2 + // Based on Variation flag, changes Nick format in conversation view + // removes arrow sign and append only a seperator. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + // and check dynamic features + if ( !requireUserIdentity ) + { + CCAPicture* pic = new( ELeave ) CCAPicture( iMap, + iToMe ); + TPictureHeader header; + header.iPicture = TSwizzle( pic ); + iRichText->CancelInsertCharFormat(); // to prevent ETEXT 31 panic + iRichText->InsertL( nickStart, header ); // takes ownership + iRichText->InsertL( nickStart + 1, KSeparator ); + nickEnd = 1; + } + else + { + iRichText->InsertL( nickStart, KSeparator ); + nickEnd = 0; + } + } + else if ( sender.Length() > 0 ) + { + // this is normal group message (or p2p message if icons are not used), + // insert sender and separator + iRichText->InsertL( nickStart, sender ); + nickEnd = nickStart + sender.Length(); + iRichText->InsertL( nickEnd, KSeparator ); + } + + // formatting (bold nickname) + nickSel.SetSelection( nickStart, nickEnd ); + if ( nickSel.Length() > 0 ) + { + BoldL( nickSel ); + } + } + return nickSel; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::LayoutChangedL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::LayoutChangedL( TInt /*aType*/ ) + { + UpdateSkinnedTextColourL(); + if ( Highlighted() ) + { + ChangeHighlightColorL(); + } + } + +TInt CCARichTextContainer::IntResourceValueL( TInt aResourceId ) + { + // Show user Identity, if variated so + TResourceReader reader; + TInt value( KErrNone ); + CCoeEnv::Static()->CreateResourceReaderLC( reader, aResourceId ); + value = ResourceUtils::ReadTInt32L( reader ); + CleanupStack::PopAndDestroy(); // reader + + return value; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::AddMessageL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::AddMessageL( MCAConversationMessage& aMessage ) + { + if ( !iShowTimeStamps && + aMessage.SystemMessageType() == TEnumsPC::ESystemMessageDateChange ) + { + // if timestamp setting is OFF we don't show day change items + return; + } + + TInt oldCount = iMessages.Count(); + + // create wrapper + CCAMessageWrapper* wrapper = CCAMessageWrapper::NewL( aMessage, *this ); + + CleanupStack::PushL( wrapper ); + iMessages.AppendL( wrapper ); // takes the ownership + CleanupStack::Pop( wrapper ); + + if ( aMessage.ContentType() == TEnumsPC::EContentPicture ) + { + if ( IMUtils::ContentProtectedL( aMessage.ContentData() ) ) + { + aMessage.SetProcessState( TEnumsPC::EContentNotSupported ); + } + else + { + // add thumbnail processor for message + aMessage.AddContentProcessorL( iThumbSize ); + + // If this is recorded chat container (iScrollOver == EFalse) + // and added message was first move highlight + if ( oldCount == 0 && !iScrollOver ) + { + iMsgAddedToContentProcessor = 1; + } + } + } + + // start new paragraph + TInt lineStart = iRichText->DocumentLength(); + if ( lineStart != 0 ) + { + iRichText->InsertL( lineStart, CEditableText::EParagraphDelimiter ); + } + + // Set Alignment from local layout + iRichText->InsertL( iRichText->DocumentLength(), + AknLayoutUtils::LayoutMirrored() ? KUnicodeRLM : KUnicodeLRM ); + + // add message to rich text + TInt startPos( iRichText->DocumentLength() ); + + // IM client UI customization, phase 2 + // Based on Variation flag, enables User Identity with Time Stamp + // in chat conversation view. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + // and check dynamic features + if ( requireUserIdentity ) + { + TCursorSelection userSel = AddUserIdentityL( *wrapper ); + } + + TCursorSelection timeSel = AddTimeStampL( *wrapper ); + TCursorSelection nickSel = AddNickL( *wrapper ); + AddContentL( startPos, *wrapper ); + TInt endPos( iRichText->DocumentLength() ); + + // set selection for highlight and the whole selection + // (so that this can be removed) + // append deletioncount to new messages + wrapper->Selection().SetSelection( endPos - 1, startPos ); + wrapper->WholeSelection().SetSelection( endPos, lineStart ); + wrapper->MessageSelection().SetSelection( endPos - 1, nickSel.HigherPos() ); + + CHAT_DP( D_CHAT_LIT( "owncol: %d msgcol: %d" ), iOwnMsgColorInUse, iMsgColorInUse ); + + // append coloring + TCursorSelection& sel = iColorWholeMessage ? wrapper->Selection() : nickSel; + TInt len( sel.Length() ); + CHAT_DP( D_CHAT_LIT( "selectiong length %d" ), len ); + + // Make sure the background (highlight) is not copied from + // previous message. + BackColorL( sel, KRgbWhite ); + TextBackColorL( sel, KRgbWhite ); + + if ( len > 0 && + aMessage.MessageType() != TEnumsPC::EMessageSystem ) + { + // custom colors + if ( aMessage.MessagerType() == TEnumsPC::EMessageSent ) + { + // own msg + if ( iOwnMsgColorInUse ) + { + TextColorL( sel, iOwnMsgColor ); + } + else + { + //default color + TextColorL( sel, iDefaultMsgColor ); + } + } + else if ( aMessage.MessagerType() == TEnumsPC::EMessageReceived ) + { + // other msg + if ( iMsgColorInUse ) + { + TextColorL( sel, iMsgColor ); + } + else + { + //default color + TextColorL( sel, iDefaultMsgColor ); + } + } + + } + + + // inform of inserted rich text + TCursorSelection curSel( CurrentSelection() ); + TRAPD( error, iTextView.HandleAdditionL( lineStart == 0, curSel, EFalse ) ); + if ( error != KErrNone ) + { + CHAT_DP_TXT( "CCARichTextContainer::AddMessageL: Recovering from error" ); + + // Remove failed text + TCursorSelection del = wrapper->WholeSelection(); + iRichText->DeleteL( del.LowerPos(), del.Length() ); + + if ( error == KErrNoMemory ) + { + // If OOM => stop processing all our pending messages + iRecoveringFromOOM = ETrue; + TInt count = iMessages.Count(); + for ( TInt i = count - 1; i >= 0; --i ) + { + TRAP_IGNORE( iMessages[i]->Message().RemoveProcessingL() ); + } + iRecoveringFromOOM = EFalse; + } + else + { + TRAP_IGNORE( wrapper->Message().RemoveProcessingL() ); + } + + // Remove the failed wrapper and leave + TInt index = iMessages.Find( wrapper ); + if ( index != KErrNotFound ) + { + delete wrapper; + iMessages.Remove( index ); + } + + // Try to handle changes once more + TRAPD( updateErr, iTextView.HandleGlobalChangeNoRedrawL() ); + if ( !updateErr ) + { + TCursorSelection curSel( CurrentSelection() ); + TRAP_IGNORE( iTextView.ScrollVisibleL( curSel, EFalse ) ); + } + User::Leave( error ); + } + + // and scroll (unless in highlight-mode) + if ( iHighlightState == ENoHighlight ) + { + TCursorSelection curSel( CurrentSelection() ); + iTextView.ScrollVisibleL( curSel, EFalse ); + + // If this is recorded chat container (iScrollOver == EFalse) + // and added message was first move highlight + // so we have focus on the topmost item. + if ( oldCount == 0 && !iScrollOver ) + { + if ( !iMsgAddedToContentProcessor ) + { + MoveHighlightL( ENext ); + } + if ( iAddMsgObserver ) + { + iAddMsgObserver->HandleMessageAddedL( iMessages.Count() ); + } + } + + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::MessageChangedL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::MessageChangedL( TInt aIndex ) + { + CHAT_DP_FUNC_ENTER( "CCARichTextContainer::MessageChangedL" ); + __CHAT_ASSERT_DEBUG( aIndex >= 0 && aIndex < iMessages.Count() ); + + if ( iRecoveringFromOOM ) + { + // Don't handle changes if we had OOM situation. + return; + } + + CCAMessageWrapper* wrapper = iMessages[ aIndex ]; + + // we don't know what's changed, so update whole message + + TCursorSelection& thumbPos = wrapper->ThumbPos(); + if ( thumbPos.iAnchorPos != 0 ) + { + TInt oldLength( thumbPos.Length() ); + + // update thumbnail + if ( thumbPos.Length() > 0 ) + { + // delete old thumb + iRichText->DeleteL( thumbPos.iAnchorPos, oldLength ); + + TCursorSelection& sel = wrapper->Selection(); + TCursorSelection& wholeSel = wrapper->WholeSelection(); + + sel.iCursorPos -= thumbPos.Length(); + wholeSel.iCursorPos -= thumbPos.Length(); + } + AddThumbL( thumbPos.iAnchorPos, *wrapper ); + TInt newLength = wrapper->ThumbPos().Length(); + + // if there are messages after this one, + // change the selections of those so that they + // are correct after we've added some characters + UpdateWrappers( aIndex + 1, newLength - oldLength ); + + // inform of deleted and inserted text + if ( !wrapper->IsInserted() ) + { + iTextView.HandleInsertDeleteL( wrapper->ThumbPos(), oldLength ); + } + } + + if ( !wrapper->IsInserted() ) + { + iTextView.HandleFormatChangedL( wrapper->Selection(), + iHighlightState != ENoHighlight ); + } + else + { + // HandleGlobalChangeNoRedrawL worked before, but not anymore.. dunno why. + // We have to use this instead. + TCursorSelection sel = CurrentSelection(); + iTextView.HandleAdditionL( ETrue, sel, EFalse ); + } + + // Scroll view correctly + TCursorSelection sel = CurrentSelection(); + iTextView.ScrollVisibleL( sel, EFalse ); + + //Messages whose content are images + //have completed the processing + if ( iMsgAddedToContentProcessor ) + { + //decrement the messages which has completed processing + iMsgAddedToContentProcessor--; + + if ( !iScrollOver ) + { + //only for recorded chats iScrollOver=EFalse + //put the focus on the first item + MoveHighlightL( ENext ); + } + + else + { + //when user navigate among tab. + //for conversations/groupview + if ( ( iHighlightState == ENoHighlight ) && + !iMsgAddedToContentProcessor && + iAddMsgObserver ) + { + iAddMsgObserver->HandleMessageAddedL( iMessages.Count() ); + } + } + } + } + + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::RemoveMessage +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::RemoveMessage( TInt aIndex ) + { + __CHAT_ASSERT_DEBUG( aIndex >= 0 && aIndex < iMessages.Count() ); + CCAMessageWrapper* wrapper = iMessages[ aIndex ]; + + TCursorSelection& sel = wrapper->WholeSelection(); + TInt selectionLength( sel.Length() ); + + TRAPD( err, iRichText->DeleteL( sel.LowerPos(), selectionLength ) ) + if ( err != KErrNone ) + { + // nothing else we could do if the rich text deletion, + // for some reason, didn't succeed. + CActiveScheduler::Current()->Error( err ); + } + else + { + // inform viewer of deletion + TCursorSelection startPos( sel.LowerPos(), sel.LowerPos() ); + TRAPD( err, iTextView.HandleInsertDeleteL( startPos, selectionLength ) ); + if ( err != KErrNone ) + { + CActiveScheduler::Current()->Error( err ); + } + iTextView.Redraw(); + + // message deleted from rich text, we must update the + // positions of all messages after this one + UpdateWrappers( aIndex + 1, -selectionLength ); + + // delete wrapper + delete wrapper; + iMessages.Remove( aIndex ); + + // check selection + TInt count( iMessages.Count() ); + if ( iSelected >= count ) + { + iSelected = count - 1; + if ( iSelected < 0 ) + { + // no messages, nothing to highlight + iSelected = 0; + TRAPD( err, DisableHighlightL() ); + if ( err != KErrNone ) + { + CActiveScheduler::Current()->Error( err ); + } + } + } + + // check scroll position + TCursorSelection scroll( iTextView.ScrollSelection() ); + TInt docLen( iRichText->DocumentLength() ); + if ( scroll.HigherPos() >= docLen && docLen ) // no need for scroll with zero doclen + { + // update if it was wrong + scroll.iAnchorPos = docLen - 1; + scroll.iCursorPos = docLen - 1; + TRAPD( err, iTextView.ScrollVisibleL( scroll, EFalse ) ); + if ( err != KErrNone ) + { + CActiveScheduler::Current()->Error( err ); + } + } + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::IsDeleted +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCARichTextContainer::IsDeleted() const + { + return iMessagesDeleted; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::MarkDeleted +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::MarkDeleted() + { + iMessagesDeleted = ETrue; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::InsertMessageL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::InsertMessageL( MCAConversationMessage& aMessage ) + { + if ( !iShowTimeStamps && + aMessage.SystemMessageType() == TEnumsPC::ESystemMessageDateChange ) + { + // if timestamp setting is OFF we don't show day change items + return; + } + // create wrapper + CCAMessageWrapper* wrapper = CCAMessageWrapper::NewL( aMessage, *this ); + + CleanupStack::PushL( wrapper ); + iMessages.InsertL( wrapper, 0 ); // takes the ownership + CleanupStack::Pop( wrapper ); + + if ( aMessage.ContentType() == TEnumsPC::EContentPicture ) + { + if ( IMUtils::ContentProtectedL( aMessage.ContentData() ) ) + { + aMessage.SetProcessState( TEnumsPC::EContentNotSupported ); + } + else + { + // add thumbnail processor for message + aMessage.AddContentProcessorL( iThumbSize ); + + //once any message added to the content processor + //increment this iMsgAddedToContentProcessor + iMsgAddedToContentProcessor++; + + } + } + + TInt oldLen = iRichText->DocumentLength(); + + // Insert paragraph break if this is not the last message + TInt paraBreak = 0; + if ( iMessages.Count() > 1 ) + { + iRichText->InsertL( 0, CEditableText::EParagraphDelimiter ); + paraBreak = 1; + } + + // Insert message to rich text in reverse order compared to adding + TInt contentLen = InsertContentL( *wrapper ); + TCursorSelection nickSel = InsertNickL( *wrapper ); + TCursorSelection timeSel = InsertTimeStampL( *wrapper ); + // IM client UI customization, phase 2 + // Based on Variation flag, enables User Identity with Time Stamp + // in chat conversation view. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + TCursorSelection userSel; + if ( requireUserIdentity ) + { + userSel = InsertUserIdentityL( *wrapper ); + } + + TInt dirChrLen = iRichText->DocumentLength(); + iRichText->InsertL( 0, AknLayoutUtils::LayoutMirrored() ? + KUnicodeRLM : KUnicodeLRM ); + dirChrLen = iRichText->DocumentLength() - dirChrLen; + + wrapper->SetInserted( ETrue ); + + // read variation flag values + //TInt needUserIdentity = IntResourceValueL(RSC_CHAT_VARIATION_SHOW_USER_IDENTITY); + TInt needUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + if ( needUserIdentity ) + { + // Update user selection + userSel.iAnchorPos += dirChrLen; + userSel.iCursorPos += dirChrLen; + // Update timestamp selection + timeSel.iAnchorPos += userSel.HigherPos(); + timeSel.iCursorPos += userSel.HigherPos(); + // Update nick selection + nickSel.iAnchorPos += timeSel.HigherPos(); + nickSel.iCursorPos += timeSel.HigherPos(); + } + else + { + // Update timestamp selection + timeSel.iAnchorPos += dirChrLen; + timeSel.iCursorPos += dirChrLen; + // Update nick selection + nickSel.iAnchorPos += timeSel.HigherPos(); + nickSel.iCursorPos += timeSel.HigherPos(); + } + // Update thumb pos + TCursorSelection& thumbPos = wrapper->ThumbPos(); + thumbPos.iAnchorPos += nickSel.HigherPos() + KSeparator().Length(); + thumbPos.iCursorPos += nickSel.HigherPos() + KSeparator().Length(); + + TInt addedLen = iRichText->DocumentLength() - oldLen; + + // parse text for highlights and store positions to messagewrapper + HBufC* text = HBufC::NewMaxLC( contentLen ); + TPtr txt( text->Des() ); + iRichText->Extract( txt, + nickSel.HigherPos() + KSeparator().Length() + 1, // Don't extract separator and space + contentLen ); + ParseTextL( txt, nickSel.HigherPos() + KSeparator().Length() + 1, + nickSel.HigherPos() + KSeparator().Length() + 1, *wrapper ); + CleanupStack::PopAndDestroy( text ); + + // Set selection for highlight and the whole selection + // so that this message can be removed + wrapper->Selection().SetSelection( addedLen - 1 - paraBreak, 0 ); + wrapper->WholeSelection().SetSelection( addedLen - 1, 0 ); + wrapper->MessageSelection().SetSelection( addedLen - 1, + nickSel.HigherPos() + 1 ); + + if ( iHighlightState != ENoHighlight ) + { + BackColorL( wrapper->WholeSelection(), KRgbWhite ); + TextBackColorL( wrapper->WholeSelection(), KRgbWhite ); + } + + // Update wrappers after this if there is any + if ( iMessages.Count() > 1 ) + { + UpdateWrappers( 1, addedLen ); + } + + // Append coloring + TCursorSelection& sel = iColorWholeMessage ? wrapper->Selection() : nickSel; + TInt len( sel.Length() ); + CHAT_DP( D_CHAT_LIT( "selectiong length %d" ), len ); + + if ( len > 0 && + aMessage.MessageType() != TEnumsPC::EMessageSystem ) + { + // Custom colors + if ( aMessage.MessagerType() == TEnumsPC::EMessageSent ) + { + // Own msg + if ( iOwnMsgColorInUse ) + { + TextColorL( sel, iOwnMsgColor ); + } + else + { + //default color + TextColorL( sel, iDefaultMsgColor ); + } + + } + else if ( aMessage.MessagerType() == TEnumsPC::EMessageReceived ) + { + // Other msg + if ( iMsgColorInUse ) + { + TextColorL( sel, iMsgColor ); + } + else + { + //default color + TextColorL( sel, iDefaultMsgColor ); + } + } + } + + if ( len > 0 && + aMessage.MessageType() == TEnumsPC::EMessageSystem ) + { + TextColorL( sel, iDefaultMsgColor ); + } + // Inform of inserted rich text + TInt error = KErrNone; + if ( oldLen == 0 ) + { + TCursorSelection curSel( CurrentSelection() ); + // First message inserted, format the whole text + TRAP( error, iTextView.HandleAdditionL( ETrue, curSel, EFalse ) ); + } + else + { + TCursorSelection curSel( CurrentSelection() ); + // There might be a faster method to handle text addition, but + // this works for now. + TRAP( error, iTextView.HandleAdditionL( ETrue, curSel, EFalse ) ); + } + + if ( error != KErrNone ) + { + // Remove the failed wrapper and leave + wrapper->Message().RemoveProcessingL(); + TInt index = iMessages.Find( wrapper ); + if ( index != KErrNotFound ) + { + delete wrapper; + iMessages.Remove( index ); + } + User::Leave( error ); + } + + // And scroll (unless in highlight-mode) + if ( iHighlightState == ENoHighlight ) + { + TCursorSelection curSel( CurrentSelection() ); + iTextView.ScrollVisibleL( curSel, EFalse ); + + //if no messages are being processed for the content + //i.e., all messages are text messages + if ( !iMsgAddedToContentProcessor && iAddMsgObserver ) + { + iAddMsgObserver->HandleMessageAddedL( iMessages.Count() ); + } + } + else + { + // In highlight mode, message inserted -> increase selected + // message index and previous selection indexes + iSelected++; + iPrevious.iAnchorPos += addedLen; + iPrevious.iCursorPos += addedLen; + + TCursorSelection curSel( CurrentSelection() ); + iTextView.ScrollVisibleL( curSel, EFalse ); + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::UpdateWrappers +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::UpdateWrappers( TInt aFirstIndex, TInt aChange ) + { + TInt count( iMessages.Count() ); + for ( TInt i( aFirstIndex ); i < count; ++i ) + { + CCAMessageWrapper* wrapper = iMessages[i]; + TCursorSelection& selection = wrapper->Selection(); + TCursorSelection& wholeSelection = wrapper->WholeSelection(); + TCursorSelection& thumbPos = wrapper->ThumbPos(); + selection.iAnchorPos += aChange; + selection.iCursorPos += aChange; + wholeSelection.iAnchorPos += aChange; + wholeSelection.iCursorPos += aChange; + thumbPos.iAnchorPos += aChange; + thumbPos.iCursorPos += aChange; + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::Union +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::Union( const TCursorSelection& aSel1, + const TCursorSelection& aSel2 ) + { + // make union of selections + TCursorSelection changed; + changed.iAnchorPos = aSel1.LowerPos(); + changed.iCursorPos = aSel1.HigherPos(); + + TInt low( aSel2.LowerPos() ); + TInt high( aSel2.HigherPos() ); + if ( low < changed.iAnchorPos ) + { + changed.iAnchorPos = low; + } + if ( high > changed.iCursorPos ) + { + changed.iCursorPos = high; + } + + return changed; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::CurrentSelection +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::CurrentSelection() const + { + TCursorSelection sel( 0, 0 ); + if ( iHighlightState == ENoHighlight ) + { + // if nothing highlighted, return last message + TInt count( iMessages.Count() ); + if ( count > 0 ) + { + sel = iMessages[count-1]->Selection(); + } + } + else + { + // something highlighted, start with current message + sel = iMessages[iSelected]->Selection(); + + // check if the highlighted selection is item inside message + if ( iHighlightState == EItemSelected ) + { + // calculate item highlight position + TCursorSelection itemSel( + iMessages[iSelected]->Highlights()[iSelectedItem] ); + + TInt messageStart( sel.LowerPos() ); + sel.iAnchorPos = itemSel.iAnchorPos + messageStart; + sel.iCursorPos = itemSel.iCursorPos + messageStart; + } + } + + return sel; + } + + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::TextBackColorL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::TextBackColorL( const TCursorSelection& aSel, + const TRgb& aColor ) + { +#ifdef _DEBUG + TInt len( iRichText->DocumentLength() ); + CHAT_DP( D_CHAT_LIT( + "CCARichTextContainer::TextBackColorL selection: %d,%d, textlen %d" ), + aSel.iAnchorPos, aSel.iCursorPos, len ); +#endif //_DEBUG + + TCharFormat charFormat; + TCharFormatMask charFormatMask; + charFormatMask.SetAttrib( EAttFontHighlightColor ); + charFormatMask.SetAttrib( EAttFontHighlightStyle ); + + charFormat.iFontPresentation.iHighlightColor = aColor; + charFormat.iFontPresentation.iHighlightStyle = aColor == KRgbWhite ? + TFontPresentation::EFontHighlightNone : + TFontPresentation::EFontHighlightNormal; + + iRichText->ApplyCharFormatL( charFormat, charFormatMask, + aSel.LowerPos(), + aSel.Length() ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::TextColorL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::TextColorL( const TCursorSelection& aSel, + const TRgb& aColor ) + { +#ifdef _DEBUG + TInt len( iRichText->DocumentLength() ); + CHAT_DP( D_CHAT_LIT( + "CCARichTextContainer::TextColorL selection: %d,%d, textlen %d" ), + aSel.iAnchorPos, aSel.iCursorPos, len ); +#endif //_DEBUG + TCharFormat charFormat; + TCharFormatMask charFormatMask; + charFormatMask.SetAttrib( EAttColor ); + charFormat.iFontPresentation.iTextColor = aColor; + + iRichText->ApplyCharFormatL( charFormat, charFormatMask, + aSel.LowerPos(), + aSel.Length() ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::BackColorL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::BackColorL( const TCursorSelection& aSel, + const TRgb& aColor ) + { +#ifdef _DEBUG + TInt len( iRichText->DocumentLength() ); + CHAT_DP( D_CHAT_LIT( + "CCARichTextContainer::BackColorL selection: %d,%d, textlen %d" ), + aSel.iAnchorPos, aSel.iCursorPos, len ); +#endif //_DEBUG + + CParaFormat paraFormat; + TParaFormatMask paraFormatMask; + paraFormatMask.SetAttrib( EAttFillColor ); + + paraFormat.iFillColor = aColor; + iRichText->ApplyParaFormatL( ¶Format, paraFormatMask, + aSel.LowerPos(), + aSel.Length() ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::HighLightItemL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::HighLightItemL( const TCursorSelection& aSel ) + { +#ifdef _DEBUG + TInt len( iRichText->DocumentLength() ); + CHAT_DP( D_CHAT_LIT( + "CCARichTextContainer::HighLightItemL selection: %d,%d, textlen %d" ), + aSel.iAnchorPos, aSel.iCursorPos, len ); +#endif //_DEBUG + + TCharFormat charFormat; + TCharFormatMask charFormatMask; + charFormatMask.SetAttrib( EAttFontUnderline ); + + charFormat.iFontPresentation.iUnderline = EUnderlineOn; + + iRichText->ApplyCharFormatL( charFormat, charFormatMask, + aSel.LowerPos(), + aSel.Length() ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::BoldL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::BoldL( const TCursorSelection& aSel ) + { +#ifdef _DEBUG + TInt len( iRichText->DocumentLength() ); + CHAT_DP( D_CHAT_LIT( + "CCARichTextContainer::BoldL selection: %d,%d, textlen %d" ), + aSel.iAnchorPos, aSel.iCursorPos, len ); +#endif //_DEBUG + + TCharFormat charFormat; + TCharFormatMask charFormatMask; + + charFormat.iFontSpec.iFontStyle.SetStrokeWeight( EStrokeWeightBold ); + charFormatMask.SetAttrib( EAttFontStrokeWeight ); + + iRichText->ApplyCharFormatL( charFormat, charFormatMask, + aSel.LowerPos(), + aSel.Length() ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::ParseTextL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::ParseTextL( const TDesC& aText, TInt aStartPos, + TInt aRelativePos, CCAMessageWrapper& aMessageWrapper ) + { + CFindItemEngine* findItem = CFindItemEngine::NewL( aText, + CFindItemEngine::TFindItemSearchCase( + CFindItemEngine::EFindItemSearchPhoneNumberBin | + CFindItemEngine::EFindItemSearchMailAddressBin | + CFindItemEngine::EFindItemSearchURLBin/* | + CFindItemEngine::EFindItemSearchScheme*/ ) + ); + + CleanupStack::PushL( findItem ); + + // reset array + RArray& highlights = aMessageWrapper.Highlights(); + RArray& types = aMessageWrapper.HighlightTypes(); + highlights.Reset(); + types.Reset(); + + // get found items + const CArrayFixFlat* items = + findItem->ItemArray(); + TInt count( items->Count() ); + + for ( TInt i( 0 ); i < count; ++i ) + { + const CFindItemEngine::SFoundItem item = items->At( i ); + + // selection in rich text + TCursorSelection realSel( aStartPos + item.iStartPos + item.iLength, + aStartPos + item.iStartPos ); + + // relative selection inside one message + TCursorSelection relativeSel( aRelativePos + item.iStartPos + + item.iLength, + aRelativePos + item.iStartPos ); + + highlights.AppendL( relativeSel ); + types.AppendL( item.iItemType ); + + // apply highlight item formatting + HighLightItemL( realSel ); + } + + CleanupStack::PopAndDestroy( findItem ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::AddNickL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::AddNickL( + CCAMessageWrapper& aMessageWrapper ) + { + + + // insert nick + MCAConversationMessage& message = aMessageWrapper.Message(); + + if ( message.FailedMessage() ) + { + return TCursorSelection( 0, 0 ); + } + + TPtrC sender; + TPtrC recipient; + + if ( message.MessageType() == TEnumsPC::EMessageGroup || + message.MessageType() == TEnumsPC::EMessageWhisper ) + { + sender.Set( message.Sender() ); + recipient.Set( message.Recipient() ); + } + else + { + MCAStoredContacts* contacts = + CCAStorageManagerFactory::ContactListInterfaceL(); + if ( message.MessagerType() == TEnumsPC::EMessageSent + && message.MessageType() != TEnumsPC::EMessageSystem ) + { + sender.Set( contacts->OwnStatus().Identification() ); + } + else + { + sender.Set( contacts->Identification( message.Sender() ) ); + } + + recipient.Set( contacts->Identification( message.Recipient() ) ); + } + TCursorSelection nickSel( 0, 0 ); + + if ( message.MessagerType() == TEnumsPC::EMessageSent && + message.MessageType() == TEnumsPC::EMessagePTOP && + iOwnMsgIcon ) + { + // this is sent (or failed) p2p message, + // insert "from me" icon and separator + // (if icons are in use) + + // IM client UI customization, phase 2 + // Based on Variation flag, changes Nick format in conversation view + // removes arrow sign and append only a seperator. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + // and check dynamic features + if ( !requireUserIdentity ) + { + CCAPicture* pic = new( ELeave )CCAPicture( iMap, iFromMe ); + TPictureHeader header; + header.iPicture = TSwizzle( pic ); + iRichText->CancelInsertCharFormat(); // to prevent ETEXT 31 panic + iRichText->InsertL( iRichText->DocumentLength(), header ); // takes ownership + } + iRichText->InsertL( iRichText->DocumentLength(), KSeparator ); + } + else + { + TInt nickStart( iRichText->DocumentLength() ); + TInt nickEnd( nickStart ); + + if ( message.MessageType() == TEnumsPC::EMessageWhisper ) + { + // This is received whisper message (to us), + // insert sender, "to me" icon and separator + iRichText->InsertL( nickStart, sender ); + nickEnd = iRichText->DocumentLength(); + + CCAPicture* pic = NULL; + if ( message.MessagerType() == TEnumsPC::EMessageSent ) + { + pic = new( ELeave )CCAPicture( iMap, + iFromMe ); + } + else + { + pic = new( ELeave )CCAPicture( iMap, + iToMe ); + } + TPictureHeader header; + header.iPicture = TSwizzle( pic ); + iRichText->CancelInsertCharFormat(); // to prevent ETEXT 31 panic + iRichText->InsertL( nickEnd, header ); // takes ownership + iRichText->InsertL( iRichText->DocumentLength(), recipient ); + nickEnd = iRichText->DocumentLength(); + iRichText->InsertL( iRichText->DocumentLength(), KSeparator ); + } + else if ( message.MessageType() == TEnumsPC::EMessagePTOP && + message.MessagerType() == TEnumsPC::EMessageReceived && + iMsgIcon ) + { + // received p2p message and we want to display icon + // insert "to me" icon and separator + // IM client UI customization, phase 2 + // Based on Variation flag, changes Nick format in conversation view + // removes arrow sign and append only a seperator. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + // and check dynamic features + if ( !requireUserIdentity ) + { + CCAPicture* pic = new( ELeave )CCAPicture( iMap, + iToMe ); + TPictureHeader header; + header.iPicture = TSwizzle( pic ); + iRichText->CancelInsertCharFormat(); // to prevent ETEXT 31 panic + iRichText->InsertL( iRichText->DocumentLength(), header ); // takes ownership + } + nickEnd = iRichText->DocumentLength(); + iRichText->InsertL( iRichText->DocumentLength(), KSeparator ); + } + else if ( sender.Length() > 0 ) + { + // this is normal group message (or p2p message if icons are not used), + // insert sender and separator + iRichText->InsertL( nickStart, sender ); + nickEnd = iRichText->DocumentLength(); + iRichText->InsertL( iRichText->DocumentLength(), KSeparator ); + } + + // formatting (bold nickname) + nickSel.SetSelection( nickStart, nickEnd ); + if ( nickSel.Length() > 0 ) + { + BoldL( nickSel ); + } + } + return nickSel; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::AddTimeStampL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::AddTimeStampL( + CCAMessageWrapper& aMessageWrapper ) + { + TInt start = iRichText->DocumentLength(); + TCursorSelection timeSel( start, start ); + + if ( !iSettings.GetBoolValuePC( TEnumsPC::EShowTimeStamps, NULL ) || + aMessageWrapper.Message().SystemMessageType() == + TEnumsPC::ESystemMessageDateChange ) + { + // 1. Setting for time stamp is OFF + // 2. Special case: date change don't have time stamp. + return timeSel; + } + + // format time + HBufC* timeStamp = HBufC::NewLC( KTimeStampMaxLength + KSpace().Length() ); + TPtr timePtr( timeStamp->Des() ); + + HBufC* timeStampFormat = NULL; + timeStampFormat = StringLoader::LoadLC( R_QTN_TIME_USUAL_WITH_ZERO ); + + TTime time = aMessageWrapper.Message().TimeStamp(); + TRAPD( err, time.FormatL( timePtr, *timeStampFormat ) ); + if ( err == KErrOverflow ) + { + // Reserved text space was not enough. + // Adjust the KTimeStampMaxLength + __CHAT_ASSERT_DEBUG( EFalse ); + + // in release builds return without timestamp + CleanupStack::PopAndDestroy( 2, timeStamp ); // timeStamp, timeStampFormat + return timeSel; + } + + // Convert numbers to local language + AknTextUtils::LanguageSpecificNumberConversion( timePtr ); + + // Set Alignment of Pm/Am from local setting + // This keeps the am/pm text close to the time. + timePtr.Append( AknLayoutUtils::LayoutMirrored() ? + KUnicodeRLM : KUnicodeLRM ); + + // IM client UI customization, phase 2 + // Based on Variation flag, changes Time Stamp format in conversation view. + // where Time Stamp should appear in brackets. + + // read variation flag values + TInt requireUserIdentity = IMUtils::IntResourceValueL( RSC_CHAT_VARIATION_SHOW_USER_IDENTITY ); + // and check dynamic features + if ( requireUserIdentity ) + { + // append time to rich text + timePtr.Insert( 0, _L( "(" ) ); + timePtr.Append( _L( ")" ) ); + timePtr.Append( KSpace ); + } + else + { + timePtr.Append( KSpace ); + } + iRichText->InsertL( start, timePtr ); + + CleanupStack::PopAndDestroy( 2, timeStamp ); // timeStamp, timeStampFormat + timeSel.SetSelection( start, iRichText->DocumentLength() - 1 ); + + // bold the time + if ( timeSel.Length() > 0 ) + { + BoldL( timeSel ); + } + + return timeSel; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::AddUserIdentityL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::AddUserIdentityL( + CCAMessageWrapper& aMessageWrapper ) + { + TInt start = iRichText->DocumentLength(); + TCursorSelection userSel( start, start ); + + MCAConversationMessage& message = aMessageWrapper.Message(); + TPtrC sender; + TPtrC recipient; + MCAStoredContacts* contacts = CCAStorageManagerFactory::ContactListInterfaceL(); + if ( message.MessagerType() == TEnumsPC::EMessageSent + && message.MessageType() != TEnumsPC::EMessageSystem ) + { + sender.Set( contacts->OwnStatus().Identification() ); + } + else + { + sender.Set( contacts->Identification( message.Sender() ) ); + } + recipient.Set( contacts->Identification( message.Recipient() ) ); + + if ( message.MessagerType() == TEnumsPC::EMessageSent && + message.MessageType() == TEnumsPC::EMessagePTOP && + iOwnMsgIcon ) + { + if ( sender.Length() >= KMaxLength ) + { + TPtrC aTempPtr; + aTempPtr.Set( sender.Left( 8 ) ); + TBuf<10> aTruncateSender( aTempPtr ); + aTruncateSender.Append( _L( ".." ) ); + iRichText->InsertL( start, aTruncateSender ); + } + else + { + iRichText->InsertL( iRichText->DocumentLength(), sender ); + } + } + else if ( message.MessageType() == TEnumsPC::EMessagePTOP && + message.MessagerType() == TEnumsPC::EMessageReceived && + iMsgIcon ) + { + if ( recipient.Length() >= KMaxLength ) + { + TPtrC aTempPtr; + aTempPtr.Set( recipient.Left( 8 ) ); + TBuf<10> aTruncateRecipient( aTempPtr ); + aTruncateRecipient.Append( _L( ".." ) ); + iRichText->InsertL( iRichText->DocumentLength(), aTruncateRecipient ); + } + else + { + iRichText->InsertL( iRichText->DocumentLength(), recipient ); + } + } + iRichText->InsertL( iRichText->DocumentLength(), KSpace ); + userSel.SetSelection( start, iRichText->DocumentLength() - 1 ); + + // bold the time + if ( userSel.Length() > 0 ) + { + BoldL( userSel ); + } + + return userSel; + } +// ----------------------------------------------------------------------------- +// CCARichTextContainer::AddContentL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::AddContentL( TInt aMsgStart, + CCAMessageWrapper& aMessageWrapper ) + { + MCAConversationMessage& message = aMessageWrapper.Message(); + + TEnumsPC::TContentType type( message.ContentType() ); + switch ( type ) + { + case TEnumsPC::EContentText: + { + // insert text + TPtrC msg( message.Text() ); + + HBufC* tempMsg = NULL; + + TInt paraBreak = msg.Locate( CEditableText::EParagraphDelimiter ); + // Replace all paragraph delimiters with line breaks + // to keep alingment consistent + if ( paraBreak != KErrNotFound ) + { + tempMsg = msg.AllocLC(); + TPtr ptr( tempMsg->Des() ); + ptr.Zero(); + + ReplaceParaDelimsWithLineBreaks( msg, ptr ); + msg.Set( ptr ); + } + + HBufC* numberConv = NULL; + + // Convert numerals to local language + // 1) date item + if ( aMessageWrapper.Message().SystemMessageType() == + TEnumsPC::ESystemMessageDateChange ) + { + // don't touch the original message + numberConv = msg.AllocLC(); + TPtr ptr( numberConv->Des() ); + AknTextUtils::LanguageSpecificNumberConversion( ptr ); + msg.Set( ptr ); + } + + TInt textStart( iRichText->DocumentLength() ); + iRichText->InsertL( textStart, msg ); + + // check for extensions (smileys) + TCursorSelection selection( iRichText->DocumentLength(), + textStart ); + iExtensionsHandler.ConvertSelectionToExtensionL( *iRichText, + selection ); + + TInt textEnd( iRichText->DocumentLength() ); + + // add nbs to preserve formatting + iRichText->InsertL( iRichText->DocumentLength(), + CEditableText::EZeroWidthNoBreakSpace ); + + // if this is system message, it should be bold + if ( message.MessageType() == TEnumsPC::EMessageSystem ) + { + TCursorSelection sel( textStart, textEnd ); + BoldL( sel ); + // System messages need different kind of alignment + CParaFormat paraFormat; + TParaFormatMask paraFormatMask; + paraFormat.iLeftMarginInTwips = 0; + paraFormatMask.SetAttrib( EAttLeftMargin ); + iRichText->ApplyParaFormatL( ¶Format, paraFormatMask, + textStart, sel.Length() ); + } + else + { + // Apply alignment for "normal" messages + CParaFormat paraFormat; + TParaFormatMask paraFormatMask; + paraFormat.iLeftMarginInTwips = 100; + paraFormatMask.SetAttrib( EAttLeftMargin ); + iRichText->ApplyParaFormatL( ¶Format, paraFormatMask, + textStart, textEnd - textStart ); + } + + // parse text for highlights and store positions to messagewrapper + HBufC* text = HBufC::NewMaxLC( textEnd - textStart ); + TPtr txt( text->Des() ); + iRichText->Extract( txt, textStart, textEnd - textStart ); + ParseTextL( txt, textStart, textStart - aMsgStart, aMessageWrapper ); + CleanupStack::PopAndDestroy( text ); + + // Cleanup + if ( numberConv ) + { + CleanupStack::PopAndDestroy( numberConv ); + } + if ( tempMsg ) + { + CleanupStack::PopAndDestroy( tempMsg ); + } + break; + } + case TEnumsPC::EContentPicture: // Flowthrough + case TEnumsPC::EContentOther: + { + // insert thumbnail (if it's ready) + AddThumbL( iRichText->DocumentLength(), aMessageWrapper ); + break; + } + case TEnumsPC::EContentInvalid: + { + break; + } + default: + { + // unsupported type + __CHAT_ASSERT_DEBUG( false ); + break; + } + } + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::AddThumbL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::AddThumbL( TInt aPos, + CCAMessageWrapper& aMessageWrapper ) + { + TCursorSelection& thumb = aMessageWrapper.ThumbPos(); + thumb.iAnchorPos = aPos; + thumb.iCursorPos = aPos; + + MCAConversationMessage& message = aMessageWrapper.Message(); + TEnumsPC::TContentType type = message.ContentType(); + if ( type == TEnumsPC::EContentPicture || + type == TEnumsPC::EContentOther ) + { + // this is a content message + CFbsBitmap* bitmap = NULL; + CFbsBitmap* mask = NULL; + switch ( message.ContentProcessState() ) + { + case TEnumsPC::EThumbnailReady: + { + // get the thumbnail from message + bitmap = message.Thumbnail(); + break; + } + case TEnumsPC::EContentNotSupported: + { + bitmap = iUnsupported->Bitmap(); + mask = iUnsupported->Mask(); + break; + } + case TEnumsPC::EContentCorrupted: + case TEnumsPC::EContentNotSupportedDrm: + { + bitmap = iCorrupted->Bitmap(); + mask = iCorrupted->Mask(); + break; + } + default: + { + // other types do not have thumbnails. + return; + } + } + + // create gulicon + CGulIcon* icon = CGulIcon::NewL( bitmap, mask ); + //we don`t own the bitmap, just the CGulIcon + icon->SetBitmapsOwnedExternally( ETrue ); + CleanupStack::PushL( icon ); + + // insert in to rich text + CCAPicture* pic = new( ELeave )CCAPicture( iMap, icon, KErrNotFound, EFalse ); + CleanupStack::Pop( icon ); //ownership is taken by pic + + TSize thumbnailSize; + pic->GetOriginalSizeInTwips( thumbnailSize ); + + CleanupStack::PushL( pic ); + TPictureHeader header; + header.iPicture = TSwizzle( pic ); + iRichText->CancelInsertCharFormat(); // to prevent ETEXT 31 panic + + // new paragraph, image and new paragraph + // 1 char for EParagraphDelimiter + // 1 char for picture + // 1 char for EZeroWidthNoBreakSpace + // 1 char for EParagraphDelimiter + TInt pos = aPos; + iRichText->InsertL( pos++, CEditableText::EParagraphDelimiter ); + TInt picStart = pos; + CleanupStack::Pop( pic ); + iRichText->InsertL( pos++, header ); // takes ownership + iRichText->InsertL( pos++, CEditableText::EZeroWidthNoBreakSpace ); + TInt picEnd = pos; + iRichText->InsertL( pos++, CEditableText::EParagraphDelimiter ); + + // store length + thumb.iCursorPos = pos; + + // update wrapper + TCursorSelection& sel = aMessageWrapper.Selection(); + TCursorSelection& wholeSel = aMessageWrapper.WholeSelection(); + sel.iCursorPos += thumb.Length(); + wholeSel.iCursorPos += thumb.Length(); + + // append formatting + CParaFormat paraFormat; + TParaFormatMask paraFormatMask; + paraFormat.iIndentInTwips = 0; + paraFormat.iLeftMarginInTwips = 100; + paraFormatMask.SetAttrib( EAttIndent ); + paraFormatMask.SetAttrib( EAttLeftMargin ); + + paraFormat.iLineSpacingInTwips = thumbnailSize.iHeight; + paraFormatMask.SetAttrib( EAttLineSpacing ); + + // apply formatting to picture character only + iRichText->ApplyParaFormatL( ¶Format, paraFormatMask, picStart, + picEnd - picStart ); + + CHAT_DP( D_CHAT_LIT( "*Added thumbnail to %d-%d" ), + thumb.iAnchorPos, thumb.iCursorPos ); + } + } +// ----------------------------------------------------------------------------- +// CCARichTextContainer::UpdateSkinnedTextColourL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::UpdateSkinnedTextColourL() + { + // Get current text's color + TRgb defaultSkinTextColor( KRgbBlack ); + AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), + defaultSkinTextColor, + KAknsIIDQsnTextColors, + EAknsCIQsnTextColorsCG6 ); + + iDefaultMsgColor = defaultSkinTextColor; + TCharFormat charFormat; + TCharFormatMask charFormatMask; + charFormat.iFontPresentation.iTextColor = defaultSkinTextColor; + charFormatMask.SetAttrib( EAttColor ); + + // The color settings is read from the resource file only, so IntResourceValueFromRssL is used instead of IntResourceValueL + TBool defaultColors = IMUtils::IntResourceValueFromRssL( + RSC_CHAT_DEFAULT_MESSAGE_COLORS ); + + // Update text colour in all messages + TInt messagecount = iMessages.Count(); + for ( TInt i = 0; i < messagecount; ++i ) + { + MCAConversationMessage &msg = iMessages[ i ]->Message(); + TCursorSelection sel( 0, 0 ); + + // system message use always default color + if ( msg.MessageType() == TEnumsPC::EMessageSystem ) + { + sel = iMessages[ i ]->WholeSelection(); + } + // Sent messages + else if ( msg.MessagerType() == TEnumsPC::EMessageSent ) + { + sel = SkinColoredSelection( *iMessages[ i ], + defaultColors, + iOwnMsgColorInUse ); + } + // Received messages + else if ( msg.MessagerType() == TEnumsPC::EMessageReceived ) + { + sel = SkinColoredSelection( *iMessages[ i ], + defaultColors, + iMsgColorInUse ); + } + + // Only update texts which use default color. + if ( sel.Length() > 0 ) + { + iRichText->ApplyCharFormatL( charFormat, + charFormatMask, + sel.LowerPos(), + sel.Length() ); + } + } + // also appended messages should have new colour + iRichText->SetInsertCharFormatL( charFormat, charFormatMask, iRichText->DocumentLength() ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::SkinColoredSelection +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCursorSelection CCARichTextContainer::SkinColoredSelection( + CCAMessageWrapper& aMessageWrapper, + TBool aUseDefaultColors, + TBool aCustomDefinedColor ) + { + // This logic determines which part of the message is colored by the theme + if ( !aCustomDefinedColor && aUseDefaultColors ) + { + return aMessageWrapper.WholeSelection(); + } + + if ( !iColorWholeMessage ) + { + return aMessageWrapper.MessageSelection(); + } + + return TCursorSelection( 0, 0 ); + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::ReplaceParaDelimsWithLineBreaks +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::ReplaceParaDelimsWithLineBreaks( + const TPtrC& aSource, + TPtr& aTarget ) const + { +#ifdef _DEBUG + TPtrC target; + target.Set( aTarget ); + CHAT_DP( D_PLAIN_LIT( "CCARichTextContainer::ReplaceParaDelimsWithLineBreaks: target = %S" ), &target ); +#endif + + TInt paraBreak = aSource.Mid( aTarget.Length() ).Locate( + CEditableText::EParagraphDelimiter ); + + if ( paraBreak == KErrNotFound ) + { + // No more paragraph delimiters, append what's left + aTarget.Append( aSource.Mid( aTarget.Length() ) ); + // And break away from recursion + return; + } + + aTarget.Append( aSource.Mid( aTarget.Length(), paraBreak ) ); + aTarget.Append( CEditableText::ELineBreak ); + +#ifdef _DEBUG + target.Set( aTarget ); + CHAT_DP( D_PLAIN_LIT( "CCARichTextContainer::ReplaceParaDelimsWithLineBreaks: target = %S" ), &target ); +#endif + + ReplaceParaDelimsWithLineBreaks( aSource, aTarget ); + } + + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::GetMessageCount +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCARichTextContainer::GetMessageCount() + { + return iMessages.Count(); + } +// ----------------------------------------------------------------------------- +// CCARichTextContainer::GetMessageCount +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::ChangeHighlightColorL() + { + // get the selection + TCursorSelection curSel( CurrentSelection() ); + + // remove colors from old highlight + TInt textLen( iRichText->DocumentLength() ); + if ( iPrevious.iAnchorPos < textLen && + iPrevious.iCursorPos <= textLen ) + { + BackColorL( iPrevious, KRgbWhite ); + TextBackColorL( iPrevious, KRgbWhite ); + } + + // and set new one + if ( iHighlightState == EItemSelected ) + { + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + TRgb color; + + TInt error = AknsUtils::GetCachedColor( skin, + color, + KAknsIIDQsnHighlightColors, + EAknsCIQsnHighlightColorsCG2 ); + + if ( !error ) + { + // No error, use skinned background color + TextBackColorL( curSel, color ); + } + else + { + TextBackColorL( curSel, AKN_LAF_COLOR_STATIC( KChatHighlightColor ) ); + } + } + else + { + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + TRgb color; + + TInt error = AknsUtils::GetCachedColor( skin, + color, + KAknsIIDQsnHighlightColors, + EAknsCIQsnHighlightColorsCG2 ); + + if ( !error ) + { + // No error, use skinned background color + BackColorL( curSel, color ); + } + else + { + BackColorL( curSel, AKN_LAF_COLOR_STATIC( KChatHighlightColor ) ); + } + } + + // handle changed format + TCursorSelection changed( Union( iPrevious, curSel ) ); + + iTextView.HandleFormatChangedL( changed ); + + iTextView.ScrollVisibleL( curSel, ETrue ); + + iPrevious = curSel; + } + +// ----------------------------------------------------------------------------- +// CCARichTextContainer::UnRegisterRichTxtCtrlObserver +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCARichTextContainer::UnRegisterRichTxtCtrlObserver() + { + iAddMsgObserver = NULL; + } + +// End of File