/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-15 The Processing Foundation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.ui;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Window;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.ImageObserver;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;
import processing.app.Language;
import processing.app.Messages;
import processing.app.Platform;
import processing.app.Preferences;
import processing.app.Util;
import processing.core.PApplet;
import processing.data.StringList;
/**
* Utility functions for base that require a java.awt.Toolkit object. These
* are broken out from Base as we start moving toward the possibility of the
* code running in headless mode.
*/
public class Toolkit {
static final java.awt.Toolkit awtToolkit =
java.awt.Toolkit.getDefaultToolkit();
/** Command on Mac OS X, Ctrl on Windows and Linux */
static final int SHORTCUT_KEY_MASK =
awtToolkit.getMenuShortcutKeyMask();
/** Command-W on Mac OS X, Ctrl-W on Windows and Linux */
public static final KeyStroke WINDOW_CLOSE_KEYSTROKE =
KeyStroke.getKeyStroke('W', SHORTCUT_KEY_MASK);
/** Command-Option on Mac OS X, Ctrl-Alt on Windows and Linux */
static final int SHORTCUT_ALT_KEY_MASK =
ActionEvent.ALT_MASK | SHORTCUT_KEY_MASK;
/** Command-Shift on Mac OS X, Ctrl-Shift on Windows and Linux */
static final int SHORTCUT_SHIFT_KEY_MASK =
ActionEvent.SHIFT_MASK | SHORTCUT_KEY_MASK;
/**
* Standardized width for buttons. Mac OS X 10.3 wants 70 as its default,
* Windows XP needs 66, and my Ubuntu machine needs 80+, so 80 seems proper.
* This is now stored in the languages file since this may need to be larger
* for languages that are consistently wider than English.
*/
static public int getButtonWidth() {
// Made into a method so that calling Toolkit methods doesn't require
// the languages to be loaded, and with that, Base initialized completely
return zoom(Integer.parseInt(Language.text("preferences.button.width")));
}
/**
* A software engineer, somewhere, needs to have their abstraction
* taken away. Who crafts the sort of API that would require a
* five-line helper function just to set the shortcut key for a
* menu item?
*/
static public JMenuItem newJMenuItem(String title, int what) {
JMenuItem menuItem = new JMenuItem(title);
int modifiers = awtToolkit.getMenuShortcutKeyMask();
menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers));
return menuItem;
}
/**
* @param action: use an Action, which sets the title, reaction
* and enabled-ness all by itself.
*/
static public JMenuItem newJMenuItem(Action action, int what) {
JMenuItem menuItem = new JMenuItem(action);
int modifiers = awtToolkit.getMenuShortcutKeyMask();
menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers));
return menuItem;
}
/**
* Like newJMenuItem() but adds shift as a modifier for the shortcut.
*/
static public JMenuItem newJMenuItemShift(String title, int what) {
JMenuItem menuItem = new JMenuItem(title);
int modifiers = awtToolkit.getMenuShortcutKeyMask();
modifiers |= ActionEvent.SHIFT_MASK;
menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers));
return menuItem;
}
/**
* Like newJMenuItem() but adds shift as a modifier for the shortcut.
*/
static public JMenuItem newJMenuItemShift(Action action, int what) {
JMenuItem menuItem = new JMenuItem(action);
int modifiers = awtToolkit.getMenuShortcutKeyMask();
modifiers |= ActionEvent.SHIFT_MASK;
menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers));
return menuItem;
}
/**
* Same as newJMenuItem(), but adds the ALT (on Linux and Windows)
* or OPTION (on Mac OS X) key as a modifier. This function should almost
* never be used, because it's bad for non-US keyboards that use ALT in
* strange and wondrous ways.
*/
static public JMenuItem newJMenuItemAlt(String title, int what) {
JMenuItem menuItem = new JMenuItem(title);
menuItem.setAccelerator(KeyStroke.getKeyStroke(what, SHORTCUT_ALT_KEY_MASK));
return menuItem;
}
static public JCheckBoxMenuItem newJCheckBoxMenuItem(String title, int what) {
JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(title);
int modifiers = awtToolkit.getMenuShortcutKeyMask();
menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers));
return menuItem;
}
static public void addDisabledItem(JMenu menu, String title) {
JMenuItem item = new JMenuItem(title);
item.setEnabled(false);
menu.add(item);
}
/**
* Removes all mnemonics, then sets a mnemonic for each menu and menu item
* recursively by these rules:
*
*
Failing that, it loops through the first letter of each word, where a word
* is a block of Unicode "alphabetical" chars, looking for an upper-case ASCII mnemonic
* that is not taken. This is to try to be relevant, by using a letter well-associated
* with the command. (MS guidelines)
*
Ditto, but with lowercase.
*
Next, it tries the second ASCII character, if its width >= half the width of
* 'A'.
*
If the first letters are all taken/non-ASCII, then it loops through the
* ASCII letters in the item, widest to narrowest, seeing if any of them is not taken.
* To improve readability, it discriminates against decenders (qypgj), imagining they
* have 2/3 their actual width. (MS guidelines: avoid decenders). It also discriminates
* against vowels, imagining they have 2/3 their actual width. (MS and Gnome guidelines:
* avoid vowels.)
*
Failing that, it will loop left-to-right for an available digit. This is a last
* resort because the normal setMnemonic dislikes them.
*
If that doesn't work, it doesn't assign a mnemonic.