|
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 } |