/* * GenericGUIUtilities.java - Various GUI utility functions * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 1999, 2004 Slava Pestov * Copyright (C) 2016 Eric Le Lay (move from GUIUtilities.java) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * 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 org.gjt.sp.util; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Insets; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.AbstractButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import org.gjt.sp.jedit.OperatingSystem; import org.gjt.sp.jedit.textarea.TextAreaMouseHandler; /** Various GUI utility functions not depending on jEdit, for use in StandaloneTextArea. * * * @author Slava Pestov * @author Eric Le Lay * @version $Id$ */ public class GenericGUIUtilities { //{{{ prettifyMenuLabel() method /** * `Prettifies' a menu item label by removing the `$' sign. This * can be used to process the contents of an <i>action</i>.label * property. * @param label the label * @return a pretty label * @since jEdit 5.3.1 */ public static String prettifyMenuLabel(String label) { int index = label.indexOf('$'); if(index != -1) { label = label.substring(0,index) .concat(label.substring(index + 1)); } return label; } //}}} //{{{ setAutoMnemonic() method /** * Sets the mnemonic for the given button using jEdit convention, * taking the letter after the dollar. * @param button The button to set the mnemonic for. * @since jEdit 5.3.1 */ public static void setAutoMnemonic(AbstractButton button) { String label = button.getText(); char mnemonic; int index = label.indexOf('$'); if (index != -1 && label.length() - index > 1) { mnemonic = Character.toLowerCase(label.charAt(index + 1)); label = label.substring(0, index).concat(label.substring(++index)); } else { mnemonic = '\0'; } if (mnemonic != '\0') { button.setMnemonic(mnemonic); button.setText(label); } } //}}} //{{{ adjustForScreenBounds() method /** * Gives a rectangle the specified bounds, ensuring it is within the * screen bounds. * @since jEdit 5.3.1 */ public static void adjustForScreenBounds(Rectangle desired) { // Make sure the window is displayed in visible region Rectangle osbounds = OperatingSystem.getScreenBounds(desired); if (desired.width > osbounds.width) { desired.width = osbounds.width; } if (desired.x < osbounds.x) { desired.x = osbounds.x; } if (desired.x + desired.width > osbounds.x + osbounds.width) { desired.x = osbounds.x + osbounds.width - desired.width; } if (desired.height > osbounds.height) { desired.height = osbounds.height; } if (desired.y < osbounds.y) { desired.y = osbounds.y; } if (desired.y + desired.height > osbounds.y + osbounds.height) { desired.y = osbounds.y + osbounds.height - desired.height; } } //}}} //{{{ requestFocus() method /** * Focuses on the specified component as soon as the window becomes * active. * @param win The window * @param comp The component * @since jEdit 5.3.1 */ public static void requestFocus(final Window win, final Component comp) { win.addWindowFocusListener(new WindowAdapter() { @Override public void windowGainedFocus(WindowEvent evt) { EventQueue.invokeLater(new Runnable() { @Override public void run() { comp.requestFocusInWindow(); } }); win.removeWindowFocusListener(this); } }); } //}}} //{{{ isPopupTrigger() method /** * Returns if the specified event is the popup trigger event. * This implements precisely defined behavior, as opposed to * MouseEvent.isPopupTrigger(). * @param evt The event * @since jEdit 5.3.1 */ public static boolean isPopupTrigger(MouseEvent evt) { return TextAreaMouseHandler.isRightButton(evt.getModifiers()); } //}}} //{{{ isMiddleButton() method /** * @param modifiers The modifiers flag from a mouse event * @since jEdit 5.3.1 */ public static boolean isMiddleButton(int modifiers) { return TextAreaMouseHandler.isMiddleButton(modifiers); } //}}} //{{{ isRightButton() method /** * @param modifiers The modifiers flag from a mouse event * @since jEdit 5.3.1 */ public static boolean isRightButton(int modifiers) { return TextAreaMouseHandler.isRightButton(modifiers); } //}}} //{{{ getScreenBounds() method /** * Returns the screen bounds, taking into account multi-screen * environments. * @since jEdit 5.3.1 */ public static Rectangle getScreenBounds() { Rectangle bounds = GraphicsEnvironment.getLocalGraphicsEnvironment(). getMaximumWindowBounds(); GraphicsDevice [] devices = GraphicsEnvironment. getLocalGraphicsEnvironment().getScreenDevices(); if (devices.length > 1) { for (GraphicsDevice device: devices) { for (GraphicsConfiguration config: device.getConfigurations()) bounds = bounds.union(config.getBounds()); } } return bounds; } //}}} //{{{ showPopupMenu() method /** * Shows the specified popup menu, ensuring it is displayed within * the bounds of the screen. * @param popup The popup menu * @param comp The component to show it for * @param x The x co-ordinate * @param y The y co-ordinate * @since jEdit 4.0pre1 * @see javax.swing.JComponent#setComponentPopupMenu(javax.swing.JPopupMenu) setComponentPopupMenu * which works better and is simpler to use: you don't have to write the code to * show/hide popups in response to mouse events anymore. * @since jEdit 5.3.1 */ public static void showPopupMenu(JPopupMenu popup, Component comp, int x, int y) { showPopupMenu(popup,comp,x,y,true); } //}}} //{{{ showPopupMenu() method /** * Shows the specified popup menu, ensuring it is displayed within * the bounds of the screen. * @param popup The popup menu * @param comp The component to show it for * @param x The x co-ordinate * @param y The y co-ordinate * @param point If true, then the popup originates from a single point; * otherwise it will originate from the component itself. This affects * positioning in the case where the popup does not fit onscreen. * * @since jEdit 5.3.1 */ public static void showPopupMenu(JPopupMenu popup, Component comp, int x, int y, boolean point) { int offsetX = 0; int offsetY = 0; int extraOffset = point ? 1 : 0; Component win = comp; while(!(win instanceof Window || win == null)) { offsetX += win.getX(); offsetY += win.getY(); win = win.getParent(); } if(win != null) { Dimension size = popup.getPreferredSize(); Rectangle screenSize = getScreenBounds(); if(x + offsetX + size.width + win.getX() > screenSize.width && x + offsetX + win.getX() >= size.width) { //System.err.println("x overflow"); if(point) x -= size.width + extraOffset; else x = win.getWidth() - size.width - offsetX + extraOffset; } else { x += extraOffset; } //System.err.println("y=" + y + ",offsetY=" + offsetY // + ",size.height=" + size.height // + ",win.height=" + win.getHeight()); if(y + offsetY + size.height + win.getY() > screenSize.height && y + offsetY + win.getY() >= size.height) { if(point) y = win.getHeight() - size.height - offsetY + extraOffset; else y = -size.height - 1; } else { y += extraOffset; } popup.show(comp,x,y); } else popup.show(comp,x + extraOffset,y + extraOffset); } //}}} //{{{ isAncestorOf() method /** * Returns if the first component is an ancestor of the * second by traversing up the component hierarchy. * * @param comp1 The ancestor * @param comp2 The component to check * @since jEdit 5.3.1 */ public static boolean isAncestorOf(Component comp1, Component comp2) { while(comp2 != null) { if(comp1 == comp2) return true; else comp2 = comp2.getParent(); } return false; } //}}} //{{{ getParentDialog() method /** * Traverses the given component's parent tree looking for an * instance of JDialog, and return it. If not found, return null. * @param c The component * @since jEdit 5.3.1 */ public static JDialog getParentDialog(Component c) { return (JDialog) SwingUtilities.getAncestorOfClass(JDialog.class, c); } //}}} //{{{ setEnabledRecursively() method /** * Call setEnabled() recursively on the container and its descendants. * @param c The container * @param enabled The enabled state to set * @since jEdit 5.3.1 */ public static void setEnabledRecursively(Container c, boolean enabled) { for (Component child: c.getComponents()) { if (child instanceof Container) setEnabledRecursively((Container)child, enabled); else child.setEnabled(enabled); } c.setEnabled(enabled); } //}}} //{{{ setButtonContentMargin() method /** * Sets the content margin of a button (for Nimbus L&F). * @param button the button to modify * @param margin the new margin * @since jEdit 5.3.1 */ public static void setButtonContentMargin(AbstractButton button, Insets margin) { UIDefaults defaults = new UIDefaults(); defaults.put("Button.contentMargins", margin); defaults.put("ToggleButton.contentMargins", margin); button.putClientProperty("Nimbus.Overrides", defaults); } //}}} //{{{ /** * Makes components the same size by finding the largest width and height of the * given components then setting all components to that width and height. This is * especially useful for making JButtons the same size. * @param components The components to make the same size. * @since jEdit 5.3.1 */ public static void makeSameSize(Component... components) { if (components == null) return; int width = 0; int height = 0; for (Component component : components) { if (component == null) continue; width = Math.max(width, component.getPreferredSize().width); height = Math.max(height, component.getPreferredSize().height); } Dimension d = new Dimension(width, height); for (Component component : components) { if (component == null) continue; component.setPreferredSize(d); } } //}}} //{{{ defaultTableDimension() method /** * JTable cell size, based on global defaults. * @since jEdit 5.3.1 */ public static Dimension defaultTableCellSize() { JLabel label = new JLabel("miniminiminiminiminiminiminiminiminimini"); UIDefaults defaults = UIManager.getDefaults(); Object font = defaults.get("Table.font"); if (font instanceof Font) label.setFont((Font)font); return label.getPreferredSize(); } //}}} //{{{ defaultColumnWidth() method /** * Column width for JTable, based on global defaults. * @since jEdit 5.3.1 */ public static int defaultColumnWidth() { return defaultTableCellSize().width; } //}}} //{{{ defaultRowHeight() method /** * Row height for JTable, based on global defaults. * @since jEdit 5.3.1 */ public static int defaultRowHeight() { return defaultTableCellSize().height; } //}}} }