/* * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package org.flexdock.util; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; import java.awt.image.BufferedImage; import javax.swing.JComponent; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; import org.flexdock.docking.DockingPort; import org.flexdock.docking.defaults.DefaultDockingPort; import org.flexdock.docking.defaults.DockingSplitPane; import com.l2fprod.gui.plaf.skin.Skin; import com.l2fprod.gui.plaf.skin.SkinLookAndFeel; /** * @author Christopher Butler */ public class SwingUtility { public static Component toComponent(Object obj) { return obj instanceof Component? (Component)obj: null; } public static void revalidate(Component comp) { if (comp instanceof JComponent) { comp.revalidate(); } } public static void repaint(Component comp) { if(comp instanceof JComponent) { comp.repaint(); } } public static void drawRect(Graphics g, Rectangle r) { if(g==null || r==null) { return; } g.drawRect(r.x, r.y, r.width, r.height); } public static DockingPort[] getChildPorts(DockingPort port) { if(!(port instanceof DefaultDockingPort)) { return new DockingPort[0]; } DefaultDockingPort parent = (DefaultDockingPort)port; Component docked = parent.getDockedComponent(); if(!(docked instanceof JSplitPane)) { return new DockingPort[0]; } JSplitPane split = (JSplitPane)docked; DockingPort left = null; DockingPort right = null; if(split.getLeftComponent() instanceof DockingPort) { left = (DockingPort)split.getLeftComponent(); } if(split.getRightComponent() instanceof DockingPort) { right = (DockingPort)split.getRightComponent(); } if(left==null && right==null) { return new DockingPort[0]; } if(left==null) { return new DockingPort[] {right}; } if(right==null) { return new DockingPort[] {left}; } return new DockingPort[] {left, right}; } public static Point[] getPoints(Rectangle rect) { return getPoints(rect, null); } public static Point[] getPoints(Rectangle rect, Component convertFromScreen) { if(rect==null) { return null; } Rectangle r = (Rectangle)rect.clone(); Point p = r.getLocation(); if(convertFromScreen!=null) { SwingUtilities.convertPointFromScreen(p, convertFromScreen); } r.setLocation(p); return new Point[] { p, new Point(p.x + r.width, p.y), new Point(p.x + r.width, p.y+r.height), new Point(p.x, p.y+r.height) }; } public static final void centerOnScreen(Window window) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension windowSize = window.getSize(); if (windowSize.height > screenSize.height) { windowSize.height = screenSize.height; } if (windowSize.width > screenSize.width) { windowSize.width = screenSize.width; } window.setLocation((screenSize.width - windowSize.width) / 2, (screenSize.height - windowSize.height) / 2); } public static void center(Window window, Component parent) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Rectangle bounds = new Rectangle(parent.getLocationOnScreen(), parent.getSize()); int w = window.getWidth(); int h = window.getHeight(); // center according to parent int x = ((int) bounds.getCenterX()) - w / 2; int y = ((int) bounds.getCenterY()) - h / 2; // does it fit on screen? if (x < 0) { x = 0; } else if (x + w > screenSize.getWidth()) { x = ((int) screenSize.getWidth()) - w; } if (y < 0) { y = 0; } else if (y + h > screenSize.getHeight()) { y = ((int) screenSize.getHeight()) - h; } // done window.setBounds(x, y, w, h); } public static Container getContentPane(Component c) { RootWindow rootWin = RootWindow.getRootContainer(c); return rootWin==null? null: rootWin.getContentPane(); } public static void setPlaf(Class lookAndFeelClass) { String className = lookAndFeelClass==null? null: lookAndFeelClass.getName(); setPlaf(className); } public static void setPlaf(String lookAndFeelClassName) { if(loadSkinLF(lookAndFeelClassName)) { return; } try { UIManager.setLookAndFeel(lookAndFeelClassName); } catch(Exception e) { System.err.println("Exception: " +e.getMessage()); e.printStackTrace(); } } private static boolean loadSkinLF(String themePack) { if(themePack==null || !themePack.endsWith(".zip") || !isSkinLFInstalled()) { return false; } try { Skin skin = SkinLookAndFeel.loadThemePack(ResourceManager.getResource(themePack)); SkinLookAndFeel.setSkin(skin); UIManager.setLookAndFeel(SkinLookAndFeel.class.getName()); return true; } catch(Exception e) { System.err.println("Exception: " +e.getMessage()); e.printStackTrace(); return false; } } public static void add(Point p1, Point p2) { if(p1!=null && p2!=null) { p1.x += p2.x; p1.y += p2.y; } } public static void subtract(Point p1, Point p2) { if(p1!=null && p2!=null) { p1.x -= p2.x; p1.y -= p2.y; } } public static void translate(Component src, Polygon poly, Component dest) { if(src==null || poly==null || dest==null) { return; } Rectangle srcRect = src.getBounds(); srcRect.setLocation(0, 0); Rectangle destRect = SwingUtilities.convertRectangle(src, srcRect, dest); int deltaX = destRect.x - srcRect.x; int deltaY = destRect.y - srcRect.y; int len = poly.npoints; for(int i=0; i<len; i++) { poly.xpoints[i] += deltaX; poly.ypoints[i] += deltaY; } } public static void focus(final Component c) { RootWindow window = RootWindow.getRootContainer(c); if(window==null) { return; } Component root = window.getRootContainer(); Component comp = c; for(Component parent=comp.getParent(); parent!=root; parent=comp.getParent()) { if(parent instanceof JTabbedPane) { ((JTabbedPane)parent).setSelectedComponent(comp); } comp = parent; } EventQueue.invokeLater(new Runnable() { @Override public void run() { c.requestFocus(); } }); } public static Component getNearestFocusableComponent(Component c) { return getNearestFocusableComponent(c, null); } public static Component getNearestFocusableComponent(Component c, Container desiredRoot) { if(c==null) { c = desiredRoot; } if(c==null) { c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); } boolean cachedFocusCycleRoot = false; // make the desiredRoot into a focusCycleRoot if(desiredRoot!=null) { cachedFocusCycleRoot = desiredRoot.isFocusCycleRoot(); if(!cachedFocusCycleRoot) { desiredRoot.setFocusCycleRoot(true); } } Container focusRoot = null; if(c instanceof Container) { Container cnt = (Container)c; focusRoot = cnt.isFocusCycleRoot(cnt)? cnt: cnt.getFocusCycleRootAncestor(); } else { focusRoot = c.getFocusCycleRootAncestor(); } Component focuser = null; if (focusRoot != null) { //zw, remarked - selected component should become focused //focuser = focusRoot.getFocusTraversalPolicy().getLastComponent(focusRoot); focuser = c; //zw, added - selected component should become focused } // restore the desiredRoot to its previous state if(desiredRoot!=null && !cachedFocusCycleRoot) { desiredRoot.setFocusCycleRoot(cachedFocusCycleRoot); } return focuser; } public static void activateWindow(Component c) { RootWindow window = RootWindow.getRootContainer(c); if(window!=null && !window.isActive()) { window.toFront(); } } /* Color utility */ /** * @param color * @param factor * @return a new color, brighter than the one passed as argument by a percentage factor * * <br>author Cyril Gambis - [Mar 17, 2005] */ public static Color brighter(Color color, double factor) { int red = color.getRed(); int green = color.getGreen(); int blue = color.getBlue(); int i = (int) (1.0 / (1.0 - factor)); if (red == 0 && green == 0 && blue == 0) { return new Color(i, i, i); } if (red > 0 && red < i) { red = i; } if (green > 0 && green < i) { green = i; } if (blue > 0 && blue < i) { blue = i; } return new Color(Math.min((int) (red / factor), 255), Math.min((int) (green / factor), 255), Math.min((int) (blue / factor), 255)); } /** * @param color * @param factor * @return a new color, darker than the one passed as argument by a percentage factor * * <br>author Cyril Gambis - [Mar 17, 2005] */ public static Color darker(Color color, double factor) { return new Color(Math.max((int) (color.getRed() * factor), 0), Math.max((int) (color.getGreen() * factor), 0), Math.max((int) (color.getBlue() * factor), 0)); } /** * @param color * @return the grey color corresponding to the color passed as parameter * * <br>author Cyril Gambis - [Mar 17, 2005] */ public static Color grayScale(Color color) { int grayTone = ((color.getRed() + color.getGreen() + color.getBlue())/3); return new Color(grayTone, grayTone, grayTone); } public static BufferedImage createImage(Component comp) { if(comp==null) { return null; } BufferedImage image = (BufferedImage)comp.createImage(comp.getWidth(), comp.getHeight()); Graphics g = image.createGraphics(); comp.paintAll(g); return image; } public static float getDividerProportion(JSplitPane splitPane) { if(splitPane==null) { return 0; } int size = splitPane.getOrientation()==JSplitPane.HORIZONTAL_SPLIT? splitPane.getWidth(): splitPane.getHeight(); int divLoc = splitPane.getDividerLocation(); return size==0? 0: divLoc/((float)size - splitPane.getDividerSize()); } public static Component getOtherComponent(JSplitPane split, Component current) { if(split==null || current==null) { return null; } Component other = split.getLeftComponent(); if(other==current) { other = split.getRightComponent(); } return other; } public static void putClientProperty(Component c, Object key, Object value) { if(c instanceof JComponent) { ((JComponent)c).putClientProperty(key, value); } } public static Object getClientProperty(Component c, Object key) { if(c instanceof JComponent) { return ((JComponent)c).getClientProperty(key); } return null; } public static void removeClientProperty(Component c, Object key) { if(c instanceof JComponent) { ((JComponent)c).putClientProperty(key, null); } } public static Window getActiveWindow() { KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager(); return mgr.getActiveWindow(); } public static int getSplitPaneSize(JSplitPane splitPane) { if(splitPane==null) { return 0; } boolean horiz = splitPane.getOrientation()==JSplitPane.HORIZONTAL_SPLIT? true: false; return horiz? splitPane.getWidth(): splitPane.getHeight(); } /** * Moves the supplied <code>JSplitPane</code> divider to the specified <code>proportion</code>. * Valid values for <code>proportion</code> range from <code>0.0F<code> * to <code>1.0F</code>. For example, a <code>proportion</code> of <code>0.3F</code> will move the * divider to 30% of the "size" (<i>width</i> for horizontal split, <i>height</i> for vertical split) of the * split container that contains the specified <code>Dockable</code>. If a <code>proportion</code> of less * than <code>0.0F</code> is supplied, the value </code>0.0F</code> is used. If a <code>proportion</code> * greater than <code>1.0F</code> is supplied, the value </code>1.0F</code> is used. * <br/> * This method should be effective regardless of whether the split layout in question has been fully realized * and is currently visible on the screen. This should alleviate common problems associated with setting * percentages of unrealized <code>Component</code> dimensions, which are initially <code>0x0</code> before * the <code>Component</code> has been rendered to the screen. * <br/> * If the specified <code>JSplitPane</code> is <code>null</code>, then this method returns with no action * taken. * * @param split the <code>JSplitPane</code> whose divider location is to be set. * @param proportion a double-precision floating point value that specifies a percentage, * from zero (top/left) to 1.0 (bottom/right) * @see #getSplitPaneSize(JSplitPane) * @see JSplitPane#setDividerLocation(double) */ public static void setSplitDivider(final JSplitPane split, float proportion) { if(split==null) { return; } proportion = Math.max(0f, proportion); final float percent = Math.min(1f, proportion); int size = getSplitPaneSize(split); if(split.isVisible() && size>0 && EventQueue.isDispatchThread()) { split.setDividerLocation(proportion); split.validate(); return; } EventQueue.invokeLater(new Runnable() { @Override public void run() { setSplitDivider(split, percent); } }); } public static boolean isSkinLFInstalled() { return LookAndFeelSettings.isSkinLFSupported(); } public static void toggleFocus(int direction) { KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); Component focused = manager.getFocusOwner(); Component newFocused = getNext(focused, direction); if (newFocused != null) { SwingUtility.focus(newFocused); } } private static Component getNext(Component comp, int direction) { Component next = null; JTabbedPane tab; if (comp instanceof JTabbedPane) { tab = (JTabbedPane) comp; } else { tab = (JTabbedPane) SwingUtilities.getAncestorOfClass(JTabbedPane.class, comp); } if (tab != null) { int index = tab.getSelectedIndex(); if (direction > 0 && index < tab.getTabCount() - 1) { tab.setSelectedIndex(index + 1); next = tab; } else if (direction <= 0 && index > 0) { tab.setSelectedIndex(index - 1); next = tab; } else { DefaultDockingPort port = (DefaultDockingPort) SwingUtilities.getAncestorOfClass(DefaultDockingPort.class, tab); next = getNext(port, direction); } } else { DockingSplitPane pane = (DockingSplitPane) SwingUtilities.getAncestorOfClass(DockingSplitPane.class, comp); if (pane == null) { return getFirstComponent(comp, direction); } Component left = pane.getLeftComponent(); Component right = pane.getRightComponent(); if (SwingUtilities.isDescendingFrom(comp, left)) { if (direction > 0) { next = getFirstComponent(right, direction); } else { DefaultDockingPort port = (DefaultDockingPort) SwingUtilities.getAncestorOfClass(DefaultDockingPort.class, pane); next = getNext(port, direction); } } else { if (direction > 0) { DefaultDockingPort port = (DefaultDockingPort) SwingUtilities.getAncestorOfClass(DefaultDockingPort.class, pane); next = getNext(port, direction); } else { next = getFirstComponent(left, direction); } } } return next; } private static Component getFirstComponent(Component c, int direction) { while (c instanceof DefaultDockingPort) { c = ((DockingPort) c).getDockedComponent(); if (c instanceof DockingSplitPane) { DockingSplitPane pane = (DockingSplitPane) c; c = direction > 0 ? pane.getLeftComponent() : pane.getRightComponent(); } } return c; } }