    rev 4839 : RT-2529: Locale specific runtime errors (Turkish)

       1 /*
       2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
       3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       4  *
       5  * This code is free software; you can redistribute it and/or modify it
       6  * under the terms of the GNU General Public License version 2 only, as
       7  * published by the Free Software Foundation.  Oracle designates this
       8  * particular file as subject to the "Classpath" exception as provided
       9  * by Oracle in the LICENSE file that accompanied this code.
      10  *
      11  * This code is distributed in the hope that it will be useful, but WITHOUT
      12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14  * version 2 for more details (a copy is included in the LICENSE file that
      15  * accompanied this code).
      16  *
      17  * You should have received a copy of the GNU General Public License version
      18  * 2 along with this work; if not, write to the Free Software Foundation,
      19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      20  *
      21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      22  * or visit www.oracle.com if you need additional information or have any
      23  * questions.
      24  */
      25
      26 package com.sun.javafx.scene.control.behavior;
      27
      28 import java.util.Locale;
      29 import java.util.StringTokenizer;
      30
      31 import javafx.scene.input.KeyCode;
      32
      33 /**
      34  * <p>
      35  * Provides support for embedding a mnemonic, accelerator and text in a single
      36  * string. A mnemonic is a single character that will either give a control
      37  * focus or activate it when pressed in conjunction with the base platform's
      38  * mnemonic modifier (typically the {@code Alt} key). An accelerator is a key
      39  * combination that will activate the object.
      40  * </p>
      41  *
      42  * <p>
      43  * The syntax of the string content is as follows:
      44  * </p>
      45  *
      46  * <ul>
      47  * <li>
      48  * <p>
      49  * <b>Simple mnemonic</b>: the first character preceded by the first {@code _}
      50  * character will be treated as the mnemonic. For example, "E_xit" will cause
      51  * the text to become "Exit" and the mnemonic will be "x". This is the most
      52  * common designation of a mnemonic, and the skin for a control will present the
      53  * mnemonic in the string with an underline. To prevent {@code _} from being
      54  * treated as the mnemonic prefix character, repeat it twice in a row. A
      55  * mnemonic is not required.
      56  * </p>
      57  * </li>
      58  * <li>
      59  * <p>
      60  * <b>Extended mnemonic</b>: an optional representation of a mnemonic is
      61  * {@code _(c)}, where {@code c} is the mnemonic character. For example,
      62  * "Exit_(q)" will cause the text to become "Exit" and the
      63  * {@link #extendedMnemonicText} to become "(q)". This is typically provided in
      64  * translated strings to support mnemonics where the main text does not have any
      65  * characters that map to keyboard keys. In these cases, the skin for the
      66  * control will typically present the mnemonic surrounded by parentheses. The
      67  * skin will also honor the hiding and presentation of the extended mnemonic
      68  * string on platforms where the mnemonic is only displayed when the mnemonic
      69  * modifier key is pressed.
      70  * <li>
      71  * <p>
      72  * <b>Accelerator</b>: an accelerator must be defined as the last element in the
      73  * string. It is delimited from the rest of the string by {@code @}. The
      74  * {@code @} must be followed by any number of modifier designations delimited
      75  * by {@code +} then followed by the accelerator character (e.g.,
      76  * {@code Ctrl+Shift+K}). The legal modifier designations are as follows (case
      77  * insensitive, and not to be translated): {@code Ctrl}, {@code Alt},
      78  * {@code Shift} and {@code Shortcut}. If an {@code @} appears
      79  * twice in a row in the string, it will be treated as a single {@code @} and
      80  * not as the accelerator delimiter.
      81  * <p></li>
      82  * </ul>
      83  *
      84  */
      85 public class TextBinding {
      86
      87     /**
      88      * the marker symbol used when parsing for mnemonics
      89      */
      90     private String MNEMONIC_SYMBOL = "_";
      91
      92     /**
      93      * The text with any markup for the mnemonic and accelerator removed.
      94      */
      95     private String text = null;
      96
      97     /**
      98      * Returns the text with any markup for the mnemonic and accelerator removed
      99      *
     100      * @return the text with any markup for the mnemonic and accelerator removed
     101      */
     102     public String getText() {
     103         return text;
     104     }
     105
     106     /**
     107      * The mnemonic or {@link KeyCode#UNDEFINED} if there is no mnemonic.
     108      */
     109     private KeyCode mnemonic = KeyCode.UNDEFINED;
     110
     111     /**
     112      * Returns the mnemonic or {@link KeyCode#UNDEFINED} if there is no
     113      * mnemonic.
     114      *
     115      * @return the mnemonic or {@link KeyCode#UNDEFINED} if there is no
     116      *         mnemonic
     117      */
     118     public KeyCode getMnemonic() {
     119         return mnemonic;
     120     }
     121
     122     /**
     123      * The index of the mnemonic character in the text property or -1 if there
     124      * is no mnemonic character in the text. This is only non-negative if the
     125      * simple {@code _c} syntax was used to specify the mnemonic.
     126      */
     127     private int mnemonicIndex = -1;
     128
     129     /**
     130      * Returns the index of the mnemonic character in the text property or -1 if
     131      * there is no mnemonic character in the text. This is only non-negative if
     132      * the simple {@code _c} syntax was used to specify the mnemonic.
     133      *
     134      * @return the index of the mnemonic character in the text property or -1 if
     135      *         there is no mnemonic character in the text
     136      */
     137     public int getMnemonicIndex() {
     138         return mnemonicIndex;
     139     }
     140
     141     /**
     142      * The extended mnemonic text (if it exists). This is only non-null if the
     143      * extended mnemonic syntax was used to specify the mnemonic.
     144      */
     145     private String extendedMnemonicText = null;
     146
     147     /**
     148      * Returns the extended mnemonic text (if it exists). This is only non-null
     149      * if the extended mnemonic syntax was used to specify the mnemonic.
     150      *
     151      * @return the extended mnemonic text (if it exists) or null
     152      */
     153     public String getExtendedMnemonicText() {
     154         return extendedMnemonicText;
     155     }
     156
     157     /**
     158      * The KeyBinding that describes the accelerator or null if there is no
     159      * accelerator.
     160      */
     161     private KeyBinding accelerator = null;
     162
     163     /**
     164      * Returns the KeyBinding that describes the accelerator or null if there is
     165      * no accelerator
     166      *
     167      * @return the KeyBinding that describes the accelerator or null if there is
     168      *         no accelerator
     169      */
     170     public KeyBinding getAccelerator() {
     171         return accelerator;
     172     }
     173
     174     /**
     175      * The localized accelerator text for use in presenting in an interface.
     176      */
     177     private String acceleratorText = null;
     178
     179     /**
     180      * Returns the localized accelerator text for use in presenting in an
     181      * interface or null if there is no accelerator text.
     182      *
     183      * @return the localized accelerator text for use in presenting in an
     184      *         interface or null if there is no accelerator text
     185      */
     186     public String getAcceleratorText() {
     187         return acceleratorText;
     188     }
     189
     190
     191     /**
     192      * for accelerator : is the control key needed?
     193      */
     194     private boolean ctrl = false;
     195     public boolean getCtrl() {
     196         return ctrl;
     197     }
     198
     199     /**
     200      * for accelerator : is the shift key needed?
     201      */
     202     private boolean shift = false;
     203     public boolean getShift() {
     204         return shift;
     205     }
     206
     207     /**
     208      * for accelerator : is the control key needed?
     209      */
     210     private boolean alt = false;
     211     public boolean getAlt() {
     212         return alt;
     213     }
     214
     215     /**
     216      * for accelerator : is the shortcut key needed?
     217      */
     218     private boolean shortcut = false;
     219     public boolean getShortcut() {
     220         return shortcut;
     221     }
     222
     223     /**
     224      * Creates a new TextBinding instance from the given string.
     225      *
     226      * @param s the action text string
     227      *
     228      * @see getText
     229      * @see getMnemonic
     230      * @see getMnemonicIndex
     231      * @see getExtendedMnemonicText
     232      * @see getAccelerator
     233      * @see getAcceleratorText
     234      */
     235     public TextBinding(String s) {
     236         parseAndSplit(s);
     237     }
     238
     239     /**
     240      * Parse and split the given string into the appropriate segments.
     241      */
     242     private void parseAndSplit(String s) {
     243         if (s == null || s.length() == 0) {
     244             text = s;
     245             return;
     246         }
     247
     248         // We will use temp as a working copy of the string and will pull
     249         // mnemonic and accelerator text out of it as we find those things.
     250         //
     251         StringBuffer temp = new StringBuffer(s);
     252
     253         // Find the accelerator if it exists.
     254         //
     255         int index = temp.indexOf("@");
     256         while (index >= 0 && index < (temp.length() - 1)) {
     257             if (temp.charAt(index + 1) == '@') {
     258                 temp.delete(index, index + 1); // delete the extra '@'
     259             } else {
     260                 // Only strip out the accelerator text if we can parse it.
     261                 // [[[TODO: WDW - provide a localized form that concatenates
     262                 // the localized strings for the modifier with the key.]]]
     263                 acceleratorText = temp.substring(index + 1);
     264                 accelerator = parseAcceleratorText(acceleratorText);
     265                 if (accelerator != null) {
     266                     temp.delete(index, temp.length());
     267                     break;
     268                 } else {
     269                     acceleratorText = null;
     270                 }
     271             }
     272             index = temp.indexOf("@", index + 1);
     273         }
     274
     275         // Find the mnemonic if it exists.
     276         //
     277         index = temp.indexOf(MNEMONIC_SYMBOL);
     278         while (index >= 0 && index < (temp.length() - 1)) {
     279             // Skip two _'s in a row
     280             if (MNEMONIC_SYMBOL.equals(temp.substring(index + 1, index + 2))) {
     281                 temp.delete(index, index + 1); // delete the extra MNEMONIC_SYMBOL
     282             } else if (temp.charAt(index + 1) != '('
     283                        || index == temp.length() - 2) {
     284                 String mnemonicChar = temp.substring(index + 1, index + 2);
     285                 mnemonic = KeyCode.getKeyCode(mnemonicChar.toUpperCase());
     286                 if (mnemonic != null) {
     287                     mnemonicIndex = index;
     288                 }
     289                 temp.delete(index, index + 1);
     290                 break;
     291             } else {
     292                 int endIndex = temp.indexOf(")", index + 3);
     293                 if (endIndex == -1) { // "(" is actually the mnemonic
     294                     String mnemonicChar = temp.substring(index + 1, index + 2);
     295                     mnemonic = KeyCode.getKeyCode(mnemonicChar.toUpperCase());
     296                     if (mnemonic != null) {
     297                         mnemonicIndex = index;
     298                     }
     299                     temp.delete(index, index + 1);
     300                     break;
     301                 } else if (endIndex == index + 3) {
     302                     String mnemonicChar = temp.substring(index + 2, index + 3);
     303                     mnemonic = KeyCode.getKeyCode(mnemonicChar.toUpperCase());
     304                     extendedMnemonicText = temp.substring(index + 1, index + 4);
     305                     temp.delete(index, endIndex + 3);
     306                     break;
     307                 }
     308             }
     309             index = temp.indexOf(MNEMONIC_SYMBOL, index + 1);
     310         }
     311         text = temp.toString();
     312     }
     313
     314     /**
     315      * Returns a KeyBinding for the given accelerator text or {@code null} if
     316      * the accelerator text cannot be parsed.
     317      *
     318      * @return a KeyBinding for the given accelerator text or {@code null} if
     319      *         the accelerator text cannot be parsed
     320      */
     321     private KeyBinding parseAcceleratorText(String s) {
     322         KeyBinding result = null;
     323         String text = null;
     324         boolean controlDown = false;
     325         boolean altDown = false;
     326         boolean shiftDown = false;
     327         boolean shortcutDown = false;
     328         boolean parseFail = false;
     329         StringTokenizer tokenizer = new StringTokenizer(s, "+");
     330         while (!parseFail && tokenizer.hasMoreTokens()) {
     331             String token = tokenizer.nextToken();
     332             if (!tokenizer.hasMoreTokens()) {
     333                 text = token;
     334             } else {
     335                 KeyCode code = KeyCode.getKeyCode(token.toUpperCase(Locale.ROOT));
     336                 if (code != null) {
     337                     switch (code) {
     338                       case CONTROL:
     339                         controlDown = true;
     340                         break;
     341                       case ALT:
     342                         altDown = true;
     343                         break;
     344                       case SHIFT:
     345                         shiftDown = true;
     346                         break;
     347                       case SHORTCUT:
     348                         shortcutDown = true;
     349                         break;
     350                       default:
     351                         text = null;
     352                         parseFail = true;
     353                         break;
     354                     }
     355                 } else {
     356                     text = null;
     357                     parseFail = true;
     358                 }
     359             }
     360         }
     361         if (text != null) {
     362             KeyCode code = KeyCode.getKeyCode(text.toUpperCase());
     363             if (code != null) {
     364                 if (code != KeyCode.UNDEFINED) {
     365                     result = new KeyBinding(code, null);
     366                     if (controlDown) {
     367                         ctrl = true;
     368                         result.ctrl();
     369                     }
     370                     if (altDown) {
     371                         alt = true;
     372                         result.alt();
     373                     }
     374                     if (shiftDown) {
     375                         shift = true;
     376                         result.shift();
     377                     }
     378                     if (shortcutDown) {
     379                         shortcut = true;
     380                         result.shortcut();
     381                     }
     382                 }
     383             }
     384         }
     385         return result;
     386     }
     387
     388     @Override public String toString() {
     389         return "TextBinding [text=" + getText() + ",mnemonic=" + getMnemonic()
     390             + ", mnemonicIndex=" + getMnemonicIndex()
     391             + ", extendedMnemonicText=" + getExtendedMnemonicText()
     392             + ", accelerator=" + getAccelerator() + ", acceleratorText="
     393             + getAcceleratorText() + "]";
     394     }
     395 }
    --- EOF ---