[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/dimr/processing/master/app/src/processing/app/ui/Toolkit.java [Back]  [Original]

/* -*- 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: *
    *
  1. It tries to assign one of * KDE's defaults.
  2. *
  3. 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)
  4. *
  5. Ditto, but with lowercase.
  6. *
  7. Next, it tries the second ASCII character, if its width >= half the width of * 'A'.
  8. *
  9. 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.)
  10. *
  11. Failing that, it will loop left-to-right for an available digit. This is a last * resort because the normal setMnemonic dislikes them.
  12. *
  13. If that doesn't work, it doesn't assign a mnemonic.
  14. *
* * As a special case, strings starting "sketchbook \u2192 " have that bit ignored * because otherwise the Recent menu looks awful. However, the name "sketchbook \u2192 * Sketch", for example, will have the 'S' of "Sketch" chosen, but the 's' of 'sketchbook * will get underlined. * No letter by an underscore will be assigned. * Disabled on Mac, per Apple guidelines. * menu may contain nulls. * * Author: George Bateman. Initial work Myer Nore. * @param menu * A menu, a list of menus or an array of menu items to set mnemonics for. */ static public void setMenuMnemonics(JMenuItem... menu) { if (Platform.isMacOS()) return; if (menu.length == 0) return; // The English is http://techbase.kde.org/Projects/Usability/HIG/Keyboard_Accelerators, // made lowercase. // Nothing but [a-z] except for '&' before mnemonics and regexes for changable text. final String[] kdePreDefStrs = { "&file", "&new", "&open", "open&recent", "&save", "save&as", "saveacop&y", "saveas&template", "savea&ll", "reloa&d", "&print", "printpre&view", "&import", "e&xport", "&closefile", "clos&eallfiles", "&quit", "&edit", "&undo", "re&do", "cu&t", "©", "&paste", "&delete", "select&all", "dese&lect", "&find", "find&next", "findpre&vious", "&replace", "&gotoline", "&view", "&newview", "close&allviews", "&splitview", "&removeview", "splitter&orientation", "&horizontal", "&vertical", "view&mode", "&fullscreenmode", "&zoom", "zoom&in", "zoom&out", "zoomtopage&width", "zoomwhole&page", "zoom&factor", "&insert", "&format", "&go", "&up", "&back", "&forward", "&home", "&go", "&previouspage", "&nextpage", "&firstpage", "&lastpage", "read&updocument", "read&downdocument", "&back", "&forward", "&gotopage", "&bookmarks", "&addbookmark", "bookmark&tabsasfolder", "&editbookmarks", "&newbookmarksfolder", "&tools", "&settings", "&toolbars", "configure&shortcuts", "configuretool&bars", "&configure.*", "&help", ".+&handbook", "&whatsthis", "report&bug", "&aboutprocessing", "about&kde", "&beenden", "&suchen", // de "&preferncias", "&sair", // Preferncias; pt "&rechercher" }; // fr Pattern[] kdePreDefPats = new Pattern[kdePreDefStrs.length]; for (int i = 0; i < kdePreDefStrs.length; i++) { kdePreDefPats[i] = Pattern.compile(kdePreDefStrs[i].replace("&","")); } final Pattern nonAAlpha = Pattern.compile("[^A-Za-z]"); FontMetrics fmTmp = null; for (JMenuItem m : menu) { if (m != null) { fmTmp = m.getFontMetrics(m.getFont()); break; } } if (fmTmp == null) return; // All null menuitems; would fail. final FontMetrics fm = fmTmp; // Hack for accessing variable in comparator. final Comparator charComparator = new Comparator() { char[] baddies = "qypgjaeiouQAEIOU".toCharArray(); public int compare(Character ch1, Character ch2) { // Discriminates against descenders for readability, per MS // Human Interface Guide, and vowels per MS and Gnome. float w1 = fm.charWidth(ch1), w2 = fm.charWidth(ch2); for (char bad : baddies) { if (bad == ch1) w1 *= 0.66f; if (bad == ch2) w2 *= 0.66f; } return (int)Math.signum(w2 - w1); } }; // Holds only [0-9a-z], not uppercase. // Prevents X != x, so "Save" and "Save As" aren't both given 'a'. final List taken = new ArrayList(menu.length); char firstChar; char[] cleanChars; Character[] cleanCharas; // METHOD 1: attempt to assign KDE defaults. for (JMenuItem jmi : menu) { if (jmi == null) continue; if (jmi.getText() == null) continue; jmi.setMnemonic(0); // Reset all mnemonics. String asciiName = nonAAlpha.matcher(jmi.getText()).replaceAll(""); String lAsciiName = asciiName.toLowerCase(); for (int i = 0; i < kdePreDefStrs.length; i++) { if (kdePreDefPats[i].matcher(lAsciiName).matches()) { char mnem = asciiName.charAt(kdePreDefStrs[i].indexOf("&")); jmi.setMnemonic(mnem); jmi.setDisplayedMnemonicIndex(jmi.getText().indexOf(mnem)); taken.add((char)(mnem | 32)); // to lowercase break; } } } // Where KDE defaults fail, use an algorithm. algorithmicAssignment: for (JMenuItem jmi : menu) { if (jmi == null) continue; if (jmi.getText() == null) continue; if (jmi.getMnemonic() != 0) continue; // Already assigned. // The string can't be made lower-case as that would spoil // the width comparison. String cleanString = jmi.getText(); if (cleanString.startsWith("sketchbook \u2192 ")) cleanString = cleanString.substring(13); if (cleanString.length() == 0) continue; // First, ban letters by underscores. final List banned = new ArrayList(); for (int i = 0; i < cleanString.length(); i++) { if (cleanString.charAt(i) == '_') { if (i > 0) banned.add(Character.toLowerCase(cleanString.charAt(i - 1))); if (i + 1 < cleanString.length()) banned.add(Character.toLowerCase(cleanString.charAt(i + 1))); } } // METHOD 2: Uppercase starts of words. // Splitting into blocks of ASCII letters wouldn't work // because there could be non-ASCII letters in a word. for (String wd : cleanString.split("[^\\p{IsAlphabetic}]")) { if (wd.length() == 0) continue; firstChar = wd.charAt(0); if (taken.contains(Character.toLowerCase(firstChar))) continue; if (banned.contains(Character.toLowerCase(firstChar))) continue; if ('A'
Web Proxy Viewer  |  New URL  |  Original Page