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: */
|