/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.plugin.keybindingchooser; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; /** * Common tools for Swing gui components. * @author Damian Johnson (atagar1@gmail.com) * @version September 1, 2007 */ public class ComponentTools { /** * Applies a highly customized visual scheme based on the reference color. Background is of a * gradient hue (similar to the Java 6 default ocean theme) and uses a raised bevel border. If * designated as being pressed then this uses a lowered bevel border and the background darkens, * unless the background is already very dark, in which case it will lighten. Unfortunately Swing * doesn't directly support setting background gradients, requiring that the component's paint * method is overridden. For instance, to apply this to a JButton:
* * JButton button = new JButton(text) { * private static final long serialVersionUID = 0; * * public void paintComponent(Graphics g) { * Graphics2D g2 = (Graphics2D) g; * g2.setPaint(applyThemedScheme(this, reference, getModel().isArmed())); * g2.fillRect(0, 0, getWidth(), getHeight()); // Draws gradient background * super.paintComponent(g); // Draws button content * } * }; * button.setContentAreaFilled(false); // Disables default background * * @param component component to which custom foreground and border is applied, if null then these * attributes aren't applied * @param reference color on which background gradient and border are based * @param isPressed determines if toggled scheme is applied for components that can be pressed * @return component background gradient */ public static GradientPaint applyThemedScheme(JComponent component, Color reference, boolean isPressed) { int r = reference.getRed(); int g = reference.getGreen(); int b = reference.getBlue(); Color lightened = new Color(getValidRgb(r + 75), getValidRgb(g + 75), getValidRgb(b + 75)); Color darkened = new Color(getValidRgb(r - 75), getValidRgb(g - 75), getValidRgb(b - 75)); boolean isVeryDark = (r + g + b) / 3 < 25; // If contrast should be provided by lightening if (isPressed) { if (isVeryDark) { reference = reference.brighter(); lightened = lightened.brighter(); darkened = darkened.brighter(); } else { reference = reference.darker(); lightened = lightened.darker(); darkened = darkened.darker(); } } if (component != null) { int borderType = !isPressed ? BevelBorder.RAISED : BevelBorder.LOWERED; Border border = BorderFactory.createBevelBorder(borderType, lightened, darkened); component.setBorder(border); Color foreground = isVeryDark ? new Color(224, 224, 224) : Color.BLACK; component.setForeground(foreground); } Point p1 = new Point(0, 20); Point p2 = new Point(0, 5); return new GradientPaint(p1, reference, p2, lightened, false); } /** * Provides a visually customized button utilizing the applyThemedScheme method that will update * its theme accordingly when pressed. Any content is anti-aliased. * @param text message displayed by the button * @param reference color on which background gradient and border are based * @return button with a color scheme matching the provided color */ public static JButton makeThemedButton(String text, final Color reference) { JButton button = new JButton(text) { private static final long serialVersionUID = 0; @Override public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setPaint(applyThemedScheme(this, reference, getModel().isArmed())); g2.fillRect(0, 0, getWidth(), getHeight()); // Draws gradient background super.paintComponent(g); // Draws button content } }; button.setContentAreaFilled(false); // Disables default background return button; } /** * Centers a window within the center of the screen. * @param mover window to be centered */ public static void center(Window mover) { Dimension moverSize = mover.getPreferredSize(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int x = (screenSize.width - moverSize.width) / 2; int y = (screenSize.height - moverSize.height) / 2; mover.setLocation(x, y); } /** * Moves a window to be concentric with another. * @param mover window to be centered * @param target to be centered within */ public static void center(Window mover, Component target) { Dimension moverSize = mover.getSize(); Dimension targetSize = target.getSize(); int x = (targetSize.width - moverSize.width) / 2; x += target.getLocation().x; int y = (targetSize.height - moverSize.height) / 2; y += target.getLocation().y; mover.setLocation(x, y); } /** * Binds a given keystroke to click the button when the button's in the focused window. * @param button button to be bound to keystroke * @param event type of keyboard event that triggers the button */ public static void setKeyBinding(JButton button, KeyStroke event) { String eventName = "do" + button.getText(); button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(event, eventName); button.getActionMap().put(eventName, new AbstractAction() { private static final long serialVersionUID = 0; public void actionPerformed(ActionEvent event) { ((JButton) event.getSource()).doClick(); } }); } /** * Generates a modal dialog capable of having either a Frame or Dialog parent. * @param parent the parent component of the dialog * @param title title of dialog * @return dialog with the specified parent */ public static JDialog makeDialog(Component parent, String title) { /* * The method of doing this was exemplified by the source of the GCJ JColorChooser (with some * changes to handle what I suspect is a bug in order to handle subclasses). */ parent = findParent(parent); if (parent == null) throw new AWTError("No suitable parent found for Component."); else if (parent instanceof Frame) return new JDialog((Frame) parent, title, true); else return new JDialog((Dialog) parent, title, true); } /** * This is a helper method to recursively find the first instance of a Frame or Dialog within the * component's hierarchy. * @param comp The component in which to check for Frame or Dialog * @return Frame or Dialog ancestor, null if none is found */ private static Component findParent(Component comp) { if (comp instanceof Frame || comp instanceof Dialog) return comp; Component parent = comp.getParent(); if (parent != null) return findParent(parent); else return null; } // Provides an rgb value within valid bounds (0-255). private static int getValidRgb(int value) { value = Math.max(value, 0); value = Math.min(value, 255); return value; } }