All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jgoodies.common.swing.MnemonicUtils Maven / Gradle / Ivy

Go to download

The JGoodies Common library provides convenience code for other JGoodies libraries and applications.

There is a newer version: 1.8.1
Show newest version
/*
 * Copyright (c) 2009-2012 JGoodies Karsten Lentzsch. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of JGoodies Karsten Lentzsch nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jgoodies.common.swing;

import static com.jgoodies.common.base.Preconditions.checkNotNull;

import java.awt.event.KeyEvent;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;

import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.JLabel;

import com.jgoodies.common.base.Strings;


/**
 * Configures the text, mnemonic and mnemonic index in Actions, JLabels,
 * and AbstractButtons. The term marked text is used for Strings
 * that contain a marker character that marks both the mnemonic and
 * its index in the resulting plain text.

* * To set a mnemonic, add an ampersand ('&') before the character * that should be the mnemonic, for example "&Save". * If your text has multiple ampersands, only the first one is used to mark * the mnemonic; the other ampersands appear as normal text. * For example "&Look&Feel" has the first 'L' has mnemonic. * If you want to use the ampersand itself in the text, use two consecutive * ampersands for any ampersand that precede the one that is used to mark * the mnemonic. For example the marked text "R&&D D&epartment" * has "R&D Department" as text and the mnemonic is 'E'.

* * Examples: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Marked TextPlain TextMnemonicMnemonic IndexComment
SaveSave0-1No mnemonic
&SaveSave'S'0 
Save &asSave as'A'5Second 'a' marked
Look&FeelLookFeel'F'4& should be quoted
Look&&FeelLook&Feel0-1& is quoted
&Look&FeelLook&FeelL0Second & needs no quote
Look & FeelLook & Feel0-1Whitespace cannot be marked
R&&D D&epartmentR&D Department'E'5First & is quoted
<html>a&b</html><html>a<u>b</u></html>'B'-1'b' is underlined
<html>R&amp;D D&ep.</html><html>R&amp;D D<u>e</u>p.</html>'B'-1HTML &amp; doesn't mark
* * @author Karsten Lentzsch * @version $Revision: 1.1 $ */ public final class MnemonicUtils { /** * The single mnemonic marker. Future versions of this class shall * support other markers, for example the underline char ('_'). */ static final char MNEMONIC_MARKER = '&'; // Instance Creation ****************************************************** private MnemonicUtils() { // Suppresses default constructor, prevents instantiation. } // Setting Text, Mnemonic and Index *************************************** /** * Configures the text, mnemonic and mnemonic index for {@code target} * using the given text that can be marked with the mnemonic marker '&'. * For example if {@code markedText} is "Save &as", the text * will be set to "Save as", the mnemonic is 'a', and the * mnemonic index is 5. * * @param target the button to be configured * @param markedText the text with optional mnemonic marker * @throws NullPointerException if {@code target} is {@code null} */ public static void configure(AbstractButton target, String markedText) { checkNotNull(target, "target must not be null."); configure0(target, new MnemonicText(markedText, MNEMONIC_MARKER)); } /** * Configures the text, mnemonic and mnemonic index for {@code target} * using the given text that can be marked with the mnemonic marker '&'. * For example if {@code markedText} is "Save &as", the text * will be set to "Save as", the mnemonic is 'a', and the * mnemonic index is 5. * * @param target the Action to be configured * @param markedText the text with optional mnemonic marker * @throws NullPointerException if {@code target} is {@code null} */ public static void configure(Action target, String markedText) { checkNotNull(target, "target must not be null."); configure0(target, new MnemonicText(markedText, MNEMONIC_MARKER)); } /** * Configures the text, mnemonic and mnemonic index for {@code target} * using the given text that can be marked with the mnemonic marker '&'. * For example if {@code markedText} is "Save &as", the text * will be set to "Save as", the mnemonic is 'a', and the * mnemonic index is 5. * * @param target the label to be configured * @param markedText the text with optional mnemonic marker * @throws NullPointerException if {@code target} is {@code null} */ public static void configure(JLabel target, String markedText) { checkNotNull(target, "target must not be null."); configure0(target, new MnemonicText(markedText, MNEMONIC_MARKER)); } // Misc ******************************************************************* /** * Returns the plain text for the given marked text by removing * the mnemonic marker and marker quotes - if any. If the marked text * is HTML, the plain text has the mnemonic character underlined. * * See the {@link MnemonicUtils} class comment for information * about how to mark a mnemonic and how to quote a marker. * *
     * MnemonicUtils.plainText("Save")             == "Save"
     * MnemonicUtils.plainText("&Save")            == "Save"
     * MnemonicUtils.plainText("&Look&Feel")       == "Look&Feel"
     * MnemonicUtils.plainText("Look & Feel")      == "Look & Feel"
     * MnemonicUtils.plainText("R&&D D&epartment") == "R&D Department"
     * MnemonicUtils.plainText("<html>a&b</html>") == "<html>a<u>b</u></html>"
     * 
* * @param markedText the text that may contain a mnemonic marker * @return the text without mnemonic marker and marker quotes */ public static String plainText(String markedText) { return new MnemonicText(markedText, MNEMONIC_MARKER).text; } // Testing API ************************************************************ /** * Extracts and returns the mnemonic from the given marked text. * * @param markedText the text with optional mnemonic marker * @return the mnemonic or {@code 0} if no mnemonic is marked */ static int mnemonic(String markedText) { return new MnemonicText(markedText, MNEMONIC_MARKER).key; } /** * Finds and returns the index of the mnemonic in the marked text. * * @param markedText the text with optional mnemonic marker * @return the mnemonic index or {@code -1} if there's no mnemonic * or the text is HTML */ static int mnemonicIndex(String markedText) { return new MnemonicText(markedText, MNEMONIC_MARKER).index; } // Implementation ********************************************************* private static void configure0(AbstractButton button, MnemonicText mnemonicText) { button.setText(mnemonicText.text); button.setMnemonic(mnemonicText.key); button.setDisplayedMnemonicIndex(mnemonicText.index); } /* * We set the mnemonic only if non-null. This works around a bug in * AbstractButton that can configure itself from an Action with mnemonic * set to null but throws an NPE if the Action's mnemonic becomes * null afterwards. * * @see AbstractButton.ButtonActionPropertyChangeListener */ private static void configure0(Action action, MnemonicText mnemonicText) { Integer keyValue = Integer.valueOf(mnemonicText.key); Integer indexValue = mnemonicText.index == -1 ? null : Integer.valueOf(mnemonicText.index); action.putValue(Action.NAME, mnemonicText.text); action.putValue(Action.MNEMONIC_KEY, keyValue); action.putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, indexValue); } private static void configure0(JLabel label, MnemonicText mnemonicText) { label.setText(mnemonicText.text); label.setDisplayedMnemonic(mnemonicText.key); label.setDisplayedMnemonicIndex(mnemonicText.index); } // Helper Code ************************************************************ private static final class MnemonicText { String text; int key; int index; private MnemonicText(String markedText, char marker) { int i; if ( markedText == null || markedText.length() <= 1 || (i = markedText.indexOf(marker)) == -1) { text = markedText; key = KeyEvent.VK_UNDEFINED; index = -1; return; } boolean html = Strings.startsWithIgnoreCase(markedText, ""); StringBuilder builder = new StringBuilder(); int begin = 0; int quotedMarkers = 0; int markerIndex = -1; boolean marked = false; char markedChar = 0; CharacterIterator sci = new StringCharacterIterator(markedText); do { builder.append(markedText.substring(begin, i)); char current = sci.setIndex(i); char next = sci.next(); if (html) { int entityEnd = indexOfEntityEnd(markedText, i); if (entityEnd == -1) { marked = true; builder.append("").append(next).append(""); begin = i+2; markedChar = next; } else { builder.append(markedText.substring(i, entityEnd)); begin = entityEnd; } } else { if (next == marker) { // "Look&&Feel" builder.append(next); begin = i+2; quotedMarkers++; } else if (Character.isWhitespace(next)) { // "Look & Feel" builder.append(current).append(next); begin = i+2; } else if (next == CharacterIterator.DONE) { builder.append(current); begin = i+2; } else { builder.append(next); begin = i+2; markerIndex = i - quotedMarkers; marked = true; markedChar = next; } } i = markedText.indexOf(marker, begin); } while (i != -1 && !marked); if (begin < markedText.length()) { builder.append(markedText.substring(begin)); } text = builder.toString(); index = markerIndex; if (marked) { key = mnemonicKey(markedChar); } else { key = KeyEvent.VK_UNDEFINED; } } private static int indexOfEntityEnd(String htmlText, int start) { CharacterIterator sci = new StringCharacterIterator(htmlText, start); char c; do { c = sci.next(); if (c == ';') { return sci.getIndex(); } if (!Character.isLetterOrDigit(c)) { return -1; } } while (c != CharacterIterator.DONE); return -1; } /* A general purpose way to map from a char to a KeyCode is needed. An * AWT RFE has been filed: * http://bt2ws.central.sun.com/CrPrint?id=6559449 * CR 6559449 java/classes_awt Support for converting from char to KeyEvent VK_ keycode */ private static int mnemonicKey(char c) { int vk = c; if (vk >= 'a' && vk <= 'z') { vk -= 'a' - 'A'; } return vk; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy