org.openide.awt.Mnemonics Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Contributors: Maxym Mykhalchuk
*/
package org.openide.awt;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.openide.util.Utilities;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import javax.swing.AbstractButton;
import javax.swing.JLabel;
import javax.swing.UIManager;
/**
* Support class for setting button, menu, and label text strings with mnemonics.
* @author Maxym Mykhalchuk
* @since 3.37
* @see Issue #26640
*/
public final class Mnemonics extends Object {
/** Private constructor in order that this class is never instantiated. */
private Mnemonics() {
}
/**
* Actual setter of the text & mnemonics for the AbstractButton/JLabel or
* their subclasses.
* @param item AbstractButton/JLabel
* @param text new label
*/
private static void setLocalizedText2(Object item, String text) {
// #17664. Handle null text also.
// & in HTML should be ignored
if (text == null) { // NOI18N
setText(item, null);
return;
}
int i = findMnemonicAmpersand(text);
setMnemonicIndex(item, -1);
if (i < 0) {
// no '&' - don't set the mnemonic
setText(item, text);
setMnemonic(item, 0);
} else {
setText(item, text.substring(0, i) + text.substring(i + 1));
//#67807 no mnemonics on macosx
if (isAquaLF()) {
setMnemonic(item, 0);
} else {
char ch = text.charAt(i + 1);
if (text.startsWith("")) { // NOI18N
// Workaround for JDK bug #6510775
setText(item, text.substring(0, i) + "" + ch + "" + text.substring(i + 2)); // NOI18N
i += 3; // just in case it gets fixed
}
if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')) || ((ch >= '0') && (ch <= '9'))) {
// it's latin character or arabic digit,
// setting it as mnemonics
setMnemonic(item, ch);
// If it's something like "Save &As", we need to set another
// mnemonic index (at least under 1.4 or later)
// see #29676
setMnemonicIndex(item, i);
} else {
// it's non-latin, getting the latin correspondance
try {
int latinCode = getLatinKeycode(ch);
setMnemonic(item, latinCode);
setMnemonicIndex(item, i);
} catch (MissingResourceException e) {
Logger.getLogger(Mnemonics.class.getName()).info("Mapping from a non-Latin character '"+ch+
"' not found in a localized (branded) version of "+
"openide/awt/src/org/openide/awt/Mnemonics.properties - "+
"mnemonic cannot be assigned in "+text);
}
}
}
}
}
/**
* Sets the text for a menu item or other subclass of AbstractButton.
* Examples:
*
* example
* Input String View under JDK 1.4 or later
* Save &As
Save As
* Rock & Roll
Rock & Roll
* Drag & &Drop
Drag & Drop
* &Файл
Файл
*
* @param item a button whose text will be changed
* @param text new label
*/
public static void setLocalizedText(AbstractButton item, String text) {
setLocalizedText2(item, text);
}
/**
* Sets the text for the label or other subclass of JLabel.
* For details see {@link #setLocalizedText(AbstractButton, String)}.
* @param item a label whose text will be set
* @param text new label
*/
public static void setLocalizedText(JLabel item, String text) {
setLocalizedText2(item, text);
}
/**
* Searches for an ampersand in a string which indicates a mnemonic.
* Recognizes the following cases:
*
* - "Drag & Drop", "Ampersand ('&')" - don't have mnemonic ampersand.
* "&" is not found before " " (space), or if enclosed in "'"
* (single quotation marks).
*
- "&File", "Save &As..." - do have mnemonic ampersand.
*
- "Rock & Ro&ll", "Underline the '&' &character" - also do have
* mnemonic ampersand, but the second one.
*
- "<html><b>R&D</b> departmen&t" - has mnemonic
* ampersand before "t".
* Ampersands in HTML texts that are part of entity are ignored.
*
* @param text text to search
* @return the position of mnemonic ampersand in text, or -1 if there is none
*/
public static int findMnemonicAmpersand(String text) {
int i = -1;
boolean isHTML = text.startsWith("");
do {
// searching for the next ampersand
i = text.indexOf('&', i + 1);
if ((i >= 0) && ((i + 1) < text.length())) {
if (isHTML) {
boolean startsEntity = false;
for (int j = i + 1; j < text.length(); j++) {
char c = text.charAt(j);
if (c == ';') {
startsEntity = true;
break;
}
if (!Character.isLetterOrDigit(c)) {
break;
}
}
if (!startsEntity) {
return i;
}
}
else {
// before ' '
if (text.charAt(i + 1) == ' ') {
continue;
// before ', and after '
} else if ((text.charAt(i + 1) == '\'') && (i > 0) && (text.charAt(i - 1) == '\'')) {
continue;
}
// ampersand is marking mnemonics
return i;
}
}
} while (i >= 0);
return -1;
}
/**
* Gets the Latin symbol which corresponds
* to some non-Latin symbol on the localized keyboard.
* The search is done via lookup of Resource bundle
* for pairs having the form (e.g.) MNEMONIC_\u0424=A
.
* @param localeChar non-Latin character or a punctuator to be used as mnemonic
* @return character on latin keyboard, corresponding to the locale character,
* or the appropriate VK_*** code (if there's no latin character
* "under" the non-Latin one
*/
private static int getLatinKeycode(char localeChar) throws MissingResourceException {
// associated should be a latin character, arabic digit
// or an integer (KeyEvent.VK_***)
String str = getBundle().getString("MNEMONIC_" + localeChar); // NOI18N
if (str.length() == 1) {
return str.charAt(0);
} else {
return Integer.parseInt(str);
}
}
/**
* Wrapper for the
* AbstractButton.setMnemonicIndex
or
* JLabel.setDisplayedMnemonicIndex
method.
* @param item AbstractButton/JLabel or subclasses
* @param index Index of the Character to underline under JDK1.4
* @param latinCode Latin Character Keycode to underline under JDK1.3
*/
private static void setMnemonicIndex(Object item, int index) {
if (item instanceof AbstractButton) {
AbstractButton b = (AbstractButton) item;
b.putClientProperty(PROP_DISPLAYED_MNEMONIC_INDEX, index);
b.removePropertyChangeListener(PROP_DISPLAYED_MNEMONIC_INDEX, MNEMONIC_INDEX_LISTENER);
b.setDisplayedMnemonicIndex(index);
b.addPropertyChangeListener(PROP_DISPLAYED_MNEMONIC_INDEX, MNEMONIC_INDEX_LISTENER);
} else if (item instanceof JLabel) {
((JLabel) item).setDisplayedMnemonicIndex(index);
}
}
private static final String PROP_TEXT = "text"; // NOI18N
private static final String PROP_MNEMONIC = "mnemonic"; // NOI18N
private static final String PROP_DISPLAYED_MNEMONIC_INDEX = "displayedMnemonicIndex"; // NOI18N
private static final PropertyChangeListener MNEMONIC_INDEX_LISTENER = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
AbstractButton b = (AbstractButton) evt.getSource();
if (b.getDisplayedMnemonicIndex() == -1) {
Integer mnemonic = (Integer) b.getClientProperty(PROP_MNEMONIC);
Integer index = (Integer) b.getClientProperty(PROP_DISPLAYED_MNEMONIC_INDEX);
if (mnemonic != null && index != null && Utilities.compareObjects(b.getText(), b.getClientProperty(PROP_TEXT))) {
b.setMnemonic(mnemonic);
b.setDisplayedMnemonicIndex(index);
}
}
}
};
/**
* Wrapper for AbstractButton/JLabel.setText
* @param item AbstractButton/JLabel
* @param text the text to set
*/
private static void setText(Object item, String text) {
if (item instanceof AbstractButton) {
AbstractButton b = (AbstractButton) item;
b.putClientProperty(PROP_TEXT, text);
b.setText(text);
} else {
((JLabel) item).setText(text);
}
}
/**
* Wrapper for AbstractButton.setMnemonic and JLabel.setDisplayedMnemonic
* @param item AbstractButton/JLabel
* @param mnem Mnemonic char to set, latin [a-z,A-Z], digit [0-9], or any VK_ code
*/
private static void setMnemonic(Object item, int mnem) {
if (isAquaLF()) {
// there shall be no mnemonics on macosx.
//#55864
return;
}
if ((mnem >= 'a') && (mnem <= 'z')) {
mnem = mnem + ('A' - 'a');
}
if (item instanceof AbstractButton) {
AbstractButton b = (AbstractButton) item;
b.putClientProperty(PROP_MNEMONIC, mnem);
b.setMnemonic(mnem);
} else {
((JLabel) item).setDisplayedMnemonic(mnem);
}
}
/**
* Getter for the used Resource bundle (org.openide.awt.Mnemonics).
* Used to avoid calling
ResourceBundle.getBundle(...)
* many times in defferent places of the code.
* Does no caching, it's simply an utility method.
*/
private static ResourceBundle getBundle() {
return ResourceBundle.getBundle("org.openide.awt.Mnemonics"); // NOI18N
}
static boolean isAquaLF() {
return "Aqua".equals( UIManager.getLookAndFeel().getID() ); //NOI18N
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy