diff -r 000000000000 -r 72b543305e3a messagingappbase/smartmessaging/richbio/src/CRichBio.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingappbase/smartmessaging/richbio/src/CRichBio.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,631 @@ +/* +* Copyright (c) 2002 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: +* This is a wrapper class for rich text editor. +* +*/ + + + +// INCLUDES + +#include "CRichBio.h" // Own header +#include // CRichText +#include // Fonts +#include // CMsgExpandableControlEditor +#include // MaxBodyHeight +#include // TAknLayoutId +#include + +#include // LAF +#include + +/// panic codes +enum TRichBioPanicCodes + { + EPanicCacheProblem1 = 100, + EPanicCacheProblem2, + EPanicCacheMismatch1, + EPanicCacheMismatch2, + EPanicCacheMismatch3, + EPanicEditorIsNull1, + EPanicEditorIsNull2, + EPanicNoCache, + EPanicConstructedTwice, + EPanicNoParent + }; + +// LOCAL CONSTANTS + +// Define the margins used in editor here. +const TInt KMarginTop = 0; +const TInt KMarginBottom = 0; +const TInt KMarginLeft = 3; +const TInt KMarginRight = 1; + +// CLASS DECLARATION + +/** +* CRichbio inner class, which implements the observer +* interface. +*/ +class CRichBio::CEdwinSizeObserver : public CBase, + public MEikEdwinSizeObserver + { + public: // from MEikEdwinSizeObserver + + /** + * Constructor. + * @param Reference to the richbio object. + */ + CEdwinSizeObserver( CRichBio& aRichBio ); + + /** + * Destructor. + */ + ~CEdwinSizeObserver() {}; + + /** + * Handles edwin size event. Sets new size for this control and + * reports the observer if the control editor's size is about to + * change. Returns ETrue if size change is allowed. + * @param aEdwin Edwin control. + * @param aEventType Type of the event. + * @param aDesirableEdwinSize control's desired size. + * @return Boolean + */ + TBool HandleEdwinSizeEventL(CEikEdwin* aEdwin, + TEdwinSizeEvent aEventType, + TSize aDesirableEdwinSize); + private: + /// Reference to the richbio control. + CRichBio& iRichBio; + }; + +CRichBio::CEdwinSizeObserver::CEdwinSizeObserver( CRichBio& aRichBio ) : + iRichBio( aRichBio ) + { + } + +// --------------------------------------------------------- +// CEdwinSizeObserver::HandleEdwinSizeEventL +// +// Handles edwin size event. Sets new size for this control and reports the +// observer if the control editor's size is about to change. Returns ETrue +// if size change is allowed. +// --------------------------------------------------------- +// +TBool CRichBio::CEdwinSizeObserver::HandleEdwinSizeEventL( + CEikEdwin* /*aEdwin*/, + TEdwinSizeEvent aEventType, + TSize aDesirableEdwinSize) + { + if (aEventType == EEventSizeChanging) + { + TSize thisSize(iRichBio.Size()); + aDesirableEdwinSize.iHeight = MsgEditorCommons::MaxBodyHeight(); + + TInt delta( aDesirableEdwinSize.iHeight - iRichBio.iEditor->Size().iHeight ); + + if (delta) + { + thisSize.iHeight = aDesirableEdwinSize.iHeight; + iRichBio.SetSizeWithoutNotification(thisSize); + iRichBio.iEditor->SetSize(aDesirableEdwinSize); + } + return ETrue; + + } + return EFalse; + } + +EXPORT_C CRichBio* CRichBio::NewL(const CCoeControl* aParent, + TRichBioMode aMode) + { + CRichBio* self = new (ELeave) CRichBio( aMode ); + CleanupStack::PushL( self ); + self->ConstructL( aParent ); + CleanupStack::Pop( self ); + return self; + } + +CRichBio::~CRichBio() + { + delete iEdwinSizeObserver; + delete iEditor; + delete iLabelCache; + delete iValueCache; + } + +EXPORT_C void CRichBio::AddItemL(const TDesC& aLabel, const TDesC& aValue) + { + if (iEditor) + { + AddItemToRichTextL(aLabel, aValue); + } + // Items needs to be always cached, because of possible layout changes. + CacheItemL(aLabel, aValue); + } + +EXPORT_C TRect CRichBio::CurrentLineRect() + { + TRect lineRect(0,0,0,0); + // if fails return 0x0 rect + // this method is part of public SDK and this cannot be changed to leaveable. + TRAP_IGNORE((lineRect = iEditor->CurrentLineRectL())); + return lineRect; + } + +EXPORT_C CEikRichTextEditor& CRichBio::Editor() + { + __ASSERT_ALWAYS(iEditor, Panic(EPanicEditorIsNull1)); + return *iEditor; + } + +EXPORT_C TBool CRichBio::IsEditorBaseMode() const + { + return (ERichBioModeEditorBase == iMode); + } + +EXPORT_C void CRichBio::Reset() + { + iEditor->Reset(); + iIsFirstItem = ETrue; + TRAP_IGNORE(LoadFromCacheL()); + } + +void CRichBio::AddItemToRichTextL(const TDesC& aLabel, const TDesC& aValue) + { + TCharFormat format; + TCharFormatMask mask; + + if ( aLabel.Length() > 0 ) + { + ApplyLabelFormat( format, mask ); + AppendTextL( aLabel , format, mask ); + } + + if ( aValue.Length() > 0 ) + { + ApplyValueFormat( format, mask ); + AppendTextL( aValue , format, mask ); + } + } + +void CRichBio::AppendTextL( const TDesC& aText, + const TCharFormat& aFormat, + const TCharFormatMask& aMask ) + { + // Create the modifiable descriptor + HBufC* buf = HBufC::NewLC( aText.Length() ); + TPtr modifiedText = buf->Des(); + modifiedText = aText; + + // This is for example for arabic numbers + AknTextUtils::DisplayTextLanguageSpecificNumberConversion( modifiedText ); + TInt pos(0); + + /* + We have four cases, where specific character(s) must be + converted to the CRichText understandable linebreak character + (CEditableText::ELineBreak). + 1) Text contains ascii code linefeed (LF) character 0x0A, which + is converted to linebreak. + 2) Text contains ascii code carrier return (CR) character 0x0D + 3) Text contains both LF and CR characters particularly in this order. + These two characters must be converted to one linebreak. + */ + + TUint LF(0x0A); // Linefeed character in ascii set + TUint CR(0x0D); // Carriage return character in ascii set + + + //this implementation won't handle mixed set of CRLF / LFCR / LF / CR + //linefeeds "quite" properly but that situation is not very likely + + // Case 1 and 3 + while ( ( pos = modifiedText.Locate( LF ) ) != KErrNotFound ) // Locate LF + { + if ( pos + 1 < modifiedText.Length() && CR == modifiedText[pos + 1] ) // Is the next CR? + { + modifiedText.Delete( pos + 1, 1 ); // Delete if CR + } + modifiedText[pos] = CEditableText::ELineBreak; // Replace LF with linebreak + + //as added measure we will check the previous character + if ( (pos-1) >= 0 && CR == modifiedText[pos - 1] ) // Is the previous CR? + { + modifiedText.Delete( pos -1 , 1 ); // Delete if CR + } + } + + // Case 2 + while ( ( pos = modifiedText.Locate( CR ) ) != KErrNotFound ) // Locate CR + { + if ( pos + 1 < modifiedText.Length() && LF == modifiedText[pos + 1] ) // Is the next LF? + { + modifiedText.Delete( pos + 1, 1 ); // Delete if LF + } + modifiedText[pos] = CEditableText::ELineBreak; // Replace CR with linebreak + + //as added measure we will check the previous character + if ( (pos-1) >= 0 && LF == modifiedText[pos - 1 ] ) // Is the previous LF? + { + modifiedText.Delete( pos -1, 1 ); // Delete if LF + } + } + + // Append the modified text to the richtext. + CRichText* rich = iEditor->RichText(); + User::LeaveIfNull( rich ); + + TInt documentPos = rich->DocumentLength(); + + // Every time when text is added, the cursor is left at the end of the line. + // When the new text is added we must first add linebreak and then the text + // linebreak is not added if the text is first item. + if ( !iIsFirstItem ) + { + // Append the linebreak to the end of the richtext. + rich->InsertL(documentPos, CEditableText::ELineBreak); + } + else + { + iIsFirstItem = EFalse; + } + + documentPos = rich->DocumentLength(); + rich->SetInsertCharFormatL(aFormat, aMask, documentPos); + rich->InsertL( documentPos, modifiedText ); + + rich->CancelInsertCharFormat(); + + CleanupStack::PopAndDestroy( buf ); + } + +TKeyResponse CRichBio::OfferKeyEventL( + const TKeyEvent& aKeyEvent,TEventCode aType) + { + return iEditor->OfferKeyEventL(aKeyEvent, aType); + } + +EXPORT_C void CRichBio::SetAndGetSizeL(TSize& aSize) + { + TInt maxHeight( MsgEditorCommons::MaxBodyHeight() ); + iEditor->SetMaximumHeight( maxHeight ); + + iEditor->SetAndGetSizeL( aSize ); + + + if ( aSize.iHeight > maxHeight ) + { + aSize.iHeight = maxHeight; + } + SetSizeWithoutNotification( aSize ); + } + +EXPORT_C TInt CRichBio::VirtualHeight() + { + return iEditor->VirtualHeight(); + } + +EXPORT_C TInt CRichBio::VirtualVisibleTop() + { + return iEditor->VirtualVisibleTop(); + } + +EXPORT_C TBool CRichBio::IsCursorLocation(TMsgCursorLocation aLocation) const + { + TBool location(EFalse); + switch (aLocation) + { + case EMsgTop: + location = iEditor->CursorInFirstLine(); + break; + case EMsgBottom: + location = iEditor->CursorInLastLine(); + break; + default: + break; + } + + return location; + } + +void CRichBio::ApplyLabelFormat(TCharFormat& aFormat, TCharFormatMask& aMask) + { + ApplyFormat(aFormat, aMask, ETrue); + } + +void CRichBio::ApplyValueFormat(TCharFormat& aFormat, TCharFormatMask& aMask) + { + ApplyFormat(aFormat, aMask, EFalse); + } + +void CRichBio::ApplyFormat(TCharFormat& aFormat, TCharFormatMask& aMask, TBool aIsLabel) + { + if ( aIsLabel ) + { + TAknTextComponentLayout msg_body_pane_t2 = + AknLayoutScalable_Apps::msg_body_pane_t2( 0 ); + TAknTextLineLayout textLayout = msg_body_pane_t2.LayoutLine();//AppLayout::Smart_Messages_Line_1( 0 ); + const CFont* labelFont = AknLayoutUtils::FontFromId( textLayout.FontId() ); + aFormat.iFontSpec = labelFont->FontSpecInTwips(); + } + else + { + TAknTextComponentLayout msg_body_pane_t3 = + AknLayoutScalable_Apps::msg_body_pane_t3( 0 ); + TAknTextLineLayout editorLayout = msg_body_pane_t3.LayoutLine(); //AppLayout::Smart_Messages_Line_2( 0 ); + const CFont* editorFont = AknLayoutUtils::FontFromId( editorLayout.FontId() ); + aFormat.iFontSpec = editorFont->FontSpecInTwips(); + } + + aMask.ClearAll(); + aMask.SetAttrib(EAttFontStrokeWeight); + aMask.SetAttrib(EAttFontHeight); + aMask.SetAttrib(EAttFontTypeface); + } + +EXPORT_C CRichBio::CRichBio(TRichBioMode aMode) : iMode(aMode) + { + } + +EXPORT_C void CRichBio::ConstructL(const CCoeControl* aParent) + { + __ASSERT_DEBUG(!iEditor, Panic( EPanicConstructedTwice )); + TUint32 flags = 0; + iEditor = new (ELeave) CMsgExpandableControlEditor( aParent, flags, NULL); + iEditor->ConstructL(); + + TAknLayoutText headerTextLayout; + TMargins8 edwinMargins; + if ( AknLayoutUtils::ScalableLayoutInterfaceAvailable() ) + { + TAknLayoutRect msgTextPane; + msgTextPane.LayoutRect( + MsgEditorCommons::MsgDataPane(), + AknLayoutScalable_Apps::msg_text_pane( 0 ).LayoutLine() ); + + TAknLayoutRect msgBodyPane; + msgBodyPane.LayoutRect( + msgTextPane.Rect(), + AknLayoutScalable_Apps::msg_body_pane().LayoutLine() ); + headerTextLayout.LayoutText( + msgBodyPane.Rect(), + AknLayoutScalable_Apps::msg_body_pane_t2( 0 ).LayoutLine() ); + + edwinMargins.iTop = KMarginTop; + edwinMargins.iBottom = KMarginBottom; + edwinMargins.iLeft = headerTextLayout.TextRect().iTl.iX; //KMarginLeft; + edwinMargins.iRight = + msgTextPane.Rect().iBr.iX - + headerTextLayout.TextRect().iBr.iX;// KMarginRight; + } + else + { + // use old margins + edwinMargins.iTop = KMarginTop; + edwinMargins.iBottom = KMarginBottom; + edwinMargins.iLeft = KMarginLeft; + edwinMargins.iRight = KMarginRight; + } + iEditor->SetBorderViewMargins(edwinMargins); + + // Create the observer. + iEdwinSizeObserver = new (ELeave) CEdwinSizeObserver( *this ); + iEditor->SetEdwinSizeObserver( iEdwinSizeObserver ); + iIsFirstItem = ETrue; + iEditor->SetReadOnly(ETrue); + if (CacheExists()) + { + LoadFromCacheL(); + } + SetContainerWindowL(*aParent); + } + +TInt CRichBio::CountComponentControls() const + { + return 1; // iEditor ( created in constructL, so always 1 ) + } + +CCoeControl* CRichBio::ComponentControl(TInt aIndex) const + { + if (aIndex == 0) + { + return iEditor; + } + return NULL; + } + +void CRichBio::SizeChanged() + { + iEditor->SetExtent(Position(), iEditor->Size()); + } + +void CRichBio::Panic( TInt aPanic ) + { + _LIT(KDll, "richbio.dll"); + User::Panic(KDll, aPanic); + } + +TBool CRichBio::CacheExists() + { + if (iLabelCache) + { + __ASSERT_DEBUG(iValueCache, Panic(EPanicCacheProblem1)); + return ETrue; + } + __ASSERT_DEBUG(!iValueCache, Panic(EPanicCacheProblem2)); + return EFalse; + } + +void CRichBio::LoadFromCacheL() + { + __ASSERT_DEBUG(iLabelCache && iValueCache, Panic(EPanicNoCache)); + + TInt count(iLabelCache->Count()); + __ASSERT_DEBUG(iValueCache->Count() == count, + Panic(EPanicCacheMismatch1)); + for (TInt n(0); n < count; n++) + { + AddItemToRichTextL((*iLabelCache)[n], (*iValueCache)[n]); + } + } + +void CRichBio::CacheItemL(const TDesC& aLabel, const TDesC& aValue) + { + if (!iLabelCache || !iValueCache) + { + delete iLabelCache; + delete iValueCache; + iLabelCache = NULL; + iValueCache = NULL; + iLabelCache = new (ELeave) CDesC16ArrayFlat(10); //granularity of 10 + iValueCache = new (ELeave) CDesC16ArrayFlat(10); //granularity of 10 + } + iLabelCache->AppendL(aLabel); + iValueCache->AppendL(aValue); + + __ASSERT_DEBUG(iLabelCache->Count() == iValueCache->Count(), + Panic(EPanicCacheMismatch2)); + __ASSERT_DEBUG(iLabelCache->Count() > 0, Panic(EPanicCacheMismatch3)); + } + +void CRichBio::Draw(const TRect& /*aRect*/) const + { + //removed the clearing of window background,causes flicker + } + +#ifdef RD_SCALABLE_UI_V2 +EXPORT_C TInt CRichBio::ScrollL( TInt aPixelsToScroll, TMsgScrollDirection aDirection ) + { + TInt pixelsToScroll( 0 ); + TInt marginPixels( 0 ); + + CTextLayout* textLayout = iEditor->TextLayout(); + + if ( aDirection == EMsgScrollDown ) + { + TInt pixelsAboveBand( textLayout->PixelsAboveBand() ); + TInt bandHeight( textLayout->BandHeight() ); + TInt virtualHeight( iEditor->VirtualHeight() ); + + if ( pixelsAboveBand + bandHeight + aPixelsToScroll >= virtualHeight ) + { + pixelsToScroll = -( virtualHeight - ( pixelsAboveBand + bandHeight ) ); + + if ( pixelsToScroll != 0 ) + { + // Control has reaches end. Mark margin pixels scrolled. + marginPixels = iEditor->Size().iHeight - Size().iHeight; + } + } + else + { + // Negative pixels means scrolling down. + pixelsToScroll = -aPixelsToScroll; + } + } + else + { + pixelsToScroll = aPixelsToScroll; + } + + if ( pixelsToScroll != 0 ) + { + //scrolling to be restricted to full lines + //so there will not be any clipping of text on view + textLayout->RestrictScrollToTopsOfLines( ETrue ); + iEditor->TextView()->ScrollDisplayPixelsL( pixelsToScroll ); + + } + + return Abs( pixelsToScroll ) + marginPixels; + } + +EXPORT_C void CRichBio::NotifyViewEvent( TMsgViewEvent aEvent, TInt /*aParam*/ ) + { + switch ( aEvent ) + { + case EMsgViewEventPrepareFocusTransitionUp: + { + if ( IsFocused() ) + { + //possible leaves are discarded + TRAP_IGNORE( iEditor->ClearSelectionL() ); + } + break; + } + case EMsgViewEventPrepareFocusTransitionDown: + { + if ( IsFocused() ) + { + //possible leaves are discarded + TRAP_IGNORE( iEditor->ClearSelectionL() ); + } + break; + } + case EMsgViewEventSetCursorFirstPos: + { + if ( iEditor->TextView() ) + { + if ( iEditor->IsFirstLineVisible() ) + { + //possible leaves are discarded + TRAP_IGNORE( iEditor->SetCursorPosL( 0, EFalse ) ); + } + } + break; + } + case EMsgViewEventSetCursorLastPos: + { + if ( iEditor->TextView() ) + { + TInt len = iEditor->TextLength(); + + if ( iEditor->IsLastLineVisible() ) + { + //possible leaves are discarded + TRAP_IGNORE( iEditor->SetCursorPosL( len, EFalse ) ); + } + } + break; + } + default: + { + break; + } + } + } + +#else +EXPORT_C TInt CRichBio::ScrollL( TInt /*aPixelsToScroll*/, + TMsgScrollDirection /*aDirection*/ ) + { + return 0; + } + +EXPORT_C void CRichBio::NotifyViewEvent( TMsgViewEvent /*aEvent*/, + TInt /*aParam*/ ) + { + //no op + } + +#endif //RD_SCALABLE_UI_V2 + + + +//end of file +