LCOV - code coverage report
Current view: top level - vcl/source/window - mnemonic.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 112 155 72.3 %
Date: 2012-08-25 Functions: 7 7 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 103 250 41.2 %

           Branch data     Line data    Source code
       1                 :            : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 :            : /*************************************************************************
       3                 :            :  *
       4                 :            :  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       5                 :            :  *
       6                 :            :  * Copyright 2000, 2010 Oracle and/or its affiliates.
       7                 :            :  *
       8                 :            :  * OpenOffice.org - a multi-platform office productivity suite
       9                 :            :  *
      10                 :            :  * This file is part of OpenOffice.org.
      11                 :            :  *
      12                 :            :  * OpenOffice.org is free software: you can redistribute it and/or modify
      13                 :            :  * it under the terms of the GNU Lesser General Public License version 3
      14                 :            :  * only, as published by the Free Software Foundation.
      15                 :            :  *
      16                 :            :  * OpenOffice.org is distributed in the hope that it will be useful,
      17                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19                 :            :  * GNU Lesser General Public License version 3 for more details
      20                 :            :  * (a copy is included in the LICENSE file that accompanied this code).
      21                 :            :  *
      22                 :            :  * You should have received a copy of the GNU Lesser General Public License
      23                 :            :  * version 3 along with OpenOffice.org.  If not, see
      24                 :            :  * <http://www.openoffice.org/license.html>
      25                 :            :  * for a copy of the LGPLv3 License.
      26                 :            :  *
      27                 :            :  ************************************************************************/
      28                 :            : 
      29                 :            : 
      30                 :            : #include <string.h>
      31                 :            : #include <vcl/svapp.hxx>
      32                 :            : #include <vcl/settings.hxx>
      33                 :            : #include <vcl/mnemonic.hxx>
      34                 :            : 
      35                 :            : #include <vcl/unohelp.hxx>
      36                 :            : #include <com/sun/star/i18n/XCharacterClassification.hpp>
      37                 :            : #include <i18npool/mslangid.hxx>
      38                 :            : 
      39                 :            : using namespace ::com::sun::star;
      40                 :            : 
      41                 :            : 
      42                 :            : // =======================================================================
      43                 :            : 
      44                 :         22 : MnemonicGenerator::MnemonicGenerator()
      45                 :            : {
      46                 :         22 :     memset( maMnemonics, 1, sizeof( maMnemonics ) );
      47                 :         22 : }
      48                 :            : 
      49                 :            : // -----------------------------------------------------------------------
      50                 :            : 
      51                 :       1098 : sal_uInt16 MnemonicGenerator::ImplGetMnemonicIndex( sal_Unicode c )
      52                 :            : {
      53                 :            :     static sal_uInt16 const aImplMnemonicRangeTab[MNEMONIC_RANGES*2] =
      54                 :            :     {
      55                 :            :         MNEMONIC_RANGE_1_START, MNEMONIC_RANGE_1_END,
      56                 :            :         MNEMONIC_RANGE_2_START, MNEMONIC_RANGE_2_END,
      57                 :            :         MNEMONIC_RANGE_3_START, MNEMONIC_RANGE_3_END,
      58                 :            :         MNEMONIC_RANGE_4_START, MNEMONIC_RANGE_4_END
      59                 :            :     };
      60                 :            : 
      61                 :       1098 :     sal_uInt16 nMnemonicIndex = 0;
      62         [ +  + ]:       2376 :     for ( sal_uInt16 i = 0; i < MNEMONIC_RANGES; i++ )
      63                 :            :     {
      64 [ +  + ][ +  + ]:       2316 :         if ( (c >= aImplMnemonicRangeTab[i*2]) &&
      65                 :       2076 :              (c <= aImplMnemonicRangeTab[i*2+1]) )
      66                 :       1038 :             return nMnemonicIndex+c-aImplMnemonicRangeTab[i*2];
      67                 :            : 
      68                 :       1278 :         nMnemonicIndex += aImplMnemonicRangeTab[i*2+1]-aImplMnemonicRangeTab[i*2];
      69                 :            :     }
      70                 :            : 
      71                 :       1098 :     return MNEMONIC_INDEX_NOTFOUND;
      72                 :            : }
      73                 :            : 
      74                 :            : // -----------------------------------------------------------------------
      75                 :            : 
      76                 :        834 : sal_Unicode MnemonicGenerator::ImplFindMnemonic( const XubString& rKey )
      77                 :            : {
      78                 :        834 :     xub_StrLen nIndex = 0;
      79         [ +  + ]:        834 :     while ( (nIndex = rKey.Search( MNEMONIC_CHAR, nIndex )) != STRING_NOTFOUND )
      80                 :            :     {
      81                 :        672 :         sal_Unicode cMnemonic = rKey.GetChar( nIndex+1 );
      82         [ +  - ]:        672 :         if ( cMnemonic != MNEMONIC_CHAR )
      83                 :        672 :             return cMnemonic;
      84                 :          0 :         nIndex += 2;
      85                 :            :     }
      86                 :            : 
      87                 :        834 :     return 0;
      88                 :            : }
      89                 :            : 
      90                 :            : // -----------------------------------------------------------------------
      91                 :            : 
      92                 :        448 : void MnemonicGenerator::RegisterMnemonic( const XubString& rKey )
      93                 :            : {
      94 [ +  - ][ +  - ]:        448 :     const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetUILocale();
      95         [ +  - ]:        448 :     uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass();
      96                 :            : 
      97                 :            :     // Don't crash even when we don't have access to i18n service
      98         [ -  + ]:        448 :     if ( !xCharClass.is() )
      99                 :        448 :         return;
     100                 :            : 
     101 [ +  - ][ +  - ]:        448 :     XubString aKey = xCharClass->toUpper( rKey, 0, rKey.Len(), rLocale );
         [ +  - ][ +  - ]
     102                 :            : 
     103                 :            :     // If we find a Mnemonic, set the flag. In other case count the
     104                 :            :     // characters, because we need this to set most as possible
     105                 :            :     // Mnemonics
     106         [ +  - ]:        448 :     sal_Unicode cMnemonic = ImplFindMnemonic( aKey );
     107         [ +  + ]:        448 :     if ( cMnemonic )
     108                 :            :     {
     109                 :        342 :         sal_uInt16 nMnemonicIndex = ImplGetMnemonicIndex( cMnemonic );
     110         [ +  + ]:        342 :         if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
     111                 :        332 :             maMnemonics[nMnemonicIndex] = 0;
     112                 :            :     }
     113                 :            :     else
     114                 :            :     {
     115                 :        106 :         xub_StrLen nIndex = 0;
     116                 :        106 :         xub_StrLen nLen = aKey.Len();
     117         [ +  + ]:        438 :         while ( nIndex < nLen )
     118                 :            :         {
     119                 :        332 :             sal_Unicode c = aKey.GetChar( nIndex );
     120                 :            : 
     121                 :        332 :             sal_uInt16 nMnemonicIndex = ImplGetMnemonicIndex( c );
     122         [ +  + ]:        332 :             if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
     123                 :            :             {
     124 [ +  + ][ +  - ]:        312 :                 if ( maMnemonics[nMnemonicIndex] && (maMnemonics[nMnemonicIndex] < 0xFF) )
     125                 :        138 :                     maMnemonics[nMnemonicIndex]++;
     126                 :            :             }
     127                 :            : 
     128                 :        332 :             nIndex++;
     129                 :            :         }
     130 [ +  - ][ +  - ]:        448 :     }
     131                 :            : }
     132                 :            : 
     133                 :            : // -----------------------------------------------------------------------
     134                 :            : 
     135                 :        450 : sal_Bool MnemonicGenerator::CreateMnemonic( XubString& rKey )
     136                 :            : {
     137 [ +  + ][ +  - ]:        450 :     if ( !rKey.Len() || ImplFindMnemonic( rKey ) )
         [ +  + ][ +  + ]
     138                 :        394 :         return sal_False;
     139                 :            : 
     140 [ +  - ][ +  - ]:         56 :     const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetUILocale();
     141         [ +  - ]:         56 :     uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass();
     142                 :            : 
     143                 :            :     // Don't crash even when we don't have access to i18n service
     144         [ -  + ]:         56 :     if ( !xCharClass.is() )
     145                 :          0 :         return sal_False;
     146                 :            : 
     147 [ +  - ][ +  - ]:         56 :     XubString aKey = xCharClass->toUpper( rKey, 0, rKey.Len(), rLocale );
         [ +  - ][ +  - ]
     148                 :            : 
     149                 :         56 :     sal_Bool bChanged = sal_False;
     150                 :         56 :     xub_StrLen nLen = aKey.Len();
     151                 :            : 
     152 [ +  - ][ +  - ]:         56 :     bool bCJK = MsLangId::isCJK(Application::GetSettings().GetUILanguage());
                 [ +  - ]
     153                 :            : 
     154                 :            :     // #107889# in CJK versions ALL strings (even those that contain latin characters)
     155                 :            :     // will get mnemonics in the form: xyz (M)
     156                 :            :     // thus steps 1) and 2) are skipped for CJK locales
     157                 :            : 
     158                 :            :     // #110720#, avoid CJK-style mnemonics for latin-only strings that do not contain useful mnemonic chars
     159         [ -  + ]:         56 :     if( bCJK )
     160                 :            :     {
     161                 :          0 :         sal_Bool bLatinOnly = sal_True;
     162                 :          0 :         sal_Bool bMnemonicIndexFound = sal_False;
     163                 :            :         sal_Unicode     c;
     164                 :            :         xub_StrLen      nIndex;
     165                 :            : 
     166         [ #  # ]:          0 :         for( nIndex=0; nIndex < nLen; nIndex++ )
     167                 :            :         {
     168                 :          0 :             c = aKey.GetChar( nIndex );
     169 [ #  # ][ #  # ]:          0 :             if ( ((c >= 0x3000) && (c <= 0xD7FF)) ||    // cjk
         [ #  # ][ #  # ]
     170                 :            :                  ((c >= 0xFF61) && (c <= 0xFFDC)) )     // halfwidth forms
     171                 :            :             {
     172                 :          0 :                 bLatinOnly = sal_False;
     173                 :          0 :                 break;
     174                 :            :             }
     175         [ #  # ]:          0 :             if( ImplGetMnemonicIndex( c ) != MNEMONIC_INDEX_NOTFOUND )
     176                 :          0 :                 bMnemonicIndexFound = sal_True;
     177                 :            :         }
     178 [ #  # ][ #  # ]:          0 :         if( bLatinOnly && !bMnemonicIndexFound )
     179                 :          0 :             return sal_False;
     180                 :            :     }
     181                 :            : 
     182                 :            : 
     183                 :         56 :     int             nCJK = 0;
     184                 :            :     sal_uInt16          nMnemonicIndex;
     185                 :            :     sal_Unicode     c;
     186                 :         56 :     xub_StrLen      nIndex = 0;
     187         [ +  - ]:         56 :     if( !bCJK )
     188                 :            :     {
     189                 :            :         // 1) first try the first character of a word
     190         [ +  + ]:         56 :         do
     191                 :            :         {
     192                 :         68 :             c = aKey.GetChar( nIndex );
     193                 :            : 
     194         [ +  + ]:         68 :             if ( nCJK != 2 )
     195                 :            :             {
     196 [ -  + ][ #  # ]:         56 :                 if ( ((c >= 0x3000) && (c <= 0xD7FF)) ||    // cjk
         [ -  + ][ #  # ]
     197                 :            :                     ((c >= 0xFF61) && (c <= 0xFFDC)) )     // halfwidth forms
     198                 :          0 :                     nCJK = 1;
     199 [ +  - ][ +  - ]:         56 :                 else if ( ((c >= 0x0030) && (c <= 0x0039)) || // digits
         [ +  - ][ -  + ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     200                 :            :                         ((c >= 0x0041) && (c <= 0x005A)) || // latin capitals
     201                 :            :                         ((c >= 0x0061) && (c <= 0x007A)) || // latin small
     202                 :            :                         ((c >= 0x0370) && (c <= 0x037F)) || // greek numeral signs
     203                 :            :                         ((c >= 0x0400) && (c <= 0x04FF)) )  // cyrillic
     204                 :         56 :                     nCJK = 2;
     205                 :            :             }
     206                 :            : 
     207                 :         68 :             nMnemonicIndex = ImplGetMnemonicIndex( c );
     208         [ +  - ]:         68 :             if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
     209                 :            :             {
     210         [ +  + ]:         68 :                 if ( maMnemonics[nMnemonicIndex] )
     211                 :            :                 {
     212                 :         12 :                     maMnemonics[nMnemonicIndex] = 0;
     213         [ +  - ]:         12 :                     rKey.Insert( MNEMONIC_CHAR, nIndex );
     214                 :         12 :                     bChanged = sal_True;
     215                 :         12 :                     break;
     216                 :            :                 }
     217                 :            :             }
     218                 :            : 
     219                 :            :             // Search for next word
     220         [ +  + ]:        352 :             do
     221                 :            :             {
     222                 :        364 :                 nIndex++;
     223                 :        364 :                 c = aKey.GetChar( nIndex );
     224         [ +  + ]:        364 :                 if ( c == ' ' )
     225                 :         12 :                     break;
     226                 :            :             }
     227                 :            :             while ( nIndex < nLen );
     228                 :         56 :             nIndex++;
     229                 :            :         }
     230                 :            :         while ( nIndex < nLen );
     231                 :            : 
     232                 :            :         // 2) search for a unique/uncommon character
     233         [ +  + ]:         56 :         if ( !bChanged )
     234                 :            :         {
     235                 :         44 :             sal_uInt16      nBestCount = 0xFFFF;
     236                 :         44 :             sal_uInt16      nBestMnemonicIndex = 0;
     237                 :         44 :             xub_StrLen  nBestIndex = 0;
     238                 :         44 :             nIndex = 0;
     239         [ +  + ]:        354 :             do
     240                 :            :             {
     241                 :        356 :                 c = aKey.GetChar( nIndex );
     242                 :        356 :                 nMnemonicIndex = ImplGetMnemonicIndex( c );
     243         [ +  + ]:        356 :                 if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
     244                 :            :                 {
     245         [ +  + ]:        326 :                     if ( maMnemonics[nMnemonicIndex] )
     246                 :            :                     {
     247         [ +  + ]:         74 :                         if ( maMnemonics[nMnemonicIndex] < nBestCount )
     248                 :            :                         {
     249                 :         34 :                             nBestCount = maMnemonics[nMnemonicIndex];
     250                 :         34 :                             nBestIndex = nIndex;
     251                 :         34 :                             nBestMnemonicIndex = nMnemonicIndex;
     252         [ +  + ]:         34 :                             if ( nBestCount == 2 )
     253                 :          2 :                                 break;
     254                 :            :                         }
     255                 :            :                     }
     256                 :            :                 }
     257                 :            : 
     258                 :        354 :                 nIndex++;
     259                 :            :             }
     260                 :            :             while ( nIndex < nLen );
     261                 :            : 
     262         [ +  + ]:         44 :             if ( nBestCount != 0xFFFF )
     263                 :            :             {
     264                 :         24 :                 maMnemonics[nBestMnemonicIndex] = 0;
     265         [ +  - ]:         24 :                 rKey.Insert( MNEMONIC_CHAR, nBestIndex );
     266                 :         24 :                 bChanged = sal_True;
     267                 :            :             }
     268                 :            :         }
     269                 :            :     }
     270                 :            :     else
     271                 :          0 :         nCJK = 1;
     272                 :            : 
     273                 :            :     // 3) Add English Mnemonic for CJK Text
     274 [ +  + ][ -  + ]:         56 :     if ( !bChanged && (nCJK == 1) && rKey.Len() )
         [ #  # ][ -  + ]
     275                 :            :     {
     276                 :            :         // Append Ascii Mnemonic
     277         [ #  # ]:          0 :         for ( c = MNEMONIC_RANGE_2_START; c <= MNEMONIC_RANGE_2_END; c++ )
     278                 :            :         {
     279                 :          0 :             nMnemonicIndex = ImplGetMnemonicIndex( c );
     280         [ #  # ]:          0 :             if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
     281                 :            :             {
     282         [ #  # ]:          0 :                 if ( maMnemonics[nMnemonicIndex] )
     283                 :            :                 {
     284                 :          0 :                     maMnemonics[nMnemonicIndex] = 0;
     285                 :            :                     rtl::OUString aStr = rtl::OUStringBuffer().
     286 [ #  # ][ #  # ]:          0 :                         append('(').append(MNEMONIC_CHAR).append(c).
                 [ #  # ]
     287 [ #  # ][ #  # ]:          0 :                         append(')').makeStringAndClear();
     288                 :          0 :                     nIndex = rKey.Len();
     289         [ #  # ]:          0 :                     if( nIndex >= 2 )
     290                 :            :                     {
     291                 :            :                         static sal_Unicode cGreaterGreater[] = { 0xFF1E, 0xFF1E };
     292 [ #  # ][ #  # ]:          0 :                         if ( rKey.EqualsAscii( ">>", nIndex-2, 2 ) ||
         [ #  # ][ #  # ]
     293         [ #  # ]:          0 :                             rKey.Equals( cGreaterGreater, nIndex-2, 2 ) )
     294                 :          0 :                             nIndex -= 2;
     295                 :            :                     }
     296         [ #  # ]:          0 :                     if( nIndex >= 3 )
     297                 :            :                     {
     298                 :            :                         static sal_Unicode cDotDotDot[] = { 0xFF0E, 0xFF0E, 0xFF0E };
     299 [ #  # ][ #  # ]:          0 :                         if ( rKey.EqualsAscii( "...", nIndex-3, 3 ) ||
         [ #  # ][ #  # ]
     300         [ #  # ]:          0 :                             rKey.Equals( cDotDotDot, nIndex-3, 3 ) )
     301                 :          0 :                             nIndex -= 3;
     302                 :            :                     }
     303         [ #  # ]:          0 :                     if( nIndex >= 1)
     304                 :            :                     {
     305                 :          0 :                         sal_Unicode cLastChar = rKey.GetChar( nIndex-1 );
     306 [ #  # ][ #  # ]:          0 :                         if ( (cLastChar == ':') || (cLastChar == 0xFF1A) ||
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     307                 :            :                             (cLastChar == '.') || (cLastChar == 0xFF0E) ||
     308                 :            :                             (cLastChar == '?') || (cLastChar == 0xFF1F) ||
     309                 :            :                             (cLastChar == ' ') )
     310                 :          0 :                             nIndex--;
     311                 :            :                     }
     312 [ #  # ][ #  # ]:          0 :                     rKey.Insert( aStr, nIndex );
                 [ #  # ]
     313                 :          0 :                     bChanged = sal_True;
     314                 :          0 :                     break;
     315                 :            :                 }
     316                 :            :             }
     317                 :            :         }
     318                 :            :     }
     319                 :            : 
     320                 :            : // #i87415# Duplicates mnemonics are bad for consistent keyboard accessibility
     321                 :            : // It's probably better to not have mnemonics for some widgets, than to have ambiguous ones.
     322                 :            : //    if( ! bChanged )
     323                 :            : //    {
     324                 :            : //        /*
     325                 :            : //         *  #97809# if all else fails use the first character of a word
     326                 :            : //         *  anyway and live with duplicate mnemonics
     327                 :            : //         */
     328                 :            : //        nIndex = 0;
     329                 :            : //        do
     330                 :            : //        {
     331                 :            : //            c = aKey.GetChar( nIndex );
     332                 :            : //
     333                 :            : //            nMnemonicIndex = ImplGetMnemonicIndex( c );
     334                 :            : //            if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
     335                 :            : //            {
     336                 :            : //                maMnemonics[nMnemonicIndex] = 0;
     337                 :            : //                rKey.Insert( MNEMONIC_CHAR, nIndex );
     338                 :            : //                bChanged = sal_True;
     339                 :            : //                break;
     340                 :            : //            }
     341                 :            : //
     342                 :            : //            // Search for next word
     343                 :            : //            do
     344                 :            : //            {
     345                 :            : //                nIndex++;
     346                 :            : //                c = aKey.GetChar( nIndex );
     347                 :            : //                if ( c == ' ' )
     348                 :            : //                    break;
     349                 :            : //            }
     350                 :            : //            while ( nIndex < nLen );
     351                 :            : //            nIndex++;
     352                 :            : //        }
     353                 :            : //        while ( nIndex < nLen );
     354                 :            : //    }
     355                 :            : 
     356         [ +  - ]:        450 :     return bChanged;
     357                 :            : }
     358                 :            : 
     359                 :            : // -----------------------------------------------------------------------
     360                 :            : 
     361                 :        504 : uno::Reference< i18n::XCharacterClassification > MnemonicGenerator::GetCharClass()
     362                 :            : {
     363         [ +  + ]:        504 :     if ( !mxCharClass.is() )
     364         [ +  - ]:         22 :         mxCharClass = vcl::unohelper::CreateCharacterClassification();
     365                 :        504 :     return mxCharClass;
     366                 :            : }
     367                 :            : 
     368                 :            : // -----------------------------------------------------------------------
     369                 :            : 
     370                 :     171223 : String MnemonicGenerator::EraseAllMnemonicChars( const String& rStr )
     371                 :            : {
     372                 :     171223 :     String      aStr = rStr;
     373                 :     171223 :     xub_StrLen  nLen = aStr.Len();
     374                 :     171223 :     xub_StrLen  i    = 0;
     375                 :            : 
     376         [ +  + ]:    2246148 :     while ( i < nLen )
     377                 :            :     {
     378         [ +  + ]:    2074925 :         if ( aStr.GetChar( i ) == '~' )
     379                 :            :         {
     380                 :            :             // check for CJK-style mnemonic
     381 [ +  + ][ +  + ]:       5266 :             if( i > 0 && (i+2) < nLen )
     382                 :            :             {
     383                 :       2879 :                 sal_Unicode c = aStr.GetChar(i+1);
     384 [ #  # ][ #  # ]:       2879 :                 if( aStr.GetChar( i-1 ) == '(' &&
                 [ -  + ]
           [ -  +  #  # ]
     385                 :          0 :                     aStr.GetChar( i+2 ) == ')' &&
     386                 :            :                     c >= MNEMONIC_RANGE_2_START && c <= MNEMONIC_RANGE_2_END )
     387                 :            :                 {
     388         [ #  # ]:          0 :                     aStr.Erase( i-1, 4 );
     389                 :          0 :                     nLen -= 4;
     390                 :          0 :                     i--;
     391                 :          0 :                     continue;
     392                 :            :                 }
     393                 :            :             }
     394                 :            : 
     395                 :            :             // remove standard mnemonics
     396         [ +  - ]:       5266 :             aStr.Erase( i, 1 );
     397                 :       5266 :             nLen--;
     398                 :            :         }
     399                 :            :         else
     400                 :    2069659 :             i++;
     401                 :            :     }
     402                 :            : 
     403                 :     171223 :     return aStr;
     404                 :            : }
     405                 :            : 
     406                 :            : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10