/* * Freeplane - mind map editor * Copyright (C) 2008 Dimitry Polivaev * * This file author is Dimitry Polivaev * * 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 * (at your option) 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, see <http://www.gnu.org/licenses/>. */ package org.freeplane.core.ui.components; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.Frame; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.net.URI; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.TitledBorder; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; import javax.swing.text.JTextComponent; import org.freeplane.core.resources.IFreeplanePropertyListener; import org.freeplane.core.resources.ResourceController; import org.freeplane.core.util.LogUtils; import org.freeplane.core.util.TextUtils; import org.freeplane.features.map.NodeModel; import org.freeplane.features.mode.Controller; import org.freeplane.features.mode.ModeController; import org.freeplane.features.ui.ViewController; import org.freeplane.features.url.UrlManager; import org.freeplane.main.application.FreeplaneSplashModern; /** * Utilities for accessing the GUI, creating dialogs etc.: In scripts available as "global variable" <code>ui</code>. * <p> * In scripts this would be a simple way of opening a info popup: * <pre> * ui.informationMessage("Hello World!") * ui.informationMessage(ui.frame, "Hello World!") // longer version, equivalent * </pre> * * @author Dimitry Polivaev * @since 29.12.2008 */ public class UITools { @SuppressWarnings("serial") public static final class InsertEolAction extends AbstractAction { public void actionPerformed(ActionEvent e) { JTextComponent c = (JTextComponent) e.getSource(); c.replaceSelection("\n"); } } public static final String MAIN_FREEPLANE_FRAME = "mainFreeplaneFrame"; public static void addEscapeActionToDialog(final JDialog dialog) { class EscapeAction extends AbstractAction { private static final long serialVersionUID = 1L; public void actionPerformed(final ActionEvent e) { dialog.dispose(); }; } UITools.addEscapeActionToDialog(dialog, new EscapeAction()); } public static void addEscapeActionToDialog(final JDialog dialog, final Action action) { UITools.addKeyActionToDialog(dialog, action, "ESCAPE", "end_dialog"); } public static void addKeyActionToDialog(final JDialog dialog, final Action action, final String keyStroke, final String actionId) { action.putValue(Action.NAME, actionId); dialog.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(keyStroke), action.getValue(Action.NAME)); dialog.getRootPane().getActionMap().put(action.getValue(Action.NAME), action); } public static void convertPointFromAncestor(final Component ancestor, final Point p, Component c) { int x, y; while (c != ancestor && c != null) { x = c.getX(); y = c.getY(); p.x -= x; p.y -= y; c = c.getParent(); }; } public static void convertPointToAncestor(final Component source, final Point point, final Class<?> ancestorClass) { final Component destination = SwingUtilities.getAncestorOfClass(ancestorClass, source); UITools.convertPointToAncestor(source, point, destination); } public static void convertRectangleToAncestor(final Component from, final Rectangle r, final Component destination) { Point p = new Point(r.x, r.y); UITools.convertPointToAncestor(from, p , destination); r.x = p.x; r.y = p.y; } public static void convertPointToAncestor(final Component from, final Point p, final Component destination) { int x, y; for (Component c = from; c != destination && c != null; c = c.getParent()) { x = c.getX(); y = c.getY(); p.x += x; p.y += y; }; } static public void errorMessage(final Object message) { final String myMessage; if (message != null) { myMessage = message.toString(); } else { myMessage = TextUtils.getText("undefined_error"); } final String title = ResourceController.getResourceController().getProperty("ApplicationName", "Freeplane"); LogUtils.warn(myMessage); EventQueue.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(UITools.getFrame(), myMessage, title, JOptionPane.ERROR_MESSAGE); } }); } static public Frame getFrame() { final Frame[] frames = Frame.getFrames(); for (final Frame frame : frames) { if (MAIN_FREEPLANE_FRAME.equals(frame.getName())) { return frame; } } return frames.length >= 1 ? frames[0] : null; } /** returns a KeyStroke if possible and null otherwise. */ public static KeyStroke getKeyStroke(final String keyStrokeDescription) { if (keyStrokeDescription == null) { return null; } final KeyStroke keyStroke = KeyStroke.getKeyStroke(keyStrokeDescription); if (keyStroke != null) { return keyStroke; } final int lastSpacePos = keyStrokeDescription.lastIndexOf(' ') + 1; final String modifiedDescription = keyStrokeDescription.substring(0, lastSpacePos) + "typed " + keyStrokeDescription.substring(lastSpacePos); return KeyStroke.getKeyStroke(modifiedDescription); } /** formats a KeyStroke in a ledgible way, e.g. Control+V. Null is converted to "". * Taken from MotifGraphicsUtils.paintMenuItem(). */ public static String keyStrokeToString(KeyStroke keyStroke) { String acceleratorText = ""; if (keyStroke != null) { int modifiers = keyStroke.getModifiers(); if (modifiers > 0) { acceleratorText = KeyEvent.getKeyModifiersText(modifiers); acceleratorText += "+"; } acceleratorText += KeyEvent.getKeyText(keyStroke.getKeyCode()); } return acceleratorText; } static public void informationMessage(final String message) { UITools.informationMessage(UITools.getFrame(), message); } static public void informationMessage(final Frame frame, final String message) { final String title = ResourceController.getResourceController().getProperty("ApplicationName", "Freeplane"); UITools.informationMessage(frame, message, title); } static public void informationMessage(final Frame frame, final String message, final String title) { JOptionPane.showMessageDialog(frame, message, title, JOptionPane.INFORMATION_MESSAGE); } public static void informationMessage(final Frame frame, final String text, final String string, final int type) { JOptionPane.showMessageDialog(frame, text, string, type); } static public void setBounds(final Component frame, int win_x, int win_y, int win_width, int win_height) { final Rectangle desktopBounds = getDesktopBounds(frame); int screenWidth = desktopBounds.width; if(win_width != -1) win_width = Math.min(win_width, screenWidth ); else win_width = screenWidth * 4 / 5; int screenHeight = desktopBounds.height; if(win_height != -1) win_height = Math.min(win_height, screenHeight); else win_height = screenHeight * 4 / 5; if(win_x != -1){ win_x = Math.min(screenWidth + desktopBounds.x - win_width, win_x); win_x = Math.max(desktopBounds.x, win_x); } else win_x = desktopBounds.x + (screenWidth - win_width) / 2; if(win_y != -1){ win_y = Math.max(desktopBounds.y, win_y); win_y = Math.min(screenHeight + desktopBounds.y - win_height, win_y); } else win_y = desktopBounds.y + (screenHeight - win_height) / 2; frame.setBounds(win_x, win_y, win_width, win_height); } public static Rectangle getDesktopBounds(Component frame) { final Toolkit defaultToolkit = Toolkit.getDefaultToolkit(); final Insets screenInsets = defaultToolkit.getScreenInsets(frame.getGraphicsConfiguration()); final Dimension screenSize = defaultToolkit.getScreenSize(); final int screenWidth = screenSize.width - screenInsets.left - screenInsets.right; final int screenHeight = screenSize.height - screenInsets.top - screenInsets.bottom; return new Rectangle(screenInsets.left, screenInsets.top, screenWidth, screenHeight); } public static void setDialogLocationRelativeTo(final JDialog dialog, final Component c) { if (c == null || ! c.isShowing()) { return; } final Point compLocation = c.getLocationOnScreen(); final int cw = c.getWidth(); final int ch = c.getHeight(); final Container parent = dialog.getParent(); final Point parentLocation = parent.getLocationOnScreen(); final int pw = parent.getWidth(); final int ph = parent.getHeight(); final int dw = dialog.getWidth(); final int dh = dialog.getHeight(); final Rectangle desktopBounds = getDesktopBounds(c); final int minX = Math.max(parentLocation.x, desktopBounds.x); final int minY = Math.max(parentLocation.y, desktopBounds.y); final int maxX = Math.min(parentLocation.x + pw, desktopBounds.x + desktopBounds.width); final int maxY = Math.min(parentLocation.y + ph, desktopBounds.y + desktopBounds.height); int dx, dy; if (compLocation.x + cw < minX) { dx = minX; } else if (compLocation.x > maxX) { dx = maxX - dw; } else { final int leftSpace = compLocation.x - minX; final int rightSpace = maxX - (compLocation.x + cw); if (leftSpace > rightSpace) { if (leftSpace > dw) { dx = compLocation.x - dw; } else { dx = minX; } } else { if (rightSpace > dw) { dx = compLocation.x + cw; } else { dx = maxX - dw; } } } if (compLocation.y + ch < minY) { dy = minY; } else if (compLocation.y > maxY) { dy = maxY - dh; } else { final int topSpace = compLocation.y - minY; final int bottomSpace = maxY - (compLocation.y + ch); if (topSpace > bottomSpace) { if (topSpace > dh) { dy = compLocation.y - dh; } else { dy = minY; } } else { if (bottomSpace > dh) { dy = compLocation.y + ch; } else { dy = maxY - dh; } } } dialog.setLocation(dx, dy); } public static void setDialogLocationRelativeTo(final JDialog dialog, final NodeModel node) { if (node == null) { return; } final ViewController viewController = Controller.getCurrentController().getViewController(); viewController.scrollNodeToVisible(node); final Component c = viewController.getComponent(node); UITools.setDialogLocationRelativeTo(dialog, c); } public static void setDialogLocationUnder(final JDialog dialog, final NodeModel node) { final Controller controller = Controller.getCurrentController(); final ViewController viewController = controller.getViewController(); final JComponent c = (JComponent) viewController.getComponent(node); final int x = 0; final int y = c.getHeight(); final Point location = new Point(x, y); SwingUtilities.convertPointToScreen(location, c); UITools.setBounds(dialog, location.x, location.y, dialog.getWidth(), dialog.getHeight()); } /** * Shows the error message "attributes_adding_empty_attribute_error" */ public static void showAttributeEmptyStringErrorMessage() { JOptionPane.showMessageDialog(null, TextUtils.getText("attributes_adding_empty_attribute_error"), TextUtils.getText("error"), JOptionPane.ERROR_MESSAGE); } static public void showMessage(String message, int messageType) { backOtherWindows(); JTextArea infoPane = new JTextArea(); infoPane.setEditable(false); infoPane.setMargin(new Insets(5,5,5,5)); infoPane.setLineWrap(true); infoPane.setWrapStyleWord(true); infoPane.setText(message); infoPane.setColumns(60); JScrollPane scrollPane = new JScrollPane(infoPane); scrollPane.setPreferredSize(new Dimension(400, 200)); JOptionPane.showMessageDialog(getFrame(), scrollPane, ResourceController.getResourceController().getProperty("ApplicationName"), messageType); } public static int showConfirmDialog(final NodeModel node, final Object message, final String title, final int optionType, final int messageType) { final Controller controller = Controller.getCurrentController(); final ViewController viewController = controller.getViewController(); final Component parentComponent; if (node == null) { parentComponent = getFrame(); } else { viewController.scrollNodeToVisible(node); parentComponent = viewController.getComponent(node); } return JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType); } public static int showConfirmDialog( final NodeModel node, final Object message, final String title, final int optionType) { return showConfirmDialog( node, message, title, optionType, JOptionPane.QUESTION_MESSAGE); } public static String showInputDialog( final NodeModel node, final String message, final String initialValue) { if (node == null) { return null; } final Controller controller = Controller.getCurrentController(); final ViewController viewController = controller.getViewController(); viewController.scrollNodeToVisible(node); final Component parentComponent = viewController.getComponent(node); return JOptionPane.showInputDialog(parentComponent, message, initialValue); } public static String showInputDialog( final NodeModel node, final String text, final String title, final int type) { if (node == null) { return null; } final Controller controller = Controller.getCurrentController(); final ViewController viewController = controller.getViewController(); viewController.scrollNodeToVisible(node); final Component parentComponent = viewController.getComponent(node); return JOptionPane.showInputDialog(parentComponent, text, title, type); } private static final String SCROLLBAR_INCREMENT = "scrollbar_increment"; public static void setScrollbarIncrement(final JScrollPane scrollPane) { final int scrollbarIncrement = ResourceController.getResourceController() .getIntProperty(SCROLLBAR_INCREMENT, 1); scrollPane.getHorizontalScrollBar().setUnitIncrement(scrollbarIncrement); scrollPane.getVerticalScrollBar().setUnitIncrement(scrollbarIncrement); } public static void addScrollbarIncrementPropertyListener(final JScrollPane scrollPane) { ResourceController.getResourceController().addPropertyChangeListener(new IFreeplanePropertyListener() { public void propertyChanged(final String propertyName, final String newValue, final String oldValue) { if (!propertyName.equals(SCROLLBAR_INCREMENT)) { return; } final int scrollbarIncrement = Integer.valueOf(newValue); scrollPane.getHorizontalScrollBar().setUnitIncrement(scrollbarIncrement); scrollPane.getVerticalScrollBar().setUnitIncrement(scrollbarIncrement); } }); } public static Color getTextColorForBackground(final Color color) { final int red = color.getRed(); final int blue = color.getBlue(); final int green = color.getGreen(); return red > 0x80 && blue > 0x80 && green > 0x80 ? Color.BLACK : Color.WHITE; } public static final Dimension MAX_BUTTON_DIMENSION = new Dimension(1000, 1000); // FIXME: not used - can we remove it? -- Volker // public static Controller getController(Component c) { // if(c == null){ // return null; // } // final JRootPane rootPane = SwingUtilities.getRootPane(c); // if(rootPane == null){ // return null; // } // Controller controller = (Controller) rootPane.getClientProperty(Controller.class); // if(controller != null){ // return controller; // } // return getController(JOptionPane.getFrameForComponent(rootPane)); // } public static void focusOn(JComponent component) { component.addAncestorListener(new AncestorListener() { public void ancestorRemoved(AncestorEvent event) { } public void ancestorMoved(AncestorEvent event) { } public void ancestorAdded(AncestorEvent event) { final JComponent component = event.getComponent(); EventQueue.invokeLater(new Runnable() { public void run() { component.requestFocus(); } }); component.removeAncestorListener(this); } }); } public static BasicStroke createStroke(int width, final int[] dash) { final float[] fdash; if(dash != null){ fdash = new float[dash.length]; int i = 0; for(float d : dash){ fdash[i++] = d; } } else{ fdash = null; } final BasicStroke stroke = new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1f, fdash, 0f); return stroke; } public static void repaintAll(Container root) { root.repaint(); for(int i = 0; i < root.getComponentCount(); i++){ final Component component = root.getComponent(i); if(component instanceof Container){ repaintAll((Container) component); } else{ component.repaint(); } } } public static JDialog createCancelDialog(final Component component, final String titel, final String text) { final String[] options = { TextUtils.getText("cancel") }; final JOptionPane infoPane = new JOptionPane(text, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options); JDialog dialog = infoPane.createDialog(component, titel); dialog.setModal(false); return dialog; } public static void addTitledBorder(final JComponent c, final String title, final float size) { final TitledBorder titledBorder = BorderFactory.createTitledBorder(title); final Font titleFont = UIManager.getFont("TitledBorder.font"); titledBorder.setTitleFont(titleFont.deriveFont(size)); final Border btnBorder = c.getBorder(); if(btnBorder != null){ final CompoundBorder compoundBorder = BorderFactory.createCompoundBorder(titledBorder, btnBorder); c.setBorder(compoundBorder); } else{ c.setBorder(titledBorder); } } public static void backOtherWindows() { Window owner = getFrame(); if(owner != null){ final Window[] ownedWindows = owner.getOwnedWindows(); for(Window w : ownedWindows){ if(w.isVisible()){ w.toBack(); } } } } public static JButton createHtmlLinkStyleButton(final URI uri, final String title) { final JButton button = new JButton("<html><a href='" + uri + "'>" + title); button.setBorderPainted(false); button.setOpaque(false); button.setBackground(Color.lightGray); button.setFocusable(false); button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { final ModeController modeController = Controller.getCurrentModeController(); final UrlManager urlManager = (UrlManager) modeController.getExtension(UrlManager.class); urlManager.loadURL(uri); } }); return button; } public static final int getComponentIndex(Component component) { if (component != null && component.getParent() != null) { Container c = component.getParent(); for (int i = 0; i < c.getComponentCount(); i++) { if (c.getComponent(i) == component) return i; } } return -1; } public static final float FONT_SCALE_FACTOR; static { float factor = 1f; try { factor = UITools.getScreenResolution() / 72f; } catch (Exception e) { } FONT_SCALE_FACTOR = factor; } public static int getScreenResolution() { final int systemScreenResolution = Toolkit.getDefaultToolkit().getScreenResolution(); if(ResourceController.getResourceController().getBooleanProperty("apply_system_screen_resolution")){ return systemScreenResolution; } else return ResourceController.getResourceController().getIntProperty("user_defined_screen_resolution", systemScreenResolution); } public static Font scale(Font font) { return font.deriveFont(font.getSize2D()*FONT_SCALE_FACTOR); } public static Font invertScale(Font font) { return font.deriveFont(font.getSize2D()/FONT_SCALE_FACTOR); } public static void showFrame() { final Frame frame = UITools.getFrame(); final Window[] ownedWindows = frame.getOwnedWindows(); for (int i = 0; i < ownedWindows.length; i++) { final Window window = ownedWindows[i]; if (window.getClass().equals(FreeplaneSplashModern.class) && window.isVisible()) { window.setVisible(false); } } if(frame != null && ! frame.isVisible()){ frame.setVisible(true); frame.toFront(); } } }