uifw/EikStd/coctlsrc/aknedwincustomdrawbase.cpp
changeset 0 2f259fa3e83a
child 6 9f56a4e1b8ab
equal deleted inserted replaced
-1:000000000000 0:2f259fa3e83a
       
     1 /*
       
     2 * Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 // INCLUDE FILES
       
    20 #include "aknedwincustomdrawbase.h"
       
    21 #include <lafmain.h>
       
    22 #include <AknsDrawUtils.h>
       
    23 #include <frmtview.h>
       
    24 #include <coecntrl.h>
       
    25 #include <eikedwin.h>
       
    26 #include <eikenv.h> // for AKN_LAF_COLOR macro
       
    27 #include <AknUtils.h>
       
    28 #include <gdi.h>
       
    29 #include <AknPictographInterface.h>
       
    30 #include <AknPictographDrawerInterface.h>
       
    31 #include <aknenv.h>
       
    32 #include <AknFontProvider.h>
       
    33 #include <AknTextDecorationMetrics.h>
       
    34 #include <AknsUtils.h>
       
    35 
       
    36 
       
    37 // CONSTANTS
       
    38 
       
    39 // This is the last character that will be treated as requiring higher underline
       
    40 // const TText KMaxSpecialUnderliningChar = 0x0E5B;
       
    41 const TInt KWsBufferSize = 16000;
       
    42 
       
    43 // MEMBER FUNCTIONS
       
    44 
       
    45 CAknEdwinCustomDrawBase::CAknEdwinCustomDrawBase(
       
    46     const MLafEnv& aEnv, const CEikEdwin& aEdwin ) :
       
    47         CLafEdwinCustomDrawBase( aEnv, aEdwin ), iEdwin( aEdwin )
       
    48     {
       
    49     // This value has to be stored as the meaning of the flag governs behaviour of controls
       
    50     // at the point of construction
       
    51     iAppSkinEnabled = AknsUtils::AvkonSkinEnabled();
       
    52     }
       
    53 
       
    54 CAknEdwinCustomDrawBase::CAknEdwinCustomDrawBase(
       
    55     const MLafEnv& aEnv,
       
    56     const CEikEdwin& aEdwin,
       
    57     CTextView* aTextView,
       
    58     CWindowGc* aSystemGc ) :
       
    59         CLafEdwinCustomDrawBase( aEnv,aEdwin ),
       
    60         iEdwin( aEdwin ),
       
    61         iTextView( aTextView ),
       
    62         iSysGc( aSystemGc )
       
    63     {
       
    64     // This value has to be stored as the meaning of the flag governs behaviour of controls
       
    65     // at the point of construction
       
    66     iAppSkinEnabled = AknsUtils::AvkonSkinEnabled();
       
    67     }
       
    68 
       
    69 // -----------------------------------------------------------------------------
       
    70 // CAknEdwinCustomDrawBase::ConstructL
       
    71 // Symbian 2nd phase constructor can leave.
       
    72 // -----------------------------------------------------------------------------
       
    73 //
       
    74 void CAknEdwinCustomDrawBase::ConstructL()
       
    75     {
       
    76     iWsBufferRequestId = CAknEnv::Static()->RequestWsBuffer( KWsBufferSize );
       
    77     // Returns NULL if feature not supported.
       
    78     iPictographDrawer = CAknPictographInterface::NewL(
       
    79         static_cast<CCoeControl&>( const_cast<CEikEdwin&>( iEdwin ) ),
       
    80         *static_cast<MAknPictographAnimatorCallBack*>( this ) );
       
    81     }
       
    82 
       
    83 // -----------------------------------------------------------------------------
       
    84 // CAknEdwinCustomDraw::NewL
       
    85 // Two-phased constructor.
       
    86 // -----------------------------------------------------------------------------
       
    87 //
       
    88 CAknEdwinCustomDrawBase* CAknEdwinCustomDrawBase::NewL(
       
    89     const MLafEnv& aEnv, const CEikEdwin& aControl )
       
    90     { // static
       
    91     CAknEdwinCustomDrawBase* self =
       
    92         new( ELeave ) CAknEdwinCustomDrawBase( aEnv, aControl );
       
    93     CleanupStack::PushL( self );
       
    94     self->ConstructL();
       
    95     CleanupStack::Pop();
       
    96     return self;
       
    97     }
       
    98 
       
    99 // -----------------------------------------------------------------------------
       
   100 // CAknEdwinCustomDraw::NewL
       
   101 // Two-phased constructor.
       
   102 // -----------------------------------------------------------------------------
       
   103 //
       
   104 CAknEdwinCustomDrawBase* CAknEdwinCustomDrawBase::NewL(
       
   105     const MLafEnv& aEnv,
       
   106     const CEikEdwin& aControl,
       
   107     CTextView* aTextView,
       
   108     CWindowGc* aSystemGc )
       
   109     { // static
       
   110     CAknEdwinCustomDrawBase* self = new( ELeave ) CAknEdwinCustomDrawBase(
       
   111         aEnv, aControl, aTextView, aSystemGc );
       
   112     CleanupStack::PushL( self );
       
   113     self->ConstructL();
       
   114     CleanupStack::Pop();
       
   115     return self;
       
   116     }
       
   117 
       
   118 // Destructor
       
   119 CAknEdwinCustomDrawBase::~CAknEdwinCustomDrawBase()
       
   120     {
       
   121     delete iPictographDrawer;
       
   122     CAknEnv::Static()->CancelWsBufferRequest( iWsBufferRequestId );
       
   123     }
       
   124 
       
   125 // -----------------------------------------------------------------------------
       
   126 // CAknEdwinCustomDrawBase::DrawPictographArea
       
   127 // -----------------------------------------------------------------------------
       
   128 //
       
   129 void CAknEdwinCustomDrawBase::DrawPictographArea()
       
   130     {
       
   131     iEdwin.DrawTextView();
       
   132 
       
   133     // Mainly used for drawing form highlight frame
       
   134     const TCallBack& callback = iEdwin.PictographAnimationCallBack();
       
   135     if ( callback.iFunction )
       
   136         {
       
   137         callback.CallBack();
       
   138         }
       
   139     }
       
   140 
       
   141 // -----------------------------------------------------------------------------
       
   142 // CAknEdwinCustomDrawBase::DrawText
       
   143 // -----------------------------------------------------------------------------
       
   144 //
       
   145 void CAknEdwinCustomDrawBase::DrawText( const TParam& aParam, 
       
   146     const TLineInfo& aLineInfo, const TCharFormat& aFormat, const TDesC& aText,
       
   147     const TPoint& aTextOrigin, TInt aExtraPixels ) const
       
   148     {
       
   149     TRect textLinesRect = iEdwin.GetTextLinesRect();
       
   150 
       
   151     if ( !textLinesRect.IsEmpty() )
       
   152         {
       
   153         if ( aLineInfo.iInnerRect.iTl.iY < textLinesRect.iTl.iY ||
       
   154              aLineInfo.iInnerRect.iBr.iY > textLinesRect.iBr.iY )
       
   155             {
       
   156             return;
       
   157             }
       
   158         }
       
   159 
       
   160     TCharFormat alteredFormat(aFormat);
       
   161 
       
   162     if( aFormat.iFontPresentation.iUnderline == EUnderlineOn )
       
   163         {
       
   164         // Inhibit all excess pixels
       
   165         aExtraPixels = 0;
       
   166 
       
   167         TInt underlinePos(0);
       
   168         (void)TextNeedsCustomUnderline( aText, aParam, aFormat, underlinePos ) ;
       
   169         // always perform custom underlining
       
   170 
       
   171         TRect underlineRect(aParam.iDrawRect);
       
   172         TAknTextDecorationMetrics decoration( 0 ); // Give dummy font id for default decoration
       
   173         // Note that underlinePos in scalable is a delta relative to normal baseline
       
   174         underlineRect.iBr.iY = aLineInfo.iBaseline - underlinePos + decoration.BaselineToUnderlineOffset();
       
   175         underlineRect.iTl.iY = underlineRect.iBr.iY - decoration.UnderlineHeight();
       
   176 
       
   177         TRAPD
       
   178             (
       
   179             err,
       
   180             DrawUnderlineL( underlineRect, aParam,aLineInfo,alteredFormat,aText,aTextOrigin,aExtraPixels)
       
   181             );
       
   182         if ( err != KErrNone )
       
   183             {
       
   184             // Just draw the underline solid
       
   185             aParam.iGc.DrawRect( underlineRect );
       
   186             }
       
   187         // we've handled the underline, so switch it off now
       
   188         alteredFormat.iFontPresentation.iUnderline = EUnderlineOff;
       
   189         aParam.iGc.SetUnderlineStyle( EUnderlineOff);  // and force it off in the GC
       
   190         }
       
   191     CFont* font = NULL;
       
   192     aParam.iMap.GetNearestFontInTwips( font, aFormat.iFontSpec );
       
   193     RRegion rgn;
       
   194     if ( iEdwin.IsSmileyEnabled() && font )
       
   195         {
       
   196         rgn.AddRect( iEdwin.AdjustDrawRectForSmiley( aParam.iDrawRect ) );
       
   197         iEdwin.GetClipRegionForSmiley( rgn, *font, aText, aTextOrigin, 
       
   198             aParam.iDrawRect );
       
   199         aParam.iGc.SetClippingRegion( rgn );
       
   200         }
       
   201     CLafEdwinCustomDrawBase::DrawText( aParam, aLineInfo, alteredFormat, 
       
   202         aText, aTextOrigin, aExtraPixels );
       
   203     if ( iEdwin.IsSmileyEnabled() )
       
   204         {
       
   205         aParam.iGc.CancelClippingRegion();
       
   206         }
       
   207     rgn.Close();
       
   208     
       
   209     if ( iEdwin.IsSmileyEnabled() && font && ( &aParam.iGc ==
       
   210         static_cast<CGraphicsContext*>( iTextView->BitmapContext() ) ) )
       
   211         {
       
   212         CBitmapContext* bitmapGc( iTextView->BitmapContext() );
       
   213         CEikEdwin& edwin = const_cast<CEikEdwin&>( iEdwin );
       
   214         TPoint startPt( aParam.iDrawRect.iTl );
       
   215         startPt.iY = aLineInfo.iBaseline;
       
   216         TRAP_IGNORE( {
       
   217             edwin.HandleScrollForSmileyL();
       
   218             edwin.DrawSmileyInTextL( *bitmapGc, *font, aText, startPt );
       
   219             } );
       
   220         }
       
   221 
       
   222     // Draw pictographs if the feature is supported.
       
   223     // Character justification is not supported.
       
   224     if ( ( iPictographDrawer && !aExtraPixels ) )
       
   225         {
       
   226         CBitmapContext* bitmapGc = NULL;
       
   227 
       
   228         // This is how we check the type of the graphics context
       
   229         // The gc should be one of these:
       
   230         if ( &aParam.iGc == static_cast<CGraphicsContext*>( iSysGc ) )
       
   231             {
       
   232             bitmapGc = iSysGc;
       
   233             }
       
   234         else if ( &aParam.iGc ==
       
   235             static_cast<CGraphicsContext*>( iTextView->BitmapContext() ) )
       
   236             {
       
   237             bitmapGc = iTextView->BitmapContext();
       
   238             }
       
   239 
       
   240         if ( bitmapGc )
       
   241             {
       
   242             const TText* text = aText.Ptr();
       
   243             TInt length( aText.Length() ); 
       
   244                         
       
   245             TPoint point = aParam.iDrawRect.iTl;
       
   246             point.iY = aLineInfo.iBaseline;
       
   247             if ( iPictographDrawer && !aExtraPixels )
       
   248                 {
       
   249                 MAknPictographDrawer* drawer = 
       
   250                     iPictographDrawer->Interface();                
       
   251                 if ( font )
       
   252                     {
       
   253                     drawer->DrawPictographsInText(
       
   254                         *bitmapGc,
       
   255                         *font,
       
   256                         aText,
       
   257                         point);                    
       
   258                     }
       
   259                 }            
       
   260             }
       
   261         }
       
   262     aParam.iMap.ReleaseFont( font );
       
   263     // set brush back to original
       
   264     aParam.iGc.SetBrushColor(CEikonEnv::Static()->ControlColor(EColorControlBackground,iEdwin));
       
   265     }
       
   266 
       
   267 // -----------------------------------------------------------------------------
       
   268 // CAknEdwinCustomDrawBase::DrawText
       
   269 // -----------------------------------------------------------------------------
       
   270 //
       
   271 void CAknEdwinCustomDrawBase::DrawText( const TParam& aParam, 
       
   272     const TLineInfo& aLineInfo, const TCharFormat& aFormat, const TDesC& aText, 
       
   273     const TInt aStart, const TInt aEnd, const TPoint& aTextOrigin, 
       
   274     TInt aExtraPixels ) const
       
   275     {
       
   276     DrawText( aParam, aLineInfo, aFormat, aText.Mid( aStart, aEnd - aStart ), 
       
   277         aTextOrigin, aExtraPixels );
       
   278     }
       
   279 
       
   280 // -----------------------------------------------------------------------------
       
   281 // CAknEdwinCustomDrawBase::DrawBackground
       
   282 // -----------------------------------------------------------------------------
       
   283 //
       
   284 void CAknEdwinCustomDrawBase::DrawBackground(
       
   285     const TParam& aParam, const TRgb& aBackground, TRect& aDrawnRect ) const
       
   286     {
       
   287     if ( !iEdwin.IsBackgroundDrawingSuppressed() )
       
   288         {
       
   289         const MCoeControlBackground* drawer = iEdwin.FindBackground();
       
   290         
       
   291         if ( drawer )
       
   292             {
       
   293             if ( !iEdwin.SkipBackgroundDrawer() )
       
   294                 {
       
   295                 drawer->Draw( static_cast<CWindowGc&>( aParam.iGc ),
       
   296                               iEdwin,
       
   297                               aParam.iDrawRect );
       
   298                 }
       
   299             }
       
   300         else
       
   301             {
       
   302             if ( !DrawRectWithSkin( aParam.iGc, aParam.iDrawRect, aDrawnRect ) )
       
   303                 {
       
   304                 aParam.iGc.SetBrushStyle( CGraphicsContext::ESolidBrush );
       
   305                 aParam.iGc.SetPenStyle( CGraphicsContext::ESolidPen );
       
   306                 aParam.iGc.SetBrushColor( aBackground );
       
   307                 aParam.iGc.SetPenColor( aBackground );
       
   308                 aParam.iGc.DrawRect( aParam.iDrawRect );
       
   309                 }
       
   310             }
       
   311         }
       
   312     
       
   313     aDrawnRect = aParam.iDrawRect;
       
   314     }
       
   315 
       
   316 // -----------------------------------------------------------------------------
       
   317 // CAknEdwinCustomDrawBase::DrawLineGraphics
       
   318 // -----------------------------------------------------------------------------
       
   319 //
       
   320 void CAknEdwinCustomDrawBase::DrawLineGraphics(const TParam& aParam,const TLineInfo& aLineInfo ) const
       
   321     {
       
   322     CLafEdwinCustomDrawBase::DrawLineGraphics( aParam, aLineInfo ); // Base Call
       
   323     }
       
   324 
       
   325 
       
   326 TRgb CAknEdwinCustomDrawBase::SystemColor(TUint aColorIndex,TRgb aDefaultColor) const
       
   327     {
       
   328     TRgb ret = aDefaultColor;
       
   329     if (aColorIndex==TLogicalRgb::ESystemForegroundIndex)
       
   330         {
       
   331         MAknsSkinInstance* skin = AknsUtils::SkinInstance();
       
   332         if (skin && iEdwin.SkinColorId() != KErrNotFound)
       
   333             {
       
   334             AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnTextColors, iEdwin.SkinColorId());
       
   335             }
       
   336         }
       
   337     else if (aColorIndex==TLogicalRgb::ESystemBackgroundIndex)
       
   338         {
       
   339         // Only override aDefaultColor if SetBackgroundColorL was called:
       
   340         (void)iEdwin.EditorBackgroundColor(ret);
       
   341         // Insist on opaque background
       
   342         ret.SetAlpha(0xFF);
       
   343         }
       
   344 
       
   345     else if (aColorIndex==TLogicalRgb::ESystemSelectionForegroundIndex)
       
   346         {
       
   347         ret = KRgbWhite;
       
   348         MAknsSkinInstance* skin = AknsUtils::SkinInstance();
       
   349         if (skin)
       
   350             {
       
   351             if ( iEdwin.HighlightStyle() == EEikEdwinHighlightLink )
       
   352                 {
       
   353                 AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnHighlightColors, EAknsCIQsnTextColorsCG3);
       
   354                 }
       
   355             else // default
       
   356                 {
       
   357                 AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG24);
       
   358                 }
       
   359             }
       
   360         }
       
   361     else if (aColorIndex==TLogicalRgb::ESystemSelectionBackgroundIndex)
       
   362         {
       
   363         ret = KRgbBlue;
       
   364         MAknsSkinInstance* skin = AknsUtils::SkinInstance();
       
   365         if (skin)
       
   366             {
       
   367             if ( iEdwin.HighlightStyle() == EEikEdwinHighlightLink )
       
   368                 {
       
   369                 AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnHighlightColors, EAknsCIQsnTextColorsCG1);
       
   370                 }
       
   371             else // default
       
   372                 {
       
   373                 AknsUtils::GetCachedColor(skin, ret, KAknsIIDQsnHighlightColors, EAknsCIQsnHighlightColorsCG2);
       
   374                 }
       
   375             }
       
   376         }
       
   377 
       
   378     return ret;
       
   379     }
       
   380 
       
   381 void CAknEdwinCustomDrawBase::CAknEdwinCustomDrawBase_Reserved_1()
       
   382     {
       
   383     }
       
   384 
       
   385 /**
       
   386 * Routine to group the common skin custom drawing
       
   387 *
       
   388 * @return TBool EFalse iff the drawing was not performed
       
   389 */
       
   390 TBool CAknEdwinCustomDrawBase::DrawRectWithSkin( const CGraphicsContext& aGc, const TRect& aRect, TRect& aDrawnRect ) const
       
   391     {
       
   392     TBool drawn(EFalse);
       
   393 
       
   394     if ( iAppSkinEnabled && iTextView)
       
   395         {
       
   396         CBitmapContext* bitmapGc = NULL;
       
   397 
       
   398         // This is how we check the type of the graphics context
       
   399         // The gc should be one of these:
       
   400         if ( &aGc == static_cast<CGraphicsContext*>( iSysGc ) )
       
   401             bitmapGc = iSysGc;
       
   402         else if ( &aGc == static_cast<CGraphicsContext*>( iTextView->BitmapContext() ) )
       
   403             bitmapGc = iTextView->BitmapContext();
       
   404 #ifdef RD_UI_TRANSITION_EFFECTS_POPUPS
       
   405         else if( &aGc == static_cast<CGraphicsContext*>( &(iControl.SystemGc()) ) )
       
   406             bitmapGc = &(iControl.SystemGc());
       
   407 #endif //RD_UI_TRANSITION_EFFECTS_POPUPS
       
   408         if ( bitmapGc && iEdwin.SkinEnabled() )
       
   409             {
       
   410             MAknsSkinInstance* skin = AknsUtils::SkinInstance();
       
   411             // Edwin manages the skin background
       
   412             MAknsControlContext* edCc = iEdwin.SkinBackgroundControlContext();
       
   413 
       
   414             TInt drawFlags = KAknsDrawParamDefault;
       
   415             if( CAknEnv::Static()->TransparencyEnabled() )
       
   416             {
       
   417                 drawFlags |= KAknsDrawParamNoClearUnderImage;
       
   418             }
       
   419 
       
   420             if( AknsDrawUtils::DrawBackground( skin, edCc,
       
   421                     &iControl, *bitmapGc, aRect.iTl, aRect,
       
   422                     drawFlags ))
       
   423                 {
       
   424                 aDrawnRect = aRect;
       
   425                 drawn = ETrue;
       
   426                 }
       
   427             }
       
   428         else
       
   429             {
       
   430             // perform unoptimized draw of skin using bitmap brushstyle?
       
   431             }
       
   432 
       
   433         }
       
   434 
       
   435     return drawn;
       
   436     }
       
   437 
       
   438 TBool CAknEdwinCustomDrawBase::TextNeedsCustomUnderline( const TDesC& aText, const TParam& aParam, const TCharFormat& aFormat, TInt& underlinePos ) const
       
   439     {
       
   440     TBool needsCustomUnderline(EFalse);
       
   441 
       
   442     TInt fontProviderIndex = AknFontProvider::FontProviderIndexFromFontSpec( aFormat.iFontSpec );
       
   443     if ( fontProviderIndex > KErrNotFound )
       
   444         {
       
   445         needsCustomUnderline = AknFontProvider::HasBaselineCorrection( fontProviderIndex );
       
   446         if ( needsCustomUnderline )
       
   447             {
       
   448             TInt denominatorIfFractional(KAknFontProviderBaselineCorrectionIsAbsolute);
       
   449             TInt numInitialCharsTheSame(0); // not used
       
   450             TInt minimumBaselineLift(0);
       
   451             AknFontProvider::MinimumBaselineDeltaForDescriptor(
       
   452                 fontProviderIndex,
       
   453                 aText,
       
   454                 minimumBaselineLift,
       
   455                 denominatorIfFractional,
       
   456                 EFalse, // Look at whole descriptor and take minimum baseline lift
       
   457                 numInitialCharsTheSame);
       
   458 
       
   459             // Check for proportional baseline deltas:
       
   460             if ( denominatorIfFractional != KAknFontProviderBaselineCorrectionIsAbsolute )
       
   461                 {
       
   462                 underlinePos = minimumBaselineLift * aParam.iDrawRect.Height() / denominatorIfFractional;
       
   463                 }
       
   464             else
       
   465                 {
       
   466                 underlinePos = minimumBaselineLift;
       
   467                 }
       
   468             }
       
   469         }
       
   470 
       
   471     return needsCustomUnderline;
       
   472     }
       
   473 
       
   474 // -----------------------------------------------------------------------------
       
   475 // CAknEdwinCustomDrawBase::DrawUnderlineL
       
   476 // -----------------------------------------------------------------------------
       
   477 /*
       
   478 This method has the responsibility for drawing a custom underline.
       
   479 
       
   480 An offscreen bitmap with 8 bit greyscale is made the size of the underline. Using the same font as the editor, the text
       
   481 is drawn onto this bitmap, with variable positioning, so that the text is smeared.
       
   482 
       
   483 The "descenderBitmap" is passed to a routine that analyzes this bitmap and draws the underlines only in
       
   484 between the descenders, leaving gaps.
       
   485 
       
   486 */
       
   487 void CAknEdwinCustomDrawBase::DrawUnderlineL(
       
   488         const TRect& aUnderlineRect,
       
   489         const TParam& aParam,
       
   490         const TLineInfo& aLineInfo,
       
   491         const TCharFormat& aFormat,
       
   492         const TDesC& aText,
       
   493         const TPoint& /*aTextOrigin*/, // Not currently used
       
   494         TInt aExtraPixels) const
       
   495     {
       
   496 
       
   497     if (aUnderlineRect.Width() <= 0 || aUnderlineRect.Height() <= 0 )
       
   498         return;
       
   499 
       
   500     TRect localUnderlineRect(aUnderlineRect);
       
   501 
       
   502     // Create bitmap to draw the descenders to
       
   503     CFbsBitmap* descendersBitmap = new (ELeave) CFbsBitmap();
       
   504     CleanupStack::PushL( descendersBitmap);
       
   505     User::LeaveIfError( descendersBitmap->Create(TSize(localUnderlineRect.Width(),localUnderlineRect.Height()) , EGray256));
       
   506 
       
   507     CFbsBitmapDevice* device = CFbsBitmapDevice::NewL(descendersBitmap);
       
   508     CleanupStack::PushL(device);
       
   509     CGraphicsContext* context=NULL;
       
   510     User::LeaveIfError(device->CreateContext(context));
       
   511     CleanupStack::PushL(context);
       
   512 
       
   513     CFont* font;
       
   514     // Fetch the font.  This font is already in use by the editor.  Since it is a request in twips, it will be in the
       
   515     // twips cache and the request should not go to the Fbserv server.  It should therefore be fast.
       
   516     User::LeaveIfError(aParam.iMap.GetNearestFontInTwips( font, aFormat.iFontSpec) );
       
   517 
       
   518     context->UseFont( font );
       
   519     DrawDescendersOntoBitmap( *context, aText, aParam, aLineInfo, localUnderlineRect, aExtraPixels );
       
   520     context->DiscardFont ();
       
   521 
       
   522     aParam.iMap.ReleaseFont( font );
       
   523 
       
   524     // 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
       
   525     // already drawn onto descendersBitmap
       
   526     DrawLineWithGaps( aParam, *descendersBitmap, localUnderlineRect ) ;
       
   527 
       
   528     // Delete all the local objects
       
   529     CleanupStack::PopAndDestroy( context );
       
   530     CleanupStack::PopAndDestroy( device );
       
   531     CleanupStack::PopAndDestroy( descendersBitmap );
       
   532     }
       
   533 
       
   534 /**
       
   535 *  The descender parts of the text are drawn onto the bitmap context.
       
   536 *  The text is drawn several times with horizontal and vertical offsets. The resulting bitmap will be used to determine
       
   537 *  where the underline should be drawn to avoid touching the descenders
       
   538 */
       
   539 void CAknEdwinCustomDrawBase::DrawDescendersOntoBitmap(
       
   540         CGraphicsContext& aContext,
       
   541         const TDesC& aText,
       
   542         const TParam& /*aParam*/,
       
   543         const TLineInfo& aLineInfo,
       
   544         const TRect& aUnderlineRect,
       
   545         TInt aExtraPixels ) const
       
   546     {
       
   547     // Have to arrange to draw text onto the underline rectangle bitmap context.
       
   548     // The text needs to be positioned so that the correct parts of the text (ie.part of the descenders)
       
   549     // are within this underline rectangle. We need to calculate the distance from the top of the drawing rectangle
       
   550     // (which will perform the clipping) to the baseline of the text.
       
   551     // This will be used as the  "aBaselineOffset" parameter to the CGraphicsContext::DrawText method.
       
   552     // Expect this value to be small and negative, since the baseline is usually above the underline.
       
   553     TInt posOfBaselineFromUnderline =  posOfBaselineFromUnderline = aLineInfo.iBaseline - aUnderlineRect.iTl.iY;
       
   554 
       
   555     // Here, draw the text with letter spacing. This is what Tagma will do when it really draws the text.
       
   556     if (aExtraPixels)
       
   557         {
       
   558         aContext.SetCharJustification(aExtraPixels,aText.Length());
       
   559         }
       
   560 
       
   561     // We are going to draw the text moved around both horizontally and vertically.
       
   562     // Use this rectangle with 0,0 at top left to match the bitmap's own 0,0-based coord system
       
   563     TRect movingRect( TPoint(0,0), TPoint( aUnderlineRect.Width(), aUnderlineRect.Height()));
       
   564 
       
   565     // Double loop to perform text smearing horizontally and vertically
       
   566 
       
   567     // These constants reflect the desired gaps around the descenders
       
   568     // This calculation allows the descender-avoiding gaps to scale:
       
   569     const TInt KHorizontalSmear = Max( aUnderlineRect.Height()/2, 1);
       
   570     const TInt KVerticalSmear = KHorizontalSmear;
       
   571 
       
   572     for ( TInt xOffset = -KHorizontalSmear; xOffset <= KHorizontalSmear; xOffset++ )
       
   573         {
       
   574         for ( TInt yOffset = -KVerticalSmear; yOffset <= KVerticalSmear; yOffset++ )
       
   575             {
       
   576             // Drawing is relative to the left, and top (assuming posOfBaselineFromUnderline is constant), so fiddle
       
   577             // top left point.
       
   578             movingRect.iTl.iX = xOffset;
       
   579             movingRect.iTl.iY = 0;
       
   580             aContext.DrawText( aText, movingRect, posOfBaselineFromUnderline + yOffset, CGraphicsContext::ELeft, 0 );
       
   581             }
       
   582         }
       
   583     }
       
   584 
       
   585 /**
       
   586   Draws the underline in the aParam.iGc pen color onto the screen, but uses the smeared descenders bitmap to
       
   587   avoid drawing underline through descenders.
       
   588 
       
   589   The offscreen bitmap is walked, watching for light/dark and dark./light transitions which drive the drawing of line segments
       
   590   on the screen device.
       
   591 
       
   592   Various features are built in the bitmap walk.
       
   593   - use special "off" threshold near the beginning of a text to encourage leading bits of underline
       
   594   - special "on" threshold to encourage small trailing pieces of underlines
       
   595 
       
   596   There are special conditions that must be met for success to be claimed, and a value of ETrue to be returned.
       
   597   These conditions insist on at least one segment of line being drawn, and that lines occupy a big enough percentage of the
       
   598   total underline length.
       
   599  */
       
   600 void CAknEdwinCustomDrawBase::DrawLineWithGaps(
       
   601         const TParam& aParam,
       
   602         CFbsBitmap& aSmearedDescendersBitmap,
       
   603         const TRect& aUnderlineRect ) const
       
   604     {
       
   605     // Use the Symbian utility to provide access to bitmaps:
       
   606     TBitmapUtil util(&aSmearedDescendersBitmap);
       
   607     util.Begin( TPoint(0,0) );
       
   608 
       
   609     const TInt lastRow = aUnderlineRect.Height() - 1; // zero-based
       
   610     const TInt lastPos = aUnderlineRect.Width() - 1; // zero-based
       
   611 
       
   612     TUint32 thisPixel;
       
   613     const TUint32 thresholdPixel(254); // Must suit the TDrawMode. This value for Gray255
       
   614     const TUint32 darkerThreshold(32); // Must suit the TDrawMode. this low (near-black) value encourages underlines to start at the far left.
       
   615     const TUint32 endThreshold(128); // Must suit the TDrawMode. Lower threshold to encourage a line segment at the far right.
       
   616     const TInt shortRunLimit(1);
       
   617 
       
   618     // Anchor and end point are used to draw the lines in the most common cases
       
   619     TPoint anchor;
       
   620     TPoint earlyAnchor;
       
   621 
       
   622     // State flags
       
   623     TBool currentlyInALine;  // An anchor point of some sort has been recorded. Committed to drawing at least something for the segment.
       
   624     TBool earlyLineActive; // Special state where the earlyAnchor is used rather than the normally recorded one.
       
   625 
       
   626     TInt numberSegmentsDrawn(0); // records how many segments of underline are drawn.  Used to set a return value
       
   627     TInt lengthDrawnOnThisLine(0);
       
   628     TInt maxPercentOfALineDrawn(0);
       
   629 
       
   630     // Analyse the smeared character bitmap, and draw line segments that avoid the descenders
       
   631     // The smeared descenders appear as black (or grey) pixels in the offscreen bitmap
       
   632     // Using gray256, white is 255, black is 0.
       
   633     // Do not get confused by the fact we tend to draw where there is white, and not draw then there is black!
       
   634 
       
   635     for ( TInt row = 0; row <= lastRow; row++, util.IncYPos() )
       
   636         {
       
   637         // Set at beginning of the row at column 0
       
   638         util.SetPos( TPoint(0,row) );
       
   639 
       
   640         // row initialization.
       
   641         TInt pos = 0;  // Position in the row.
       
   642         currentlyInALine = EFalse;
       
   643         earlyLineActive = EFalse;
       
   644         lengthDrawnOnThisLine = 0; // zero the length recorded to be drawn in each row.
       
   645 
       
   646         thisPixel = util.GetPixel();
       
   647         if ( thisPixel >= darkerThreshold ) // use low (near-black) value to encourage leading lines
       
   648             {
       
   649             anchor = TPoint(aUnderlineRect.iTl.iX + pos, aUnderlineRect.iTl.iY + row); // beginning of lines
       
   650             currentlyInALine = ETrue;
       
   651             }
       
   652 
       
   653         for (util.IncXPos(), pos = 1; pos < lastPos; pos++, util.IncXPos())
       
   654             {
       
   655             thisPixel = util.GetPixel();
       
   656 
       
   657             if ( currentlyInALine )
       
   658                 {
       
   659                 // If we are not near the beginning, but have gone below normal threshold, end the line segment
       
   660                 TBool endTheLineSegment(EFalse);
       
   661                 if ( thisPixel < thresholdPixel && pos > shortRunLimit ) //then end it
       
   662                     {
       
   663                     endTheLineSegment = ETrue;
       
   664                     }
       
   665                 // If we ARE near the beginning and below the special dark threshold
       
   666                 else if ( thisPixel < darkerThreshold && pos <= shortRunLimit )
       
   667                     {
       
   668                     endTheLineSegment = ETrue;
       
   669                     }
       
   670 
       
   671                 if ( endTheLineSegment )
       
   672                     { // The last TPoint is not drawn, so use pos and not pos - 1
       
   673                     DrawLineAndRecord( aParam.iGc, aUnderlineRect, anchor, pos, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
       
   674                     currentlyInALine = EFalse;
       
   675                     earlyLineActive = EFalse; // cancel any early starts
       
   676                     }
       
   677 
       
   678                 }
       
   679             else // not currentlyInALine
       
   680                 {
       
   681                 //
       
   682                 if (thisPixel >= thresholdPixel ) // not in a line, but now above threshold
       
   683                     {
       
   684                     anchor = TPoint(aUnderlineRect.iTl.iX+pos, aUnderlineRect.iTl.iY + row);
       
   685                     currentlyInALine = ETrue;
       
   686                     }
       
   687                 else if ( thisPixel >= endThreshold ) // mark a potential "early start" for a line if even a little off black
       
   688                     {
       
   689                     // There is no test on whether we are already in earlyLineActive, so this updates, essentially recording a last
       
   690                     // point above this lower threshold
       
   691                     earlyAnchor = TPoint( aUnderlineRect.iTl.iX+pos, aUnderlineRect.iTl.iY + row );
       
   692                     earlyLineActive = ETrue;
       
   693                     // But not currently in a line
       
   694                     }
       
   695                 else // not in a line and going below (darker than) endThreshold cancels the earlyLine stuff
       
   696                     {
       
   697                     earlyLineActive = EFalse;
       
   698                     }
       
   699                 }
       
   700 
       
   701             }  // end of for loop over columns
       
   702 
       
   703         // Finish off the line. Pixel index already incremented by the for loop, but the pixel has not been fetched
       
   704         thisPixel = util.GetPixel();
       
   705         if ( currentlyInALine)
       
   706             {
       
   707             // 2nd TPoint in the call is not plotted, so go further, to lastPos+1
       
   708             DrawLineAndRecord( aParam.iGc, aUnderlineRect, anchor, lastPos+1, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
       
   709             }
       
   710         else
       
   711             {
       
   712             // Special rule, with special low threshold, to get end runs even if somewhat suppressed
       
   713             if ( thisPixel >= endThreshold )
       
   714                 {
       
   715                 if ( earlyLineActive ) // Use the recorded early anchor
       
   716                     {
       
   717                     // 2nd TPoint in the call is not plotted, so go further by 1
       
   718                     DrawLineAndRecord( aParam.iGc, aUnderlineRect, earlyAnchor, lastPos+1, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
       
   719                     }
       
   720                 else // just draw the one point
       
   721                     {
       
   722                     TPoint startPoint(aUnderlineRect.iTl.iX+lastPos,aUnderlineRect.iTl.iY + row);
       
   723                     // 2nd TPoint in the call is not plotted, so go further by 1
       
   724                     DrawLineAndRecord( aParam.iGc, aUnderlineRect, startPoint, lastPos+1, row, lengthDrawnOnThisLine, numberSegmentsDrawn);
       
   725                     }
       
   726                 }
       
   727             }
       
   728 
       
   729         maxPercentOfALineDrawn = Max( maxPercentOfALineDrawn, (lengthDrawnOnThisLine * 100 )/aUnderlineRect.Width()  );
       
   730         } // end of for loop over rows
       
   731 
       
   732     util.End();
       
   733 
       
   734     // Check criteria for whether to say this was successful or not.
       
   735     const TInt KMinPercentOfLineDrawn(33); // need a third drawn
       
   736     if ( numberSegmentsDrawn <  (aUnderlineRect.Height()*2) && ( maxPercentOfALineDrawn < KMinPercentOfLineDrawn ) )
       
   737         {
       
   738         // Not enough line drawn.
       
   739         // Extend the underlines on both sides.  Use some scaling for how much to extend
       
   740         const TInt extraLineLength = Max( 1, aUnderlineRect.Height() );
       
   741         for ( TInt row = 0; row <= lastRow; row++ ) // lastRow is zero-based
       
   742             {
       
   743             aParam.iGc.DrawLine(
       
   744                 TPoint(aUnderlineRect.iTl.iX-extraLineLength, aUnderlineRect.iTl.iY+ row ),
       
   745                 TPoint(aUnderlineRect.iTl.iX, aUnderlineRect.iTl.iY+ row)  );
       
   746             aParam.iGc.DrawLine(
       
   747                 TPoint(aUnderlineRect.iBr.iX,  aUnderlineRect.iTl.iY+ row ),
       
   748                 TPoint(aUnderlineRect.iBr.iX+extraLineLength, aUnderlineRect.iTl.iY+ row)  );
       
   749             }
       
   750 
       
   751         }
       
   752     }
       
   753 
       
   754 void CAknEdwinCustomDrawBase::DrawLineAndRecord(
       
   755     CGraphicsContext& aGc,
       
   756     const TRect& aRect,
       
   757     const TPoint& aAnchor,
       
   758     TInt aColumn,
       
   759     TInt aRow,
       
   760     TInt& aAccumulatedLength,
       
   761     TInt& aSegmentsDrawn) const
       
   762     {
       
   763     TPoint endPoint = TPoint(aRect.iTl.iX + aColumn, aRect.iTl.iY + aRow);
       
   764     aGc.DrawLine( aAnchor, endPoint );
       
   765     aAccumulatedLength += (endPoint.iX - aAnchor.iX);
       
   766     aSegmentsDrawn++;
       
   767     }