diff options
Diffstat (limited to 'vcl/source/window/mnemonic.cxx')
-rw-r--r-- | vcl/source/window/mnemonic.cxx | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/vcl/source/window/mnemonic.cxx b/vcl/source/window/mnemonic.cxx new file mode 100644 index 000000000000..c2c6c18135f2 --- /dev/null +++ b/vcl/source/window/mnemonic.cxx @@ -0,0 +1,419 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/mnemonic.hxx> + +#include <vcl/unohelp.hxx> +#include <com/sun/star/i18n/XCharacterClassification.hpp> + +using namespace ::com::sun::star; + + +// ======================================================================= + +MnemonicGenerator::MnemonicGenerator() +{ + memset( maMnemonics, 1, sizeof( maMnemonics ) ); +} + +// ----------------------------------------------------------------------- + +USHORT MnemonicGenerator::ImplGetMnemonicIndex( sal_Unicode c ) +{ + static USHORT const aImplMnemonicRangeTab[MNEMONIC_RANGES*2] = + { + MNEMONIC_RANGE_1_START, MNEMONIC_RANGE_1_END, + MNEMONIC_RANGE_2_START, MNEMONIC_RANGE_2_END, + MNEMONIC_RANGE_3_START, MNEMONIC_RANGE_3_END, + MNEMONIC_RANGE_4_START, MNEMONIC_RANGE_4_END + }; + + USHORT nMnemonicIndex = 0; + for ( USHORT i = 0; i < MNEMONIC_RANGES; i++ ) + { + if ( (c >= aImplMnemonicRangeTab[i*2]) && + (c <= aImplMnemonicRangeTab[i*2+1]) ) + return nMnemonicIndex+c-aImplMnemonicRangeTab[i*2]; + + nMnemonicIndex += aImplMnemonicRangeTab[i*2+1]-aImplMnemonicRangeTab[i*2]; + } + + return MNEMONIC_INDEX_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +sal_Unicode MnemonicGenerator::ImplFindMnemonic( const XubString& rKey ) +{ + xub_StrLen nIndex = 0; + while ( (nIndex = rKey.Search( MNEMONIC_CHAR, nIndex )) != STRING_NOTFOUND ) + { + sal_Unicode cMnemonic = rKey.GetChar( nIndex+1 ); + if ( cMnemonic != MNEMONIC_CHAR ) + return cMnemonic; + nIndex += 2; + } + + return 0; +} + +// ----------------------------------------------------------------------- + +void MnemonicGenerator::RegisterMnemonic( const XubString& rKey ) +{ + const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetUILocale(); + uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass(); + + // Don't crash even when we don't have access to i18n service + if ( !xCharClass.is() ) + return; + + XubString aKey = xCharClass->toUpper( rKey, 0, rKey.Len(), rLocale ); + + // If we find a Mnemonic, set the flag. In other case count the + // characters, because we need this to set most as possible + // Mnemonics + sal_Unicode cMnemonic = ImplFindMnemonic( aKey ); + if ( cMnemonic ) + { + USHORT nMnemonicIndex = ImplGetMnemonicIndex( cMnemonic ); + if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND ) + maMnemonics[nMnemonicIndex] = 0; + } + else + { + xub_StrLen nIndex = 0; + xub_StrLen nLen = aKey.Len(); + while ( nIndex < nLen ) + { + sal_Unicode c = aKey.GetChar( nIndex ); + + USHORT nMnemonicIndex = ImplGetMnemonicIndex( c ); + if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND ) + { + if ( maMnemonics[nMnemonicIndex] && (maMnemonics[nMnemonicIndex] < 0xFF) ) + maMnemonics[nMnemonicIndex]++; + } + + nIndex++; + } + } +} + +// ----------------------------------------------------------------------- + +BOOL MnemonicGenerator::CreateMnemonic( XubString& rKey ) +{ + if ( !rKey.Len() || ImplFindMnemonic( rKey ) ) + return FALSE; + + const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetUILocale(); + uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass(); + + // Don't crash even when we don't have access to i18n service + if ( !xCharClass.is() ) + return FALSE; + + XubString aKey = xCharClass->toUpper( rKey, 0, rKey.Len(), rLocale ); + + BOOL bChanged = FALSE; + xub_StrLen nLen = aKey.Len(); + + BOOL bCJK = FALSE; + switch( Application::GetSettings().GetUILanguage() ) + { + case LANGUAGE_JAPANESE: + case LANGUAGE_CHINESE_TRADITIONAL: + case LANGUAGE_CHINESE_SIMPLIFIED: + case LANGUAGE_CHINESE_HONGKONG: + case LANGUAGE_CHINESE_SINGAPORE: + case LANGUAGE_CHINESE_MACAU: + case LANGUAGE_KOREAN: + case LANGUAGE_KOREAN_JOHAB: + bCJK = TRUE; + break; + default: + break; + } + // #107889# in CJK versions ALL strings (even those that contain latin characters) + // will get mnemonics in the form: xyz (M) + // thus steps 1) and 2) are skipped for CJK locales + + // #110720#, avoid CJK-style mnemonics for latin-only strings that do not contain useful mnemonic chars + if( bCJK ) + { + BOOL bLatinOnly = TRUE; + BOOL bMnemonicIndexFound = FALSE; + sal_Unicode c; + xub_StrLen nIndex; + + for( nIndex=0; nIndex < nLen; nIndex++ ) + { + c = aKey.GetChar( nIndex ); + if ( ((c >= 0x3000) && (c <= 0xD7FF)) || // cjk + ((c >= 0xFF61) && (c <= 0xFFDC)) ) // halfwidth forms + { + bLatinOnly = FALSE; + break; + } + if( ImplGetMnemonicIndex( c ) != MNEMONIC_INDEX_NOTFOUND ) + bMnemonicIndexFound = TRUE; + } + if( bLatinOnly && !bMnemonicIndexFound ) + return FALSE; + } + + + int nCJK = 0; + USHORT nMnemonicIndex; + sal_Unicode c; + xub_StrLen nIndex = 0; + if( !bCJK ) + { + // 1) first try the first character of a word + do + { + c = aKey.GetChar( nIndex ); + + if ( nCJK != 2 ) + { + if ( ((c >= 0x3000) && (c <= 0xD7FF)) || // cjk + ((c >= 0xFF61) && (c <= 0xFFDC)) ) // halfwidth forms + nCJK = 1; + else if ( ((c >= 0x0030) && (c <= 0x0039)) || // digits + ((c >= 0x0041) && (c <= 0x005A)) || // latin capitals + ((c >= 0x0061) && (c <= 0x007A)) || // latin small + ((c >= 0x0370) && (c <= 0x037F)) || // greek numeral signs + ((c >= 0x0400) && (c <= 0x04FF)) ) // cyrillic + nCJK = 2; + } + + nMnemonicIndex = ImplGetMnemonicIndex( c ); + if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND ) + { + if ( maMnemonics[nMnemonicIndex] ) + { + maMnemonics[nMnemonicIndex] = 0; + rKey.Insert( MNEMONIC_CHAR, nIndex ); + bChanged = TRUE; + break; + } + } + + // Search for next word + do + { + nIndex++; + c = aKey.GetChar( nIndex ); + if ( c == ' ' ) + break; + } + while ( nIndex < nLen ); + nIndex++; + } + while ( nIndex < nLen ); + + // 2) search for a unique/uncommon character + if ( !bChanged ) + { + USHORT nBestCount = 0xFFFF; + USHORT nBestMnemonicIndex = 0; + xub_StrLen nBestIndex = 0; + nIndex = 0; + do + { + c = aKey.GetChar( nIndex ); + nMnemonicIndex = ImplGetMnemonicIndex( c ); + if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND ) + { + if ( maMnemonics[nMnemonicIndex] ) + { + if ( maMnemonics[nMnemonicIndex] < nBestCount ) + { + nBestCount = maMnemonics[nMnemonicIndex]; + nBestIndex = nIndex; + nBestMnemonicIndex = nMnemonicIndex; + if ( nBestCount == 2 ) + break; + } + } + } + + nIndex++; + } + while ( nIndex < nLen ); + + if ( nBestCount != 0xFFFF ) + { + maMnemonics[nBestMnemonicIndex] = 0; + rKey.Insert( MNEMONIC_CHAR, nBestIndex ); + bChanged = TRUE; + } + } + } + else + nCJK = 1; + + // 3) Add English Mnemonic for CJK Text + if ( !bChanged && (nCJK == 1) && rKey.Len() ) + { + // Append Ascii Mnemonic + for ( c = MNEMONIC_RANGE_2_START; c <= MNEMONIC_RANGE_2_END; c++ ) + { + nMnemonicIndex = ImplGetMnemonicIndex( c ); + if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND ) + { + if ( maMnemonics[nMnemonicIndex] ) + { + maMnemonics[nMnemonicIndex] = 0; + UniString aStr( '(' ); + aStr += MNEMONIC_CHAR; + aStr += c; + aStr += ')'; + nIndex = rKey.Len(); + if( nIndex >= 2 ) + { + static sal_Unicode cGreaterGreater[] = { 0xFF1E, 0xFF1E }; + if ( rKey.EqualsAscii( ">>", nIndex-2, 2 ) || + rKey.Equals( cGreaterGreater, nIndex-2, 2 ) ) + nIndex -= 2; + } + if( nIndex >= 3 ) + { + static sal_Unicode cDotDotDot[] = { 0xFF0E, 0xFF0E, 0xFF0E }; + if ( rKey.EqualsAscii( "...", nIndex-3, 3 ) || + rKey.Equals( cDotDotDot, nIndex-3, 3 ) ) + nIndex -= 3; + } + if( nIndex >= 1) + { + sal_Unicode cLastChar = rKey.GetChar( nIndex-1 ); + if ( (cLastChar == ':') || (cLastChar == 0xFF1A) || + (cLastChar == '.') || (cLastChar == 0xFF0E) || + (cLastChar == '?') || (cLastChar == 0xFF1F) || + (cLastChar == ' ') ) + nIndex--; + } + rKey.Insert( aStr, nIndex ); + bChanged = TRUE; + break; + } + } + } + } + +// #i87415# Duplicates mnemonics are bad for consistent keyboard accessibility +// It's probably better to not have mnemonics for some widgets, than to have ambiguous ones. +// if( ! bChanged ) +// { +// /* +// * #97809# if all else fails use the first character of a word +// * anyway and live with duplicate mnemonics +// */ +// nIndex = 0; +// do +// { +// c = aKey.GetChar( nIndex ); +// +// nMnemonicIndex = ImplGetMnemonicIndex( c ); +// if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND ) +// { +// maMnemonics[nMnemonicIndex] = 0; +// rKey.Insert( MNEMONIC_CHAR, nIndex ); +// bChanged = TRUE; +// break; +// } +// +// // Search for next word +// do +// { +// nIndex++; +// c = aKey.GetChar( nIndex ); +// if ( c == ' ' ) +// break; +// } +// while ( nIndex < nLen ); +// nIndex++; +// } +// while ( nIndex < nLen ); +// } + + return bChanged; +} + +// ----------------------------------------------------------------------- + +uno::Reference< i18n::XCharacterClassification > MnemonicGenerator::GetCharClass() +{ + if ( !mxCharClass.is() ) + mxCharClass = vcl::unohelper::CreateCharacterClassification(); + return mxCharClass; +} + +// ----------------------------------------------------------------------- + +String MnemonicGenerator::EraseAllMnemonicChars( const String& rStr ) +{ + String aStr = rStr; + xub_StrLen nLen = aStr.Len(); + xub_StrLen i = 0; + + while ( i < nLen ) + { + if ( aStr.GetChar( i ) == '~' ) + { + // check for CJK-style mnemonic + if( i > 0 && (i+2) < nLen ) + { + sal_Unicode c = aStr.GetChar(i+1); + if( aStr.GetChar( i-1 ) == '(' && + aStr.GetChar( i+2 ) == ')' && + c >= MNEMONIC_RANGE_2_START && c <= MNEMONIC_RANGE_2_END ) + { + aStr.Erase( i-1, 4 ); + nLen -= 4; + i--; + continue; + } + } + + // remove standard mnemonics + aStr.Erase( i, 1 ); + nLen--; + } + else + i++; + } + + return aStr; +} |