uifw/AvKon/src/AknTextWrapper.cpp
changeset 0 2f259fa3e83a
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:  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