uifw/EikStd/coctlsrc/aknedwincustomdrawbase.cpp
changeset 0 2f259fa3e83a
child 10 9f56a4e1b8ab
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uifw/EikStd/coctlsrc/aknedwincustomdrawbase.cpp	Tue Feb 02 01:00:49 2010 +0200
@@ -0,0 +1,767 @@
+/*
+* 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:
+*
+*/
+
+
+// INCLUDE FILES
+#include "aknedwincustomdrawbase.h"
+#include <lafmain.h>
+#include <AknsDrawUtils.h>
+#include <frmtview.h>
+#include <coecntrl.h>
+#include <eikedwin.h>
+#include <eikenv.h> // for AKN_LAF_COLOR macro
+#include <AknUtils.h>
+#include <gdi.h>
+#include <AknPictographInterface.h>
+#include <AknPictographDrawerInterface.h>
+#include <aknenv.h>
+#include <AknFontProvider.h>
+#include <AknTextDecorationMetrics.h>
+#include <AknsUtils.h>
+
+
+// CONSTANTS
+
+// This is the last character that will be treated as requiring higher underline
+// const TText KMaxSpecialUnderliningChar = 0x0E5B;
+const TInt KWsBufferSize = 16000;
+
+// MEMBER FUNCTIONS
+
+CAknEdwinCustomDrawBase::CAknEdwinCustomDrawBase(
+    const MLafEnv& aEnv, const CEikEdwin& aEdwin ) :
+        CLafEdwinCustomDrawBase( aEnv, aEdwin ), iEdwin( aEdwin )
+    {
+    // This value has to be stored as the meaning of the flag governs behaviour of controls
+    // at the point of construction
+    iAppSkinEnabled = AknsUtils::AvkonSkinEnabled();
+    }
+
+CAknEdwinCustomDrawBase::CAknEdwinCustomDrawBase(
+    const MLafEnv& aEnv,
+    const CEikEdwin& aEdwin,
+    CTextView* aTextView,
+    CWindowGc* aSystemGc ) :
+        CLafEdwinCustomDrawBase( aEnv,aEdwin ),
+        iEdwin( aEdwin ),
+        iTextView( aTextView ),
+        iSysGc( aSystemGc )
+    {
+    // This value has to be stored as the meaning of the flag governs behaviour of controls
+    // at the point of construction
+    iAppSkinEnabled = AknsUtils::AvkonSkinEnabled();
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDrawBase::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CAknEdwinCustomDrawBase::ConstructL()
+    {
+    iWsBufferRequestId = CAknEnv::Static()->RequestWsBuffer( KWsBufferSize );
+    // Returns NULL if feature not supported.
+    iPictographDrawer = CAknPictographInterface::NewL(
+        static_cast<CCoeControl&>( const_cast<CEikEdwin&>( iEdwin ) ),
+        *static_cast<MAknPictographAnimatorCallBack*>( this ) );
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDraw::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CAknEdwinCustomDrawBase* CAknEdwinCustomDrawBase::NewL(
+    const MLafEnv& aEnv, const CEikEdwin& aControl )
+    { // static
+    CAknEdwinCustomDrawBase* self =
+        new( ELeave ) CAknEdwinCustomDrawBase( aEnv, aControl );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop();
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDraw::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CAknEdwinCustomDrawBase* CAknEdwinCustomDrawBase::NewL(
+    const MLafEnv& aEnv,
+    const CEikEdwin& aControl,
+    CTextView* aTextView,
+    CWindowGc* aSystemGc )
+    { // static
+    CAknEdwinCustomDrawBase* self = new( ELeave ) CAknEdwinCustomDrawBase(
+        aEnv, aControl, aTextView, aSystemGc );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop();
+    return self;
+    }
+
+// Destructor
+CAknEdwinCustomDrawBase::~CAknEdwinCustomDrawBase()
+    {
+    delete iPictographDrawer;
+    CAknEnv::Static()->CancelWsBufferRequest( iWsBufferRequestId );
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDrawBase::DrawPictographArea
+// -----------------------------------------------------------------------------
+//
+void CAknEdwinCustomDrawBase::DrawPictographArea()
+    {
+    iEdwin.DrawTextView();
+
+    // Mainly used for drawing form highlight frame
+    const TCallBack& callback = iEdwin.PictographAnimationCallBack();
+    if ( callback.iFunction )
+        {
+        callback.CallBack();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDrawBase::DrawText
+// -----------------------------------------------------------------------------
+//
+void CAknEdwinCustomDrawBase::DrawText( const TParam& aParam, 
+    const TLineInfo& aLineInfo, const TCharFormat& aFormat, const TDesC& aText,
+    const TPoint& aTextOrigin, TInt aExtraPixels ) const
+    {
+    TRect textLinesRect = iEdwin.GetTextLinesRect();
+
+    if ( !textLinesRect.IsEmpty() )
+        {
+        if ( aLineInfo.iInnerRect.iTl.iY < textLinesRect.iTl.iY ||
+             aLineInfo.iInnerRect.iBr.iY > textLinesRect.iBr.iY )
+            {
+            return;
+            }
+        }
+
+    TCharFormat alteredFormat(aFormat);
+
+    if( aFormat.iFontPresentation.iUnderline == EUnderlineOn )
+        {
+        // Inhibit all excess pixels
+        aExtraPixels = 0;
+
+        TInt underlinePos(0);
+        (void)TextNeedsCustomUnderline( aText, aParam, aFormat, underlinePos ) ;
+        // always perform custom underlining
+
+        TRect underlineRect(aParam.iDrawRect);
+        TAknTextDecorationMetrics decoration( 0 ); // Give dummy font id for default decoration
+        // Note that underlinePos in scalable is a delta relative to normal baseline
+        underlineRect.iBr.iY = aLineInfo.iBaseline - underlinePos + decoration.BaselineToUnderlineOffset();
+        underlineRect.iTl.iY = underlineRect.iBr.iY - decoration.UnderlineHeight();
+
+        TRAPD
+            (
+            err,
+            DrawUnderlineL( underlineRect, aParam,aLineInfo,alteredFormat,aText,aTextOrigin,aExtraPixels)
+            );
+        if ( err != KErrNone )
+            {
+            // Just draw the underline solid
+            aParam.iGc.DrawRect( underlineRect );
+            }
+        // we've handled the underline, so switch it off now
+        alteredFormat.iFontPresentation.iUnderline = EUnderlineOff;
+        aParam.iGc.SetUnderlineStyle( EUnderlineOff);  // and force it off in the GC
+        }
+    CFont* font = NULL;
+    aParam.iMap.GetNearestFontInTwips( font, aFormat.iFontSpec );
+    RRegion rgn;
+    if ( iEdwin.IsSmileyEnabled() && font )
+        {
+        rgn.AddRect( iEdwin.AdjustDrawRectForSmiley( aParam.iDrawRect ) );
+        iEdwin.GetClipRegionForSmiley( rgn, *font, aText, aTextOrigin, 
+            aParam.iDrawRect );
+        aParam.iGc.SetClippingRegion( rgn );
+        }
+    CLafEdwinCustomDrawBase::DrawText( aParam, aLineInfo, alteredFormat, 
+        aText, aTextOrigin, aExtraPixels );
+    if ( iEdwin.IsSmileyEnabled() )
+        {
+        aParam.iGc.CancelClippingRegion();
+        }
+    rgn.Close();
+    
+    if ( iEdwin.IsSmileyEnabled() && font && ( &aParam.iGc ==
+        static_cast<CGraphicsContext*>( iTextView->BitmapContext() ) ) )
+        {
+        CBitmapContext* bitmapGc( iTextView->BitmapContext() );
+        CEikEdwin& edwin = const_cast<CEikEdwin&>( iEdwin );
+        TPoint startPt( aParam.iDrawRect.iTl );
+        startPt.iY = aLineInfo.iBaseline;
+        TRAP_IGNORE( {
+            edwin.HandleScrollForSmileyL();
+            edwin.DrawSmileyInTextL( *bitmapGc, *font, aText, startPt );
+            } );
+        }
+
+    // Draw pictographs if the feature is supported.
+    // Character justification is not supported.
+    if ( ( iPictographDrawer && !aExtraPixels ) )
+        {
+        CBitmapContext* bitmapGc = NULL;
+
+        // This is how we check the type of the graphics context
+        // The gc should be one of these:
+        if ( &aParam.iGc == static_cast<CGraphicsContext*>( iSysGc ) )
+            {
+            bitmapGc = iSysGc;
+            }
+        else if ( &aParam.iGc ==
+            static_cast<CGraphicsContext*>( iTextView->BitmapContext() ) )
+            {
+            bitmapGc = iTextView->BitmapContext();
+            }
+
+        if ( bitmapGc )
+            {
+            const TText* text = aText.Ptr();
+            TInt length( aText.Length() ); 
+                        
+            TPoint point = aParam.iDrawRect.iTl;
+            point.iY = aLineInfo.iBaseline;
+            if ( iPictographDrawer && !aExtraPixels )
+                {
+                MAknPictographDrawer* drawer = 
+                    iPictographDrawer->Interface();                
+                if ( font )
+                    {
+                    drawer->DrawPictographsInText(
+                        *bitmapGc,
+                        *font,
+                        aText,
+                        point);                    
+                    }
+                }            
+            }
+        }
+    aParam.iMap.ReleaseFont( font );
+    // set brush back to original
+    aParam.iGc.SetBrushColor(CEikonEnv::Static()->ControlColor(EColorControlBackground,iEdwin));
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDrawBase::DrawText
+// -----------------------------------------------------------------------------
+//
+void CAknEdwinCustomDrawBase::DrawText( const TParam& aParam, 
+    const TLineInfo& aLineInfo, const TCharFormat& aFormat, const TDesC& aText, 
+    const TInt aStart, const TInt aEnd, const TPoint& aTextOrigin, 
+    TInt aExtraPixels ) const
+    {
+    DrawText( aParam, aLineInfo, aFormat, aText.Mid( aStart, aEnd - aStart ), 
+        aTextOrigin, aExtraPixels );
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDrawBase::DrawBackground
+// -----------------------------------------------------------------------------
+//
+void CAknEdwinCustomDrawBase::DrawBackground(
+    const TParam& aParam, const TRgb& aBackground, TRect& aDrawnRect ) const
+    {
+    if ( !iEdwin.IsBackgroundDrawingSuppressed() )
+        {
+        const MCoeControlBackground* drawer = iEdwin.FindBackground();
+        
+        if ( drawer )
+            {
+            if ( !iEdwin.SkipBackgroundDrawer() )
+                {
+                drawer->Draw( static_cast<CWindowGc&>( aParam.iGc ),
+                              iEdwin,
+                              aParam.iDrawRect );
+                }
+            }
+        else
+            {
+            if ( !DrawRectWithSkin( aParam.iGc, aParam.iDrawRect, aDrawnRect ) )
+                {
+                aParam.iGc.SetBrushStyle( CGraphicsContext::ESolidBrush );
+                aParam.iGc.SetPenStyle( CGraphicsContext::ESolidPen );
+                aParam.iGc.SetBrushColor( aBackground );
+                aParam.iGc.SetPenColor( aBackground );
+                aParam.iGc.DrawRect( aParam.iDrawRect );
+                }
+            }
+        }
+    
+    aDrawnRect = aParam.iDrawRect;
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDrawBase::DrawLineGraphics
+// -----------------------------------------------------------------------------
+//
+void CAknEdwinCustomDrawBase::DrawLineGraphics(const TParam& aParam,const TLineInfo& aLineInfo ) const
+    {
+    CLafEdwinCustomDrawBase::DrawLineGraphics( aParam, aLineInfo ); // Base Call
+    }
+
+
+TRgb CAknEdwinCustomDrawBase::SystemColor(TUint aColorIndex,TRgb aDefaultColor) const
+    {
+    TRgb ret = aDefaultColor;
+    if (aColorIndex==TLogicalRgb::ESystemForegroundIndex)
+        {
+        MAknsSkinInstance* skin = AknsUtils::SkinInstance();
+        if (skin && iEdwin.SkinColorId() != KErrNotFound)
+            {
+            AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnTextColors, iEdwin.SkinColorId());
+            }
+        }
+    else if (aColorIndex==TLogicalRgb::ESystemBackgroundIndex)
+        {
+        // Only override aDefaultColor if SetBackgroundColorL was called:
+        (void)iEdwin.EditorBackgroundColor(ret);
+        // Insist on opaque background
+        ret.SetAlpha(0xFF);
+        }
+
+    else if (aColorIndex==TLogicalRgb::ESystemSelectionForegroundIndex)
+        {
+        ret = KRgbWhite;
+        MAknsSkinInstance* skin = AknsUtils::SkinInstance();
+        if (skin)
+            {
+            if ( iEdwin.HighlightStyle() == EEikEdwinHighlightLink )
+                {
+                AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnHighlightColors, EAknsCIQsnTextColorsCG3);
+                }
+            else // default
+                {
+                AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG24);
+                }
+            }
+        }
+    else if (aColorIndex==TLogicalRgb::ESystemSelectionBackgroundIndex)
+        {
+        ret = KRgbBlue;
+        MAknsSkinInstance* skin = AknsUtils::SkinInstance();
+        if (skin)
+            {
+            if ( iEdwin.HighlightStyle() == EEikEdwinHighlightLink )
+                {
+                AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnHighlightColors, EAknsCIQsnTextColorsCG1);
+                }
+            else // default
+                {
+                AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnHighlightColors, EAknsCIQsnHighlightColorsCG2);
+                }
+            }
+        }
+
+    return ret;
+    }
+
+void CAknEdwinCustomDrawBase::CAknEdwinCustomDrawBase_Reserved_1()
+    {
+    }
+
+/**
+* Routine to group the common skin custom drawing
+*
+* @return TBool EFalse iff the drawing was not performed
+*/
+TBool CAknEdwinCustomDrawBase::DrawRectWithSkin( const CGraphicsContext& aGc, const TRect& aRect, TRect& aDrawnRect ) const
+    {
+    TBool drawn(EFalse);
+
+    if ( iAppSkinEnabled && iTextView)
+        {
+        CBitmapContext* bitmapGc = NULL;
+
+        // This is how we check the type of the graphics context
+        // The gc should be one of these:
+        if ( &aGc == static_cast<CGraphicsContext*>( iSysGc ) )
+            bitmapGc = iSysGc;
+        else if ( &aGc == static_cast<CGraphicsContext*>( iTextView->BitmapContext() ) )
+            bitmapGc = iTextView->BitmapContext();
+#ifdef RD_UI_TRANSITION_EFFECTS_POPUPS
+        else if( &aGc == static_cast<CGraphicsContext*>( &(iControl.SystemGc()) ) )
+            bitmapGc = &(iControl.SystemGc());
+#endif //RD_UI_TRANSITION_EFFECTS_POPUPS
+        if ( bitmapGc && iEdwin.SkinEnabled() )
+            {
+            MAknsSkinInstance* skin = AknsUtils::SkinInstance();
+            // Edwin manages the skin background
+            MAknsControlContext* edCc = iEdwin.SkinBackgroundControlContext();
+
+            TInt drawFlags = KAknsDrawParamDefault;
+            if( CAknEnv::Static()->TransparencyEnabled() )
+            {
+                drawFlags |= KAknsDrawParamNoClearUnderImage;
+            }
+
+            if( AknsDrawUtils::DrawBackground( skin, edCc,
+                    &iControl, *bitmapGc, aRect.iTl, aRect,
+                    drawFlags ))
+                {
+                aDrawnRect = aRect;
+                drawn = ETrue;
+                }
+            }
+        else
+            {
+            // perform unoptimized draw of skin using bitmap brushstyle?
+            }
+
+        }
+
+    return drawn;
+    }
+
+TBool CAknEdwinCustomDrawBase::TextNeedsCustomUnderline( const TDesC& aText, const TParam& aParam, const TCharFormat& aFormat, TInt& underlinePos ) const
+    {
+    TBool needsCustomUnderline(EFalse);
+
+    TInt fontProviderIndex = AknFontProvider::FontProviderIndexFromFontSpec( aFormat.iFontSpec );
+    if ( fontProviderIndex > KErrNotFound )
+        {
+        needsCustomUnderline = AknFontProvider::HasBaselineCorrection( fontProviderIndex );
+        if ( needsCustomUnderline )
+            {
+            TInt denominatorIfFractional(KAknFontProviderBaselineCorrectionIsAbsolute);
+            TInt numInitialCharsTheSame(0); // not used
+            TInt minimumBaselineLift(0);
+            AknFontProvider::MinimumBaselineDeltaForDescriptor(
+                fontProviderIndex,
+                aText,
+                minimumBaselineLift,
+                denominatorIfFractional,
+                EFalse, // Look at whole descriptor and take minimum baseline lift
+                numInitialCharsTheSame);
+
+            // Check for proportional baseline deltas:
+            if ( denominatorIfFractional != KAknFontProviderBaselineCorrectionIsAbsolute )
+                {
+                underlinePos = minimumBaselineLift * aParam.iDrawRect.Height() / denominatorIfFractional;
+                }
+            else
+                {
+                underlinePos = minimumBaselineLift;
+                }
+            }
+        }
+
+    return needsCustomUnderline;
+    }
+
+// -----------------------------------------------------------------------------
+// CAknEdwinCustomDrawBase::DrawUnderlineL
+// -----------------------------------------------------------------------------
+/*
+This method has the responsibility for drawing a custom underline.
+
+An offscreen bitmap with 8 bit greyscale is made the size of the underline. Using the same font as the editor, the text
+is drawn onto this bitmap, with variable positioning, so that the text is smeared.
+
+The "descenderBitmap" is passed to a routine that analyzes this bitmap and draws the underlines only in
+between the descenders, leaving gaps.
+
+*/
+void CAknEdwinCustomDrawBase::DrawUnderlineL(
+        const TRect& aUnderlineRect,
+        const TParam& aParam,
+        const TLineInfo& aLineInfo,
+        const TCharFormat& aFormat,
+        const TDesC& aText,
+        const TPoint& /*aTextOrigin*/, // Not currently used
+        TInt aExtraPixels) const
+    {
+
+    if (aUnderlineRect.Width() <= 0 || aUnderlineRect.Height() <= 0 )
+        return;
+
+    TRect localUnderlineRect(aUnderlineRect);
+
+    // Create bitmap to draw the descenders to
+    CFbsBitmap* descendersBitmap = new (ELeave) CFbsBitmap();
+    CleanupStack::PushL( descendersBitmap);
+    User::LeaveIfError( descendersBitmap->Create(TSize(localUnderlineRect.Width(),localUnderlineRect.Height()) , EGray256));
+
+    CFbsBitmapDevice* device = CFbsBitmapDevice::NewL(descendersBitmap);
+    CleanupStack::PushL(device);
+    CGraphicsContext* context=NULL;
+    User::LeaveIfError(device->CreateContext(context));
+    CleanupStack::PushL(context);
+
+    CFont* font;
+    // Fetch the font.  This font is already in use by the editor.  Since it is a request in twips, it will be in the
+    // twips cache and the request should not go to the Fbserv server.  It should therefore be fast.
+    User::LeaveIfError(aParam.iMap.GetNearestFontInTwips( font, aFormat.iFontSpec) );
+
+    context->UseFont( font );
+    DrawDescendersOntoBitmap( *context, aText, aParam, aLineInfo, localUnderlineRect, aExtraPixels );
+    context->DiscardFont ();
+
+    aParam.iMap.ReleaseFont( font );
+
+    // Draw the line using aParam.iGc so as to pick up correct foreground color. Algo inside this method is used to avoid the descenders that are
+    // already drawn onto descendersBitmap
+    DrawLineWithGaps( aParam, *descendersBitmap, localUnderlineRect ) ;
+
+    // Delete all the local objects
+    CleanupStack::PopAndDestroy( context );
+    CleanupStack::PopAndDestroy( device );
+    CleanupStack::PopAndDestroy( descendersBitmap );
+    }
+
+/**
+*  The descender parts of the text are drawn onto the bitmap context.
+*  The text is drawn several times with horizontal and vertical offsets. The resulting bitmap will be used to determine
+*  where the underline should be drawn to avoid touching the descenders
+*/
+void CAknEdwinCustomDrawBase::DrawDescendersOntoBitmap(
+        CGraphicsContext& aContext,
+        const TDesC& aText,
+        const TParam& /*aParam*/,
+        const TLineInfo& aLineInfo,
+        const TRect& aUnderlineRect,
+        TInt aExtraPixels ) const
+    {
+    // Have to arrange to draw text onto the underline rectangle bitmap context.
+    // The text needs to be positioned so that the correct parts of the text (ie.part of the descenders)
+    // are within this underline rectangle. We need to calculate the distance from the top of the drawing rectangle
+    // (which will perform the clipping) to the baseline of the text.
+    // This will be used as the  "aBaselineOffset" parameter to the CGraphicsContext::DrawText method.
+    // Expect this value to be small and negative, since the baseline is usually above the underline.
+    TInt posOfBaselineFromUnderline =  posOfBaselineFromUnderline = aLineInfo.iBaseline - aUnderlineRect.iTl.iY;
+
+    // Here, draw the text with letter spacing. This is what Tagma will do when it really draws the text.
+    if (aExtraPixels)
+        {
+        aContext.SetCharJustification(aExtraPixels,aText.Length());
+        }
+
+    // We are going to draw the text moved around both horizontally and vertically.
+    // Use this rectangle with 0,0 at top left to match the bitmap's own 0,0-based coord system
+    TRect movingRect( TPoint(0,0), TPoint( aUnderlineRect.Width(), aUnderlineRect.Height()));
+
+    // Double loop to perform text smearing horizontally and vertically
+
+    // These constants reflect the desired gaps around the descenders
+    // This calculation allows the descender-avoiding gaps to scale:
+    const TInt KHorizontalSmear = Max( aUnderlineRect.Height()/2, 1);
+    const TInt KVerticalSmear = KHorizontalSmear;
+
+    for ( TInt xOffset = -KHorizontalSmear; xOffset <= KHorizontalSmear; xOffset++ )
+        {
+        for ( TInt yOffset = -KVerticalSmear; yOffset <= KVerticalSmear; yOffset++ )
+            {
+            // Drawing is relative to the left, and top (assuming posOfBaselineFromUnderline is constant), so fiddle
+            // top left point.
+            movingRect.iTl.iX = xOffset;
+            movingRect.iTl.iY = 0;
+            aContext.DrawText( aText, movingRect, posOfBaselineFromUnderline + yOffset, CGraphicsContext::ELeft, 0 );
+            }
+        }
+    }
+
+/**
+  Draws the underline in the aParam.iGc pen color onto the screen, but uses the smeared descenders bitmap to
+  avoid drawing underline through descenders.
+
+  The offscreen bitmap is walked, watching for light/dark and dark./light transitions which drive the drawing of line segments
+  on the screen device.
+
+  Various features are built in the bitmap walk.
+  - use special "off" threshold near the beginning of a text to encourage leading bits of underline
+  - special "on" threshold to encourage small trailing pieces of underlines
+
+  There are special conditions that must be met for success to be claimed, and a value of ETrue to be returned.
+  These conditions insist on at least one segment of line being drawn, and that lines occupy a big enough percentage of the
+  total underline length.
+ */
+void CAknEdwinCustomDrawBase::DrawLineWithGaps(
+        const TParam& aParam,
+        CFbsBitmap& aSmearedDescendersBitmap,
+        const TRect& aUnderlineRect ) const
+    {
+    // Use the Symbian utility to provide access to bitmaps:
+    TBitmapUtil util(&aSmearedDescendersBitmap);
+    util.Begin( TPoint(0,0) );
+
+    const TInt lastRow = aUnderlineRect.Height() - 1; // zero-based
+    const TInt lastPos = aUnderlineRect.Width() - 1; // zero-based
+
+    TUint32 thisPixel;
+    const TUint32 thresholdPixel(254); // Must suit the TDrawMode. This value for Gray255
+    const TUint32 darkerThreshold(32); // Must suit the TDrawMode. this low (near-black) value encourages underlines to start at the far left.
+    const TUint32 endThreshold(128); // Must suit the TDrawMode. Lower threshold to encourage a line segment at the far right.
+    const TInt shortRunLimit(1);
+
+    // Anchor and end point are used to draw the lines in the most common cases
+    TPoint anchor;
+    TPoint earlyAnchor;
+
+    // State flags
+    TBool currentlyInALine;  // An anchor point of some sort has been recorded. Committed to drawing at least something for the segment.
+    TBool earlyLineActive; // Special state where the earlyAnchor is used rather than the normally recorded one.
+
+    TInt numberSegmentsDrawn(0); // records how many segments of underline are drawn.  Used to set a return value
+    TInt lengthDrawnOnThisLine(0);
+    TInt maxPercentOfALineDrawn(0);
+
+    // Analyse the smeared character bitmap, and draw line segments that avoid the descenders
+    // The smeared descenders appear as black (or grey) pixels in the offscreen bitmap
+    // Using gray256, white is 255, black is 0.
+    // Do not get confused by the fact we tend to draw where there is white, and not draw then there is black!
+
+    for ( TInt row = 0; row <= lastRow; row++, util.IncYPos() )
+        {
+        // Set at beginning of the row at column 0
+        util.SetPos( TPoint(0,row) );
+
+        // row initialization.
+        TInt pos = 0;  // Position in the row.
+        currentlyInALine = EFalse;
+        earlyLineActive = EFalse;
+        lengthDrawnOnThisLine = 0; // zero the length recorded to be drawn in each row.
+
+        thisPixel = util.GetPixel();
+        if ( thisPixel >= darkerThreshold ) // use low (near-black) value to encourage leading lines
+            {
+            anchor = TPoint(aUnderlineRect.iTl.iX + pos, aUnderlineRect.iTl.iY + row); // beginning of lines
+            currentlyInALine = ETrue;
+            }
+
+        for (util.IncXPos(), pos = 1; pos < lastPos; pos++, util.IncXPos())
+            {
+            thisPixel = util.GetPixel();
+
+            if ( currentlyInALine )
+                {
+                // If we are not near the beginning, but have gone below normal threshold, end the line segment
+                TBool endTheLineSegment(EFalse);
+                if ( thisPixel < thresholdPixel && pos > shortRunLimit ) //then end it
+                    {
+                    endTheLineSegment = ETrue;
+                    }
+                // If we ARE near the beginning and below the special dark threshold
+                else if ( thisPixel < darkerThreshold && pos <= shortRunLimit )
+                    {
+                    endTheLineSegment = ETrue;
+                    }
+
+                if ( endTheLineSegment )
+                    { // The last TPoint is not drawn, so use pos and not pos - 1
+                    DrawLineAndRecord( aParam.iGc, aUnderlineRect, anchor, pos, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
+                    currentlyInALine = EFalse;
+                    earlyLineActive = EFalse; // cancel any early starts
+                    }
+
+                }
+            else // not currentlyInALine
+                {
+                //
+                if (thisPixel >= thresholdPixel ) // not in a line, but now above threshold
+                    {
+                    anchor = TPoint(aUnderlineRect.iTl.iX+pos, aUnderlineRect.iTl.iY + row);
+                    currentlyInALine = ETrue;
+                    }
+                else if ( thisPixel >= endThreshold ) // mark a potential "early start" for a line if even a little off black
+                    {
+                    // There is no test on whether we are already in earlyLineActive, so this updates, essentially recording a last
+                    // point above this lower threshold
+                    earlyAnchor = TPoint( aUnderlineRect.iTl.iX+pos, aUnderlineRect.iTl.iY + row );
+                    earlyLineActive = ETrue;
+                    // But not currently in a line
+                    }
+                else // not in a line and going below (darker than) endThreshold cancels the earlyLine stuff
+                    {
+                    earlyLineActive = EFalse;
+                    }
+                }
+
+            }  // end of for loop over columns
+
+        // Finish off the line. Pixel index already incremented by the for loop, but the pixel has not been fetched
+        thisPixel = util.GetPixel();
+        if ( currentlyInALine)
+            {
+            // 2nd TPoint in the call is not plotted, so go further, to lastPos+1
+            DrawLineAndRecord( aParam.iGc, aUnderlineRect, anchor, lastPos+1, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
+            }
+        else
+            {
+            // Special rule, with special low threshold, to get end runs even if somewhat suppressed
+            if ( thisPixel >= endThreshold )
+                {
+                if ( earlyLineActive ) // Use the recorded early anchor
+                    {
+                    // 2nd TPoint in the call is not plotted, so go further by 1
+                    DrawLineAndRecord( aParam.iGc, aUnderlineRect, earlyAnchor, lastPos+1, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
+                    }
+                else // just draw the one point
+                    {
+                    TPoint startPoint(aUnderlineRect.iTl.iX+lastPos,aUnderlineRect.iTl.iY + row);
+                    // 2nd TPoint in the call is not plotted, so go further by 1
+                    DrawLineAndRecord( aParam.iGc, aUnderlineRect, startPoint, lastPos+1, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
+                    }
+                }
+            }
+
+        maxPercentOfALineDrawn = Max( maxPercentOfALineDrawn, (lengthDrawnOnThisLine * 100 )/aUnderlineRect.Width()  );
+        } // end of for loop over rows
+
+    util.End();
+
+    // Check criteria for whether to say this was successful or not.
+    const TInt KMinPercentOfLineDrawn(33); // need a third drawn
+    if ( numberSegmentsDrawn <  (aUnderlineRect.Height()*2) && ( maxPercentOfALineDrawn < KMinPercentOfLineDrawn ) )
+        {
+        // Not enough line drawn.
+        // Extend the underlines on both sides.  Use some scaling for how much to extend
+        const TInt extraLineLength = Max( 1, aUnderlineRect.Height() );
+        for ( TInt row = 0; row <= lastRow; row++ ) // lastRow is zero-based
+            {
+            aParam.iGc.DrawLine(
+                TPoint(aUnderlineRect.iTl.iX-extraLineLength, aUnderlineRect.iTl.iY+ row ),
+                TPoint(aUnderlineRect.iTl.iX, aUnderlineRect.iTl.iY+ row)  );
+            aParam.iGc.DrawLine(
+                TPoint(aUnderlineRect.iBr.iX,  aUnderlineRect.iTl.iY+ row ),
+                TPoint(aUnderlineRect.iBr.iX+extraLineLength, aUnderlineRect.iTl.iY+ row)  );
+            }
+
+        }
+    }
+
+void CAknEdwinCustomDrawBase::DrawLineAndRecord(
+    CGraphicsContext& aGc,
+    const TRect& aRect,
+    const TPoint& aAnchor,
+    TInt aColumn,
+    TInt aRow,
+    TInt& aAccumulatedLength,
+    TInt& aSegmentsDrawn) const
+    {
+    TPoint endPoint = TPoint(aRect.iTl.iX + aColumn, aRect.iTl.iY + aRow);
+    aGc.DrawLine( aAnchor, endPoint );
+    aAccumulatedLength += (endPoint.iX - aAnchor.iX);
+    aSegmentsDrawn++;
+    }