|
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: Avkon text wrapper implementation |
|
15 * This class is used by AknTextUtils and AknBidiTextUtils. |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 |
|
21 // INCLUDE FILES |
|
22 #include "AknTextWrapper.h" |
|
23 #include "aknenv.h" |
|
24 #include "AknBidiTextUtils.h" |
|
25 #include <gdi.h> |
|
26 #include <uikon.hrh> |
|
27 #include <bidi.h> |
|
28 #include <bidivisual.h> |
|
29 |
|
30 // CONSTANTS |
|
31 |
|
32 // line feed, carriage return, line separator, paragraph separator |
|
33 _LIT( KSeparators, "\x000a\x000d\x2028\x2029" ); |
|
34 |
|
35 const TText KLineFeed = 0x000A; |
|
36 const TText KCarriageReturn = 0x000D; |
|
37 const TText KLineSeparator = 0x2028; |
|
38 const TText KParagraphSeparator = 0x2029; |
|
39 const TText KZeroWidthSpace = 0x200B; |
|
40 const TText KFirstThaiCharacter = 0x0E01; |
|
41 const TText KLastThaiCharacter = 0x0E5B; |
|
42 |
|
43 enum |
|
44 { |
|
45 EWrapToArray = 0, |
|
46 EChopToArrayAndClip = 1 |
|
47 }; |
|
48 |
|
49 const TText KAknLocVariantSeparator = 0x0001; |
|
50 |
|
51 // Helper classes for User::QuickSort. |
|
52 |
|
53 NONSHARABLE_CLASS(TMyKey) : public TKey |
|
54 { |
|
55 // From TKey |
|
56 private: |
|
57 TInt Compare( TInt aLeft, TInt aRight ) const; |
|
58 TAny* At( TInt aIndex ) const; |
|
59 }; |
|
60 |
|
61 TInt TMyKey::Compare( TInt aLeft, TInt aRight ) const |
|
62 { |
|
63 const TAknLocVariant* ptr = static_cast<const TAknLocVariant*>( iPtr ); |
|
64 TInt leftLength = ptr[aLeft].iEnd - ptr[aLeft].iStart; |
|
65 TInt rightLength = ptr[aRight].iEnd - ptr[aRight].iStart; |
|
66 |
|
67 return rightLength - leftLength; |
|
68 } |
|
69 |
|
70 TAny* TMyKey::At( TInt aIndex ) const |
|
71 { |
|
72 const TAknLocVariant* ptr = static_cast<const TAknLocVariant*>( iPtr ); |
|
73 return (TAny*)( &ptr[aIndex] ); |
|
74 } |
|
75 |
|
76 NONSHARABLE_CLASS(TMySwap) : public TSwap |
|
77 { |
|
78 // Constructor |
|
79 public: |
|
80 inline TMySwap( TAknLocVariant* aVariants ) : iVariants( aVariants ) {} |
|
81 // From TSwap |
|
82 private: |
|
83 void Swap( TInt aLeft, TInt aRight ) const; |
|
84 // Data |
|
85 private: |
|
86 TAknLocVariant* iVariants; |
|
87 }; |
|
88 |
|
89 void TMySwap::Swap( TInt aLeft, TInt aRight ) const |
|
90 { |
|
91 TAknLocVariant temp = iVariants[aLeft]; |
|
92 iVariants[aLeft] = iVariants[aRight]; |
|
93 iVariants[aRight] = temp; |
|
94 } |
|
95 |
|
96 // ============================ MEMBER FUNCTIONS =============================== |
|
97 |
|
98 // ----------------------------------------------------------------------------- |
|
99 // TAknTextWrapper::TAknTextWrapper |
|
100 // C++ default constructor can NOT contain any code, that |
|
101 // might leave. |
|
102 // ----------------------------------------------------------------------------- |
|
103 // |
|
104 TAknTextWrapper::TAknTextWrapper( |
|
105 TDes& aStringToWrap, |
|
106 const CArrayFix<TInt>* aLineWidthArray, |
|
107 const CFont& aFont, |
|
108 CArrayFix<TPtrC>& aWrappedArray, |
|
109 TInt aLineWidth, |
|
110 TInt aFlags, |
|
111 AknBidiTextUtils::TParagraphDirectionality aDirectionality ) : |
|
112 iSourceString( aStringToWrap ), |
|
113 iLineWidthArray( aLineWidthArray ), |
|
114 iFont( aFont ), |
|
115 iResultArray( aWrappedArray ), |
|
116 iLineWidth( aLineWidth ), |
|
117 iFlags( aFlags ), |
|
118 iDirectionality( aDirectionality ), |
|
119 iSeparators( KSeparators() ), |
|
120 iText( NULL, 0 ) |
|
121 { |
|
122 } |
|
123 |
|
124 // Destructor |
|
125 TAknTextWrapper::~TAknTextWrapper() |
|
126 { |
|
127 } |
|
128 |
|
129 // ----------------------------------------------------------------------------- |
|
130 // TAknTextWrapper::WrapToArrayL |
|
131 // ----------------------------------------------------------------------------- |
|
132 // |
|
133 HBufC* TAknTextWrapper::WrapToArrayL() |
|
134 { |
|
135 return DoOperationL( EWrapToArray ); |
|
136 } |
|
137 |
|
138 // ----------------------------------------------------------------------------- |
|
139 // TAknTextWrapper::ChopToArrayAndClipL |
|
140 // ----------------------------------------------------------------------------- |
|
141 // |
|
142 HBufC* TAknTextWrapper::ChopToArrayAndClipL() |
|
143 { |
|
144 return DoOperationL( EChopToArrayAndClip ); |
|
145 } |
|
146 |
|
147 // ----------------------------------------------------------------------------- |
|
148 // TAknTextWrapper::DoOperationL |
|
149 // ----------------------------------------------------------------------------- |
|
150 // |
|
151 HBufC* TAknTextWrapper::DoOperationL( TInt aOp ) |
|
152 { |
|
153 iResultArray.Reset(); |
|
154 |
|
155 if ( !iSourceString.Length() ) |
|
156 { |
|
157 if ( iFlags & EReserveVisualBuffer ) |
|
158 { |
|
159 return HBufC::NewL( 0 ); |
|
160 } |
|
161 else |
|
162 { |
|
163 return NULL; |
|
164 } |
|
165 } |
|
166 |
|
167 HBufC* ret = NULL; |
|
168 |
|
169 TAknLocVariant variants[KAknMaxLocVariants]; |
|
170 TInt numVariants = GetLocVariants( iSourceString, variants ); |
|
171 |
|
172 iFlags &= ~EFits; |
|
173 TInt i = 0; |
|
174 |
|
175 HBufC* backupBuf = iSourceString.AllocLC(); |
|
176 |
|
177 for ( ; i < numVariants && !(iFlags & EFits); i++ ) |
|
178 { |
|
179 delete ret; |
|
180 ret = NULL; |
|
181 |
|
182 TInt start = variants[i].iStart; |
|
183 |
|
184 iSourceString.Copy( |
|
185 backupBuf->Mid( start, variants[i].iEnd - start ) ); |
|
186 |
|
187 iFlags |= EFits; |
|
188 // Clears EFits if truncated. |
|
189 if ( aOp == EWrapToArray ) |
|
190 { |
|
191 ret = DoWrapToArrayL(); |
|
192 } |
|
193 else |
|
194 { |
|
195 ret = DoChopToArrayAndClipL(); |
|
196 } |
|
197 } |
|
198 |
|
199 CleanupStack::PopAndDestroy(); // backupBuf |
|
200 return ret; |
|
201 } |
|
202 |
|
203 // ----------------------------------------------------------------------------- |
|
204 // TAknTextWrapper::DoWrapToArrayL |
|
205 // ----------------------------------------------------------------------------- |
|
206 // |
|
207 HBufC* TAknTextWrapper::DoWrapToArrayL() |
|
208 { |
|
209 iResultArray.Reset(); |
|
210 |
|
211 // TBidiLogicalToVisual crashes with length 0 text so we check that case here |
|
212 if ( !iSourceString.Length() ) |
|
213 { |
|
214 if ( iFlags & EReserveVisualBuffer ) |
|
215 { |
|
216 return HBufC::NewL( 0 ); |
|
217 } |
|
218 else |
|
219 { |
|
220 return NULL; |
|
221 } |
|
222 } |
|
223 |
|
224 TInt index( 0 ); |
|
225 TInt lineIndex( 0 ); |
|
226 |
|
227 TInt maxLines( KMaxTInt ); |
|
228 |
|
229 if ( iLineWidthArray && !( iFlags & EWrapAllText ) ) |
|
230 { |
|
231 maxLines = iLineWidthArray->Count(); |
|
232 } |
|
233 |
|
234 while ( index < iSourceString.Length() && iResultArray.Count() < maxLines ) |
|
235 { |
|
236 TInt lineWidth = iLineWidth; |
|
237 |
|
238 if ( iLineWidthArray ) |
|
239 { |
|
240 TInt givenWidths = iLineWidthArray->Count(); |
|
241 TInt usedLineWidthIndex = lineIndex; |
|
242 if ( lineIndex >= givenWidths ) |
|
243 { |
|
244 usedLineWidthIndex = givenWidths - 1; |
|
245 } |
|
246 |
|
247 lineWidth = (*iLineWidthArray)[usedLineWidthIndex]; |
|
248 } |
|
249 |
|
250 iText.Set( iSourceString.Right( iSourceString.Length() - index ) ); |
|
251 |
|
252 TInt fitsInLine = iFont.TextCount( iText, lineWidth ); |
|
253 // If the line width is constant and no characters fit in it, stop. |
|
254 if ( !fitsInLine && |
|
255 (!iLineWidthArray || lineIndex >= iLineWidthArray->Count()) ) |
|
256 { |
|
257 break; |
|
258 } |
|
259 |
|
260 // Is there an explicit line break in the part that fits in line |
|
261 TInt newLine = FindLineBreak( iText.Left( fitsInLine ) ); |
|
262 |
|
263 if ( newLine != KErrNotFound ) |
|
264 { |
|
265 iNextLineStart = newLine; |
|
266 PassLineBreak(); // updates iNextLineStart |
|
267 |
|
268 if ( iNextLineStart == iText.Length() ) |
|
269 { |
|
270 AppendToArrayL( iText.Left( newLine ) ); |
|
271 break; |
|
272 } |
|
273 } |
|
274 |
|
275 // All remaining text fits in line? |
|
276 else if ( iText.Length() == fitsInLine ) |
|
277 { |
|
278 AppendToArrayL( iText ); |
|
279 break; |
|
280 } |
|
281 |
|
282 TInt wrapIndex( 0 ); |
|
283 |
|
284 // Last line? If so, clip it if required... |
|
285 |
|
286 if ( iFlags & EClip && lineIndex == maxLines - 1 ) |
|
287 { |
|
288 if ( newLine != KErrNotFound ) |
|
289 { |
|
290 iText.Set( iText.Left( newLine ) ); |
|
291 } |
|
292 |
|
293 // This actually inserts ellipsis in text only if logical to |
|
294 // visual conversion is not used. |
|
295 wrapIndex = InsertEllipsis( iText, lineWidth ); |
|
296 // This flag informs logical to visual conversion to insert |
|
297 // truncation character |
|
298 iFlags |= EClipRequired; |
|
299 iFlags &= ~EFits; |
|
300 AppendToArrayL( iText.Left( wrapIndex ) ); |
|
301 break; |
|
302 } |
|
303 |
|
304 // Not last line, so no clipping required yet |
|
305 if ( newLine != KErrNotFound ) |
|
306 { |
|
307 wrapIndex = newLine; |
|
308 } |
|
309 // No explicit line break in the part that fits in line, |
|
310 // find wrapping potision |
|
311 else |
|
312 { |
|
313 if ( GetWrappingPosition( iText, fitsInLine, lineWidth, ETrue ) ) |
|
314 { |
|
315 wrapIndex = iBreakPos; |
|
316 } |
|
317 |
|
318 else |
|
319 { |
|
320 // No legal wrapping position found => illegal line breaking. |
|
321 // Put all that fits in line. |
|
322 wrapIndex = fitsInLine; |
|
323 iNextLineStart = fitsInLine; |
|
324 } |
|
325 |
|
326 PassLineBreak(); // updates iNextLineStart |
|
327 } |
|
328 |
|
329 AppendToArrayL( iText.Left( wrapIndex ) ); |
|
330 index += iNextLineStart; |
|
331 |
|
332 // next line to be wrapped |
|
333 lineIndex++; |
|
334 } |
|
335 |
|
336 return ConvertToVisualIfRequiredL(); |
|
337 } |
|
338 |
|
339 // ----------------------------------------------------------------------------- |
|
340 // TAknTextWrapper::DoChopToArrayAndClipL |
|
341 // ----------------------------------------------------------------------------- |
|
342 // |
|
343 HBufC* TAknTextWrapper::DoChopToArrayAndClipL() |
|
344 { |
|
345 iResultArray.Reset(); |
|
346 |
|
347 // First determine succifient amount of text lines for visual buffer. |
|
348 |
|
349 TInt maxLines = 0; |
|
350 |
|
351 if ( iLineWidthArray ) |
|
352 { |
|
353 maxLines = iLineWidthArray->Count(); |
|
354 } |
|
355 |
|
356 else |
|
357 { |
|
358 TInt length = iSourceString.Length(); |
|
359 const TText* text = iSourceString.Ptr(); |
|
360 |
|
361 // This counts 2 lines for lines ending to CR+LF but it does not |
|
362 // really matter as we are determining a succifient number of lines. |
|
363 for ( TInt i = 0 ; i < length ; i++ ) |
|
364 { |
|
365 if ( iSeparators.Locate( text[i] ) != KErrNotFound ) |
|
366 { |
|
367 maxLines++; |
|
368 } |
|
369 } |
|
370 |
|
371 maxLines++; // add one extra for the line after the last line break. |
|
372 } |
|
373 |
|
374 // Allocate visual buffer. |
|
375 HBufC* visualBuffer = HBufC::NewLC( |
|
376 iSourceString.Length() + maxLines * KAknBidiExtraSpacePerLine ); |
|
377 |
|
378 TInt index( 0 ); // index in iSourceString |
|
379 TInt lineIndex( 0 ); // line number |
|
380 TInt visualIndex( 0 ); // index in visualBuffer |
|
381 |
|
382 // We utilize wrapping functionality for each line to be chopped. |
|
383 // These temp arrays are for that purpose. |
|
384 CArrayFix<TInt>* tempLineWidthArray = |
|
385 new( ELeave ) CArrayFixFlat<TInt>( 1 ); |
|
386 CleanupStack::PushL( tempLineWidthArray ); |
|
387 tempLineWidthArray->AppendL( 0 ); |
|
388 |
|
389 CArrayFix<TPtrC>* tempResultArray = |
|
390 new( ELeave ) CArrayFixFlat<TPtrC>( 1 ); |
|
391 CleanupStack::PushL( tempResultArray ); |
|
392 |
|
393 while ( index < iSourceString.Length() && iResultArray.Count() < maxLines ) |
|
394 { |
|
395 iText.Set( iSourceString.Right( iSourceString.Length() - index ) ); |
|
396 |
|
397 // Locate line break |
|
398 iNextLineStart = FindLineBreak( iText ); |
|
399 |
|
400 if ( iNextLineStart == KErrNotFound ) |
|
401 { |
|
402 iNextLineStart = iText.Length(); |
|
403 } |
|
404 |
|
405 TInt flags( TAknTextWrapper::EClip ); |
|
406 |
|
407 if ( iFlags & EConvertToVisual ) |
|
408 { |
|
409 flags |= EConvertToVisual; |
|
410 } |
|
411 |
|
412 // chop the line in visual buffer |
|
413 |
|
414 TInt visualRemainingSpace = |
|
415 visualBuffer->Des().MaxLength() - visualIndex; |
|
416 |
|
417 const TText* nextLine = visualBuffer->Ptr(); |
|
418 nextLine += visualIndex; |
|
419 |
|
420 TPtr ptr( |
|
421 const_cast<TText*>( nextLine ), |
|
422 visualRemainingSpace, |
|
423 visualRemainingSpace ); |
|
424 |
|
425 ptr.Copy( iText.Left( iNextLineStart ) ); |
|
426 |
|
427 // we only wrap 1 line |
|
428 (*tempLineWidthArray)[0] = |
|
429 iLineWidthArray ? (*iLineWidthArray)[lineIndex] : iLineWidth; |
|
430 |
|
431 TAknTextWrapper wrapper( |
|
432 ptr, |
|
433 tempLineWidthArray, |
|
434 iFont, |
|
435 *tempResultArray, |
|
436 0, |
|
437 flags, |
|
438 iDirectionality ); |
|
439 |
|
440 wrapper.WrapToArrayL(); |
|
441 |
|
442 if ( !wrapper.ResultFits() ) |
|
443 { |
|
444 iFlags &= ~EFits; |
|
445 } |
|
446 |
|
447 TInt lineLength = 0; |
|
448 if ( tempResultArray->Count() ) |
|
449 { |
|
450 lineLength = (*tempResultArray)[0].Length(); |
|
451 } |
|
452 |
|
453 visualBuffer->Des().SetLength( visualIndex + lineLength ); |
|
454 |
|
455 if ( iFlags & EReserveVisualBuffer ) |
|
456 { |
|
457 AppendToArrayL( visualBuffer->Right( lineLength ) ); |
|
458 } |
|
459 else |
|
460 { |
|
461 // Cannot use AppendToArrayL here, because iResultArray is set to point to iSourceString, |
|
462 // which does not contain the final visual text yet. This would screw up removing trailing spaces. |
|
463 // It is copied there in the end of this method. |
|
464 |
|
465 TPtrC currentLine = visualBuffer->Right( lineLength ); |
|
466 |
|
467 TInt trailingSpaces = 0; |
|
468 TInt trailingIndex = currentLine.Length() - 1; |
|
469 |
|
470 // Count how many spaces there are in the end of the line. |
|
471 // Without removing them, text that is aligned to the end of line looks bad. |
|
472 while ( trailingIndex >= 0 && currentLine[trailingIndex] == ' ' ) |
|
473 { |
|
474 trailingIndex--; |
|
475 trailingSpaces++; |
|
476 } |
|
477 |
|
478 iResultArray.AppendL( iSourceString.Mid( visualIndex, lineLength - trailingSpaces ) ); |
|
479 } |
|
480 |
|
481 visualIndex += lineLength; |
|
482 |
|
483 PassLineBreak(); |
|
484 index += iNextLineStart; |
|
485 |
|
486 // next line to be wrapped |
|
487 lineIndex++; |
|
488 } |
|
489 |
|
490 CleanupStack::PopAndDestroy( 2 ); // tempLineWidthArray, tempResultArray |
|
491 |
|
492 if ( iFlags & EReserveVisualBuffer ) |
|
493 { |
|
494 CleanupStack::Pop(); |
|
495 return visualBuffer; |
|
496 } |
|
497 |
|
498 else |
|
499 { |
|
500 iSourceString = *visualBuffer; |
|
501 CleanupStack::PopAndDestroy(); // visualBuffer; |
|
502 return NULL; |
|
503 } |
|
504 } |
|
505 |
|
506 // ----------------------------------------------------------------------------- |
|
507 // TAknTextWrapper::InsertEllipsis |
|
508 // ----------------------------------------------------------------------------- |
|
509 // |
|
510 TInt TAknTextWrapper::InsertEllipsis( const TDesC& aText, TInt aLineWidth ) |
|
511 { |
|
512 // place ellipsis in the last possible place so that |
|
513 // the line still fits |
|
514 |
|
515 TInt count = iFont.TextCount( aText, |
|
516 aLineWidth - iFont.CharWidthInPixels( KEllipsis ) ); |
|
517 |
|
518 if ( !(iFlags & EConvertToVisual) ) |
|
519 { |
|
520 TText* text = (TText*)( aText.Ptr() ); |
|
521 text[ count ] = KEllipsis; |
|
522 return count + 1; |
|
523 } |
|
524 else |
|
525 { |
|
526 // ellipsis will be added in logical to visual conversion |
|
527 return count; |
|
528 } |
|
529 } |
|
530 |
|
531 // ----------------------------------------------------------------------------- |
|
532 // TAknTextWrapper::AppendToArrayL |
|
533 // ----------------------------------------------------------------------------- |
|
534 // |
|
535 void TAknTextWrapper::AppendToArrayL( const TDesC& aLine ) |
|
536 { |
|
537 const TText* line = aLine.Ptr(); |
|
538 TInt index = aLine.Length() - 1; |
|
539 |
|
540 // Remove spaces from the end of the line. |
|
541 // Otherwise, text that is aligned to the end of line looks bad. |
|
542 |
|
543 while ( index >= 0 && line[index] == ' ' ) |
|
544 { |
|
545 index--; |
|
546 } |
|
547 |
|
548 iResultArray.AppendL( aLine.Left( index + 1 ) ); |
|
549 } |
|
550 |
|
551 // ----------------------------------------------------------------------------- |
|
552 // TAknTextWrapper::FindLineBreak |
|
553 // ----------------------------------------------------------------------------- |
|
554 // |
|
555 TInt TAknTextWrapper::FindLineBreak( const TDesC& aText ) |
|
556 { |
|
557 const TText* text = aText.Ptr(); |
|
558 TInt length( aText.Length() ); |
|
559 |
|
560 for ( TInt i = 0 ; i < length ; i++ ) |
|
561 { |
|
562 if ( iSeparators.Locate( text[i] ) != KErrNotFound ) |
|
563 { |
|
564 return i; |
|
565 } |
|
566 } |
|
567 |
|
568 return KErrNotFound; |
|
569 } |
|
570 |
|
571 // ----------------------------------------------------------------------------- |
|
572 // TAknTextWrapper::GetWrappingPosition |
|
573 // ----------------------------------------------------------------------------- |
|
574 // |
|
575 TBool TAknTextWrapper::GetWrappingPosition( |
|
576 const TDesC& aText, |
|
577 TInt aFitsInLine, |
|
578 TInt aLineWidth, |
|
579 TBool aReplaceSoftHyphen ) |
|
580 { |
|
581 // If there is an explicit line break character right after the fitting |
|
582 // characters, then wrap there. |
|
583 |
|
584 if ( aText.Length() > aFitsInLine && |
|
585 iSeparators.Locate( aText[aFitsInLine] ) != KErrNotFound ) |
|
586 { |
|
587 iBreakPos = aFitsInLine; |
|
588 iNextLineStart = aFitsInLine; |
|
589 return ETrue; |
|
590 } |
|
591 |
|
592 TBool foundLineBreak( EFalse ); |
|
593 TText* text = const_cast<TText*>( iText.Ptr() ); |
|
594 |
|
595 while ( aFitsInLine > 0 ) |
|
596 { |
|
597 // use linebreak routine to find the last possible line break |
|
598 |
|
599 if ( !iBreaker.GetLineBreak( |
|
600 aText, |
|
601 1, |
|
602 aFitsInLine, |
|
603 EFalse, |
|
604 NULL, |
|
605 iBreakPos, |
|
606 iNextLineStart ) ) |
|
607 { |
|
608 // no legal wrapping positions found at all |
|
609 break; |
|
610 } |
|
611 |
|
612 ///////////////////////////////////////////////////////////// |
|
613 // Special rule for Thai wrapping: |
|
614 // Wrapping between two Thai characters is not done if the line |
|
615 // contains at least one zero-width-space or usual space. In this |
|
616 // situation the line is wrapped from the latest zero width space |
|
617 // or space. |
|
618 TInt newWrapPosition = -1; |
|
619 TInt charactersToCheck = iBreakPos - 1; |
|
620 TChar breakChar1 = text[charactersToCheck]; |
|
621 TChar breakChar2 = ' '; |
|
622 |
|
623 if (aText.Length() > aFitsInLine) |
|
624 { |
|
625 // The character after the usual line break needs to be Thai character as well. |
|
626 charactersToCheck += 1; |
|
627 breakChar2 = text[charactersToCheck]; |
|
628 } |
|
629 |
|
630 // Did the line break were made between two Thai characters. |
|
631 if ( ( breakChar1 >= KFirstThaiCharacter && breakChar1 <= KLastThaiCharacter ) && |
|
632 ( breakChar2 >= KFirstThaiCharacter && breakChar2 <= KLastThaiCharacter ) ) |
|
633 { |
|
634 for (TInt ii = charactersToCheck; ii >= 0; ii--) |
|
635 { |
|
636 const TChar charTemp = text[ii]; |
|
637 if ( (charTemp == ' ' || charTemp == KZeroWidthSpace) ) |
|
638 { |
|
639 // Space or zero-width-space found. We use it for wrapping. |
|
640 newWrapPosition = ii; |
|
641 break; |
|
642 } |
|
643 } |
|
644 } |
|
645 |
|
646 if ( newWrapPosition >= 0 ) |
|
647 { |
|
648 // The special wrapping rule is used. |
|
649 iBreakPos = newWrapPosition; |
|
650 iNextLineStart = iBreakPos + 1; |
|
651 foundLineBreak = ETrue; |
|
652 } |
|
653 ///////////////////////////////////////////////////////////// |
|
654 |
|
655 |
|
656 // Wrapping after hyphen (or soft hyphen) is allowed only |
|
657 // if there is no space right before the hyphen. |
|
658 TInt lastCharIndex( iBreakPos - 1 ); |
|
659 const TText lastChar = text[lastCharIndex]; |
|
660 |
|
661 if ( lastChar == '-' || |
|
662 lastChar == KHyphen || |
|
663 lastChar == KSoftHyphen ) |
|
664 { |
|
665 if ( lastCharIndex > 0 && text[lastCharIndex - 1] == ' ' ) |
|
666 { |
|
667 aFitsInLine = lastCharIndex; |
|
668 continue; |
|
669 } |
|
670 } |
|
671 |
|
672 if ( lastChar != KSoftHyphen ) |
|
673 { |
|
674 foundLineBreak = ETrue; |
|
675 break; |
|
676 } |
|
677 |
|
678 // If the chosen wrapping char was soft hyphen, |
|
679 // try replace it with real hyphen. |
|
680 |
|
681 text[lastCharIndex] = KHyphen; |
|
682 |
|
683 // still fits in line? |
|
684 if ( iFont.TextWidthInPixels( |
|
685 iText.Left( lastCharIndex + 1 ) ) <= aLineWidth ) |
|
686 { |
|
687 foundLineBreak = ETrue; |
|
688 |
|
689 if ( !aReplaceSoftHyphen ) |
|
690 { |
|
691 text[lastCharIndex] = KSoftHyphen; |
|
692 } |
|
693 |
|
694 break; |
|
695 } |
|
696 |
|
697 else |
|
698 { |
|
699 // This soft hyphen could not be used, because |
|
700 // expanding it to real hyphen would have made |
|
701 // the line too long to fit. |
|
702 |
|
703 text[lastCharIndex] = KSoftHyphen; |
|
704 aFitsInLine = lastCharIndex; |
|
705 } |
|
706 } |
|
707 |
|
708 return foundLineBreak; |
|
709 } |
|
710 |
|
711 // ----------------------------------------------------------------------------- |
|
712 // TAknTextWrapper::ConvertToVisualIfRequiredL |
|
713 // ----------------------------------------------------------------------------- |
|
714 // |
|
715 HBufC* TAknTextWrapper::ConvertToVisualIfRequiredL() |
|
716 { |
|
717 if ( !(iFlags & EConvertToVisual) ) |
|
718 { |
|
719 return NULL; |
|
720 } |
|
721 |
|
722 TInt lines = iResultArray.Count(); |
|
723 |
|
724 HBufC* visualBuffer = HBufC::NewLC( |
|
725 iSourceString.Length() + lines * KAknBidiExtraSpacePerLine ); |
|
726 |
|
727 CAknEnv& env = *CAknEnv::Static(); |
|
728 TChar truncationChar = 0xffff; |
|
729 |
|
730 TInt sourceStringLength = iSourceString.Length(); |
|
731 |
|
732 // initialize run info array |
|
733 User::LeaveIfError( env.PrepareRunInfoArray( iSourceString ) ); |
|
734 |
|
735 // get run info array |
|
736 TInt count; |
|
737 TBidirectionalState::TRunInfo* array = env.RunInfoArray( count ); |
|
738 |
|
739 // convert from logical to visual |
|
740 TBidiLogicalToVisual converter( |
|
741 iDirectionality == AknBidiTextUtils::EImplicit ? |
|
742 TBidiLogicalToVisual( |
|
743 iSourceString, |
|
744 array, |
|
745 count ) : |
|
746 TBidiLogicalToVisual( |
|
747 iSourceString, |
|
748 iDirectionality == AknBidiTextUtils::ERightToLeft, |
|
749 array, |
|
750 count ) ); |
|
751 |
|
752 converter.Reorder(); |
|
753 |
|
754 // Convert each line from logical to visual form, |
|
755 // and update array of wrapped lines accordingly. |
|
756 |
|
757 for ( TInt i = 0 ; i < lines ; i++ ) |
|
758 { |
|
759 TPtrC logicalLine = iResultArray[i]; |
|
760 |
|
761 TInt start = (TText*)logicalLine.Ptr() - (TText*)iSourceString.Ptr(); |
|
762 TInt length = logicalLine.Length(); |
|
763 |
|
764 TInt currentLength = visualBuffer->Length(); |
|
765 TInt visualLineLength = visualBuffer->Des().MaxLength() - currentLength; |
|
766 |
|
767 TPtr visualLine( |
|
768 ((TText*)visualBuffer->Ptr()) + currentLength, |
|
769 0, |
|
770 visualLineLength ); |
|
771 |
|
772 // if last line, set clip char if required |
|
773 if ( i == lines - 1 && iFlags & EClipRequired ) |
|
774 { |
|
775 truncationChar = KEllipsis; |
|
776 } |
|
777 |
|
778 converter.GetVisualLine( |
|
779 visualLine, |
|
780 start, |
|
781 start + length, |
|
782 truncationChar ); |
|
783 |
|
784 visualBuffer->Des().SetLength( |
|
785 visualBuffer->Length() + visualLine.Length() ); |
|
786 |
|
787 // update array of wrapped lines accordingly. |
|
788 |
|
789 iResultArray.Delete( i ); |
|
790 |
|
791 if ( iFlags & EReserveVisualBuffer ) |
|
792 { |
|
793 iResultArray.InsertL( |
|
794 i, visualBuffer->Right( visualLine.Length() ) ); |
|
795 } |
|
796 else |
|
797 { |
|
798 iSourceString.SetMax(); |
|
799 iResultArray.InsertL( |
|
800 i, iSourceString.Mid( currentLength, visualLine.Length() ) ); |
|
801 iSourceString.SetLength( sourceStringLength ); |
|
802 } |
|
803 } |
|
804 |
|
805 if ( iFlags & EReserveVisualBuffer ) |
|
806 { |
|
807 CleanupStack::Pop(); |
|
808 return visualBuffer; |
|
809 } |
|
810 |
|
811 else |
|
812 { |
|
813 iSourceString = *visualBuffer; |
|
814 CleanupStack::PopAndDestroy(); // iVisualBuffer; |
|
815 return NULL; |
|
816 } |
|
817 } |
|
818 |
|
819 // ----------------------------------------------------------------------------- |
|
820 // TAknTextWrapper::PassLineBreak |
|
821 // ----------------------------------------------------------------------------- |
|
822 // |
|
823 void TAknTextWrapper::PassLineBreak() |
|
824 { |
|
825 TText* text = const_cast<TText*>( iText.Ptr() ); |
|
826 TInt length = iText.Length(); |
|
827 |
|
828 if ( iNextLineStart < length ) |
|
829 { |
|
830 const TText c = text[iNextLineStart]; |
|
831 |
|
832 if ( c == KLineFeed || |
|
833 c == KLineSeparator || |
|
834 c == KParagraphSeparator ) |
|
835 { |
|
836 iNextLineStart++; |
|
837 } |
|
838 else if ( c == KCarriageReturn ) |
|
839 { |
|
840 iNextLineStart++; |
|
841 // after CR, also skip possible matching LF |
|
842 if ( iNextLineStart < length && text[iNextLineStart] == KLineFeed ) |
|
843 { |
|
844 iNextLineStart++; |
|
845 } |
|
846 } |
|
847 } |
|
848 } |
|
849 |
|
850 // ----------------------------------------------------------------------------- |
|
851 // TAknTextWrapper::ResultFits |
|
852 // ----------------------------------------------------------------------------- |
|
853 // |
|
854 TBool TAknTextWrapper::ResultFits() |
|
855 { |
|
856 return iFlags & EFits; |
|
857 } |
|
858 |
|
859 // ----------------------------------------------------------------------------- |
|
860 // TAknTextWrapper::GetLocVariants |
|
861 // ----------------------------------------------------------------------------- |
|
862 // |
|
863 TInt TAknTextWrapper::GetLocVariants( |
|
864 const TDesC& aText, TAknLocVariant* aVariants ) |
|
865 { |
|
866 TPtrC remaining( aText ); |
|
867 |
|
868 TInt start = 0; |
|
869 TInt end = remaining.Locate( KAknLocVariantSeparator ); |
|
870 |
|
871 TInt i = 0; |
|
872 |
|
873 while ( end >= 0 && i < KAknMaxLocVariants ) |
|
874 { |
|
875 end += start; |
|
876 aVariants[i].iStart = start; |
|
877 aVariants[i].iEnd = end; |
|
878 |
|
879 start = end + 1; |
|
880 remaining.Set( aText.Mid( start ) ); |
|
881 end = remaining.Locate( KAknLocVariantSeparator ); |
|
882 |
|
883 i++; |
|
884 } |
|
885 |
|
886 if ( i < KAknMaxLocVariants ) |
|
887 { |
|
888 // Handle the last variant. |
|
889 aVariants[i].iStart = start; |
|
890 aVariants[i].iEnd = aText.Length(); |
|
891 i++; |
|
892 } |
|
893 |
|
894 // Put the variants in descending character count order. |
|
895 if ( i > 1 ) |
|
896 { |
|
897 TMyKey key; |
|
898 key.SetPtr( aVariants ); |
|
899 |
|
900 TMySwap swap( aVariants ); |
|
901 |
|
902 // Return value ignored. |
|
903 User::QuickSort( i, key, swap ); |
|
904 } |
|
905 |
|
906 return i; |
|
907 } |
|
908 |
|
909 // End of File |