1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: mnemonic.cxx,v $
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"
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
)
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() )
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
109 sal_Unicode cMnemonic
= ImplFindMnemonic( aKey
);
112 USHORT nMnemonicIndex
= ImplGetMnemonicIndex( cMnemonic
);
113 if ( nMnemonicIndex
!= MNEMONIC_INDEX_NOTFOUND
)
114 maMnemonics
[nMnemonicIndex
] = 0;
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
]++;
136 // -----------------------------------------------------------------------
138 BOOL
MnemonicGenerator::CreateMnemonic( XubString
& rKey
)
140 if ( !rKey
.Len() || ImplFindMnemonic( rKey
) )
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() )
150 XubString aKey
= xCharClass
->toUpper( rKey
, 0, rKey
.Len(), rLocale
);
152 BOOL bChanged
= FALSE
;
153 xub_StrLen nLen
= aKey
.Len();
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
:
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
178 BOOL bLatinOnly
= TRUE
;
179 BOOL bMnemonicIndexFound
= FALSE
;
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
192 if( ImplGetMnemonicIndex( c
) != MNEMONIC_INDEX_NOTFOUND
)
193 bMnemonicIndexFound
= TRUE
;
195 if( bLatinOnly
&& !bMnemonicIndexFound
)
201 USHORT nMnemonicIndex
;
203 xub_StrLen nIndex
= 0;
206 // 1) first try the first character of a word
209 c
= aKey
.GetChar( nIndex
);
213 if ( ((c
>= 0x3000) && (c
<= 0xD7FF)) || // cjk
214 ((c
>= 0xFF61) && (c
<= 0xFFDC)) ) // halfwidth forms
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
224 nMnemonicIndex
= ImplGetMnemonicIndex( c
);
225 if ( nMnemonicIndex
!= MNEMONIC_INDEX_NOTFOUND
)
227 if ( maMnemonics
[nMnemonicIndex
] )
229 maMnemonics
[nMnemonicIndex
] = 0;
230 rKey
.Insert( MNEMONIC_CHAR
, nIndex
);
236 // Search for next word
240 c
= aKey
.GetChar( nIndex
);
244 while ( nIndex
< nLen
);
247 while ( nIndex
< nLen
);
249 // 2) search for a unique/uncommon character
252 USHORT nBestCount
= 0xFFFF;
253 USHORT nBestMnemonicIndex
= 0;
254 xub_StrLen nBestIndex
= 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
];
268 nBestMnemonicIndex
= nMnemonicIndex
;
269 if ( nBestCount
== 2 )
277 while ( nIndex
< nLen
);
279 if ( nBestCount
!= 0xFFFF )
281 maMnemonics
[nBestMnemonicIndex
] = 0;
282 rKey
.Insert( MNEMONIC_CHAR
, nBestIndex
);
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
;
309 static sal_Unicode cGreaterGreater
[] = { 0xFF1E, 0xFF1E };
310 if ( rKey
.EqualsAscii( ">>", nIndex
-2, 2 ) ||
311 rKey
.Equals( cGreaterGreater
, nIndex
-2, 2 ) )
316 static sal_Unicode cDotDotDot
[] = { 0xFF0E, 0xFF0E, 0xFF0E };
317 if ( rKey
.EqualsAscii( "...", nIndex
-3, 3 ) ||
318 rKey
.Equals( cDotDotDot
, nIndex
-3, 3 ) )
323 sal_Unicode cLastChar
= rKey
.GetChar( nIndex
-1 );
324 if ( (cLastChar
== ':') || (cLastChar
== 0xFF1A) ||
325 (cLastChar
== '.') || (cLastChar
== 0xFF0E) ||
326 (cLastChar
== '?') || (cLastChar
== 0xFF1F) ||
330 rKey
.Insert( aStr
, nIndex
);
341 * #97809# if all else fails use the first character of a word
342 * anyway and live with duplicate mnemonics
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
);
358 // Search for next word
362 c
= aKey
.GetChar( nIndex
);
366 while ( nIndex
< nLen
);
369 while ( nIndex
< nLen
);
375 // -----------------------------------------------------------------------
377 uno::Reference
< i18n::XCharacterClassification
> MnemonicGenerator::GetCharClass()
379 if ( !mxCharClass
.is() )
380 mxCharClass
= vcl::unohelper::CreateCharacterClassification();
384 // -----------------------------------------------------------------------
386 String
MnemonicGenerator::EraseAllMnemonicChars( const String
& rStr
)
389 xub_StrLen nLen
= aStr
.Len();
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 );
411 // remove standard mnemonics