merge the formfield patch from ooo-build
[ooovba.git] / vcl / source / window / mnemonic.cxx
blob44f08952941ff89c136e6c2c9121478f00d2c6fa
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: mnemonic.cxx,v $
10 * $Revision: 1.22 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include <string.h>
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
37 #include <vcl/mnemonic.hxx>
39 #include <vcl/unohelp.hxx>
40 #include <com/sun/star/i18n/XCharacterClassification.hpp>
42 using namespace ::com::sun::star;
45 // =======================================================================
47 MnemonicGenerator::MnemonicGenerator()
49 memset( maMnemonics, 1, sizeof( maMnemonics ) );
52 // -----------------------------------------------------------------------
54 USHORT MnemonicGenerator::ImplGetMnemonicIndex( sal_Unicode c )
56 static USHORT const aImplMnemonicRangeTab[MNEMONIC_RANGES*2] =
58 MNEMONIC_RANGE_1_START, MNEMONIC_RANGE_1_END,
59 MNEMONIC_RANGE_2_START, MNEMONIC_RANGE_2_END,
60 MNEMONIC_RANGE_3_START, MNEMONIC_RANGE_3_END,
61 MNEMONIC_RANGE_4_START, MNEMONIC_RANGE_4_END
64 USHORT nMnemonicIndex = 0;
65 for ( USHORT i = 0; i < MNEMONIC_RANGES; i++ )
67 if ( (c >= aImplMnemonicRangeTab[i*2]) &&
68 (c <= aImplMnemonicRangeTab[i*2+1]) )
69 return nMnemonicIndex+c-aImplMnemonicRangeTab[i*2];
71 nMnemonicIndex += aImplMnemonicRangeTab[i*2+1]-aImplMnemonicRangeTab[i*2];
74 return MNEMONIC_INDEX_NOTFOUND;
77 // -----------------------------------------------------------------------
79 sal_Unicode MnemonicGenerator::ImplFindMnemonic( const XubString& rKey )
81 xub_StrLen nIndex = 0;
82 while ( (nIndex = rKey.Search( MNEMONIC_CHAR, nIndex )) != STRING_NOTFOUND )
84 sal_Unicode cMnemonic = rKey.GetChar( nIndex+1 );
85 if ( cMnemonic != MNEMONIC_CHAR )
86 return cMnemonic;
87 nIndex += 2;
90 return 0;
93 // -----------------------------------------------------------------------
95 void MnemonicGenerator::RegisterMnemonic( const XubString& rKey )
97 const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetUILocale();
98 uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass();
100 // Don't crash even when we don't have access to i18n service
101 if ( !xCharClass.is() )
102 return;
104 XubString aKey = xCharClass->toUpper( rKey, 0, rKey.Len(), rLocale );
106 // If we find a Mnemonic, set the flag. In other case count the
107 // characters, because we need this to set most as possible
108 // Mnemonics
109 sal_Unicode cMnemonic = ImplFindMnemonic( aKey );
110 if ( cMnemonic )
112 USHORT nMnemonicIndex = ImplGetMnemonicIndex( cMnemonic );
113 if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
114 maMnemonics[nMnemonicIndex] = 0;
116 else
118 xub_StrLen nIndex = 0;
119 xub_StrLen nLen = aKey.Len();
120 while ( nIndex < nLen )
122 sal_Unicode c = aKey.GetChar( nIndex );
124 USHORT nMnemonicIndex = ImplGetMnemonicIndex( c );
125 if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
127 if ( maMnemonics[nMnemonicIndex] && (maMnemonics[nMnemonicIndex] < 0xFF) )
128 maMnemonics[nMnemonicIndex]++;
131 nIndex++;
136 // -----------------------------------------------------------------------
138 BOOL MnemonicGenerator::CreateMnemonic( XubString& rKey )
140 if ( !rKey.Len() || ImplFindMnemonic( rKey ) )
141 return FALSE;
143 const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetUILocale();
144 uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass();
146 // Don't crash even when we don't have access to i18n service
147 if ( !xCharClass.is() )
148 return FALSE;
150 XubString aKey = xCharClass->toUpper( rKey, 0, rKey.Len(), rLocale );
152 BOOL bChanged = FALSE;
153 xub_StrLen nLen = aKey.Len();
155 BOOL bCJK = FALSE;
156 switch( Application::GetSettings().GetUILanguage() )
158 case LANGUAGE_JAPANESE:
159 case LANGUAGE_CHINESE_TRADITIONAL:
160 case LANGUAGE_CHINESE_SIMPLIFIED:
161 case LANGUAGE_CHINESE_HONGKONG:
162 case LANGUAGE_CHINESE_SINGAPORE:
163 case LANGUAGE_CHINESE_MACAU:
164 case LANGUAGE_KOREAN:
165 case LANGUAGE_KOREAN_JOHAB:
166 bCJK = TRUE;
167 break;
168 default:
169 break;
171 // #107889# in CJK versions ALL strings (even those that contain latin characters)
172 // will get mnemonics in the form: xyz (M)
173 // thus steps 1) and 2) are skipped for CJK locales
175 // #110720#, avoid CJK-style mnemonics for latin-only strings that do not contain useful mnemonic chars
176 if( bCJK )
178 BOOL bLatinOnly = TRUE;
179 BOOL bMnemonicIndexFound = FALSE;
180 sal_Unicode c;
181 xub_StrLen nIndex;
183 for( nIndex=0; nIndex < nLen; nIndex++ )
185 c = aKey.GetChar( nIndex );
186 if ( ((c >= 0x3000) && (c <= 0xD7FF)) || // cjk
187 ((c >= 0xFF61) && (c <= 0xFFDC)) ) // halfwidth forms
189 bLatinOnly = FALSE;
190 break;
192 if( ImplGetMnemonicIndex( c ) != MNEMONIC_INDEX_NOTFOUND )
193 bMnemonicIndexFound = TRUE;
195 if( bLatinOnly && !bMnemonicIndexFound )
196 return FALSE;
200 int nCJK = 0;
201 USHORT nMnemonicIndex;
202 sal_Unicode c;
203 xub_StrLen nIndex = 0;
204 if( !bCJK )
206 // 1) first try the first character of a word
209 c = aKey.GetChar( nIndex );
211 if ( nCJK != 2 )
213 if ( ((c >= 0x3000) && (c <= 0xD7FF)) || // cjk
214 ((c >= 0xFF61) && (c <= 0xFFDC)) ) // halfwidth forms
215 nCJK = 1;
216 else if ( ((c >= 0x0030) && (c <= 0x0039)) || // digits
217 ((c >= 0x0041) && (c <= 0x005A)) || // latin capitals
218 ((c >= 0x0061) && (c <= 0x007A)) || // latin small
219 ((c >= 0x0370) && (c <= 0x037F)) || // greek numeral signs
220 ((c >= 0x0400) && (c <= 0x04FF)) ) // cyrillic
221 nCJK = 2;
224 nMnemonicIndex = ImplGetMnemonicIndex( c );
225 if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
227 if ( maMnemonics[nMnemonicIndex] )
229 maMnemonics[nMnemonicIndex] = 0;
230 rKey.Insert( MNEMONIC_CHAR, nIndex );
231 bChanged = TRUE;
232 break;
236 // Search for next word
239 nIndex++;
240 c = aKey.GetChar( nIndex );
241 if ( c == ' ' )
242 break;
244 while ( nIndex < nLen );
245 nIndex++;
247 while ( nIndex < nLen );
249 // 2) search for a unique/uncommon character
250 if ( !bChanged )
252 USHORT nBestCount = 0xFFFF;
253 USHORT nBestMnemonicIndex = 0;
254 xub_StrLen nBestIndex = 0;
255 nIndex = 0;
258 c = aKey.GetChar( nIndex );
259 nMnemonicIndex = ImplGetMnemonicIndex( c );
260 if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
262 if ( maMnemonics[nMnemonicIndex] )
264 if ( maMnemonics[nMnemonicIndex] < nBestCount )
266 nBestCount = maMnemonics[nMnemonicIndex];
267 nBestIndex = nIndex;
268 nBestMnemonicIndex = nMnemonicIndex;
269 if ( nBestCount == 2 )
270 break;
275 nIndex++;
277 while ( nIndex < nLen );
279 if ( nBestCount != 0xFFFF )
281 maMnemonics[nBestMnemonicIndex] = 0;
282 rKey.Insert( MNEMONIC_CHAR, nBestIndex );
283 bChanged = TRUE;
287 else
288 nCJK = 1;
290 // 3) Add English Mnemonic for CJK Text
291 if ( !bChanged && (nCJK == 1) && rKey.Len() )
293 // Append Ascii Mnemonic
294 for ( c = MNEMONIC_RANGE_2_START; c <= MNEMONIC_RANGE_2_END; c++ )
296 nMnemonicIndex = ImplGetMnemonicIndex( c );
297 if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
299 if ( maMnemonics[nMnemonicIndex] )
301 maMnemonics[nMnemonicIndex] = 0;
302 UniString aStr( '(' );
303 aStr += MNEMONIC_CHAR;
304 aStr += c;
305 aStr += ')';
306 nIndex = rKey.Len();
307 if( nIndex >= 2 )
309 static sal_Unicode cGreaterGreater[] = { 0xFF1E, 0xFF1E };
310 if ( rKey.EqualsAscii( ">>", nIndex-2, 2 ) ||
311 rKey.Equals( cGreaterGreater, nIndex-2, 2 ) )
312 nIndex -= 2;
314 if( nIndex >= 3 )
316 static sal_Unicode cDotDotDot[] = { 0xFF0E, 0xFF0E, 0xFF0E };
317 if ( rKey.EqualsAscii( "...", nIndex-3, 3 ) ||
318 rKey.Equals( cDotDotDot, nIndex-3, 3 ) )
319 nIndex -= 3;
321 if( nIndex >= 1)
323 sal_Unicode cLastChar = rKey.GetChar( nIndex-1 );
324 if ( (cLastChar == ':') || (cLastChar == 0xFF1A) ||
325 (cLastChar == '.') || (cLastChar == 0xFF0E) ||
326 (cLastChar == '?') || (cLastChar == 0xFF1F) ||
327 (cLastChar == ' ') )
328 nIndex--;
330 rKey.Insert( aStr, nIndex );
331 bChanged = TRUE;
332 break;
338 if( ! bChanged )
341 * #97809# if all else fails use the first character of a word
342 * anyway and live with duplicate mnemonics
344 nIndex = 0;
347 c = aKey.GetChar( nIndex );
349 nMnemonicIndex = ImplGetMnemonicIndex( c );
350 if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
352 maMnemonics[nMnemonicIndex] = 0;
353 rKey.Insert( MNEMONIC_CHAR, nIndex );
354 bChanged = TRUE;
355 break;
358 // Search for next word
361 nIndex++;
362 c = aKey.GetChar( nIndex );
363 if ( c == ' ' )
364 break;
366 while ( nIndex < nLen );
367 nIndex++;
369 while ( nIndex < nLen );
372 return bChanged;
375 // -----------------------------------------------------------------------
377 uno::Reference< i18n::XCharacterClassification > MnemonicGenerator::GetCharClass()
379 if ( !mxCharClass.is() )
380 mxCharClass = vcl::unohelper::CreateCharacterClassification();
381 return mxCharClass;
384 // -----------------------------------------------------------------------
386 String MnemonicGenerator::EraseAllMnemonicChars( const String& rStr )
388 String aStr = rStr;
389 xub_StrLen nLen = aStr.Len();
390 xub_StrLen i = 0;
392 while ( i < nLen )
394 if ( aStr.GetChar( i ) == '~' )
396 // check for CJK-style mnemonic
397 if( i > 0 && (i+2) < nLen )
399 sal_Unicode c = aStr.GetChar(i+1);
400 if( aStr.GetChar( i-1 ) == '(' &&
401 aStr.GetChar( i+2 ) == ')' &&
402 c >= MNEMONIC_RANGE_2_START && c <= MNEMONIC_RANGE_2_END )
404 aStr.Erase( i-1, 4 );
405 nLen -= 4;
406 i--;
407 continue;
411 // remove standard mnemonics
412 aStr.Erase( i, 1 );
413 nLen--;
415 else
416 i++;
419 return aStr;