/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.tools; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.GradientPaint; import java.awt.Image; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.logging.Level; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; import com.rapidminer.RapidMiner; import com.rapidminer.gui.MainFrame; import com.rapidminer.gui.RapidMinerGUI; import com.rapidminer.gui.look.fc.Bookmark; import com.rapidminer.gui.look.fc.BookmarkIO; import com.rapidminer.gui.tools.dialogs.ConfirmDialog; import com.rapidminer.gui.tools.dialogs.ErrorDialog; import com.rapidminer.gui.tools.dialogs.ExtendedErrorDialog; import com.rapidminer.gui.tools.dialogs.InputDialog; import com.rapidminer.gui.tools.dialogs.LongMessageDialog; import com.rapidminer.gui.tools.dialogs.MessageDialog; import com.rapidminer.gui.tools.dialogs.ResultViewDialog; import com.rapidminer.gui.tools.dialogs.SelectionInputDialog; import com.rapidminer.gui.tools.syntax.SyntaxStyle; import com.rapidminer.gui.tools.syntax.SyntaxUtilities; import com.rapidminer.gui.tools.syntax.TextAreaDefaults; import com.rapidminer.gui.tools.syntax.Token; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.operator.ports.Port; import com.rapidminer.operator.ports.Ports; import com.rapidminer.tools.FileSystemService; import com.rapidminer.tools.I18N; import com.rapidminer.tools.LogService; import com.rapidminer.tools.ParameterService; import com.rapidminer.tools.ParentResolvingMap; import com.rapidminer.tools.StringColorMap; import com.rapidminer.tools.Tools; /** * This helper class provides some static methods and properties which might be * useful for several GUI classes. These methods include * <ul> * <li>the creation of gradient paints</li> * <li>displaying (simple) error messages</li> * <li>creation of file chosers</li> * <li>creation of text panels</li> * <li>escaping HTML messages</li> * </ul> * * @author Ingo Mierswa */ public class SwingTools { /** Defines the maximal length of characters in a line of the tool tip text. */ private static final int TOOL_TIP_LINE_LENGTH = 100; /** Defines the extra height for each row in a table. */ public static final int TABLE_ROW_EXTRA_HEIGHT = 4; /** Defines the extra height for rows in a table with components. If an * {@link ExtendedJTable} is used, this amount can be added additionally * to the amount of {@link #TABLE_ROW_EXTRA_HEIGHT} which is already * added in the constructor. */ public static final int TABLE_WITH_COMPONENTS_ROW_EXTRA_HEIGHT = 10; /** Some color constants for Java Look and Feel. */ public static final Color DARKEST_YELLOW = new Color(250, 219, 172); /** Some color constants for Java Look and Feel. */ public static final Color DARK_YELLOW = new Color(250, 226, 190); /** Some color constants for Java Look and Feel. */ public static final Color LIGHT_YELLOW = new Color(250, 233, 207); /** Some color constants for Java Look and Feel. */ public static final Color LIGHTEST_YELLOW = new Color(250, 240, 225); /** Some color constants for Java Look and Feel. */ public static final Color TRANSPARENT_YELLOW = new Color(255, 245, 230, 190); /** Some color constants for Java Look and Feel. */ public static final Color VERY_DARK_BLUE = new Color(172, 172, 212); /** Some color constants for Java Look and Feel. */ public static final Color DARKEST_BLUE = new Color(182, 202, 242); /** Some color constants for Java Look and Feel. */ public static final Color DARK_BLUE = new Color(199, 213, 242); /** Some color constants for Java Look and Feel. */ public static final Color LIGHT_BLUE = new Color(216, 224, 242); /** Some color constants for Java Look and Feel. */ public static final Color LIGHTEST_BLUE = new Color(233, 236, 242); /** The Rapid-I orange color. */ public static final Color RAPID_I_ORANGE = new Color(242, 146, 0); /** The Rapid-I brown color. */ public static final Color RAPID_I_BROWN = new Color(97, 66, 11); /** The Rapid-I beige color. */ public static final Color RAPID_I_BEIGE = new Color(202, 188, 165); /** Some color constants for Java Look and Feel. */ public static final Color LIGHTEST_RED = new Color(250, 210, 210); /** A brown font color. */ public static final Color BROWN_FONT_COLOR = new Color(63,53,24); /** A brown font color. */ public static final Color LIGHT_BROWN_FONT_COLOR = new Color(113,103,74); /** This set stores all lookup paths for icons */ private static Set<String> iconPaths = new LinkedHashSet<String>(Collections.singleton("icons/")); /** Contains the small frame icons in all possible sizes. */ private static List<Image> allFrameIcons = new LinkedList<Image>(); private static FrameIconProvider frameIconProvider; private static final String DEFAULT_FRAME_ICON_BASE_NAME = "rapidminer_frame_icon_"; private static ParentResolvingMap<String,Color> GROUP_TO_COLOR_MAP = new StringColorMap(); static { setupFrameIcons(DEFAULT_FRAME_ICON_BASE_NAME); try { GROUP_TO_COLOR_MAP.parseProperties("com/rapidminer/resources/groups.properties", "group.", ".color", OperatorDescription.class.getClassLoader()); } catch (IOException e) { LogService.getRoot().warning("Cannot load operator group colors."); } } public static void setFrameIconProvider(FrameIconProvider _frameIconProvider) { frameIconProvider = _frameIconProvider; reloadFrameIcons(); } public static void setupFrameIcons(String frameIconBaseName) { setFrameIconProvider(new DefaultFrameIconProvider(frameIconBaseName)); reloadFrameIcons(); } private static void reloadFrameIcons() { if (frameIconProvider == null) { allFrameIcons = new LinkedList<Image>(); } else { allFrameIcons = frameIconProvider.getFrameIcons(); } } /** * Returns the list of all available program icon sizes. */ public static List<Image> getFrameIconList() { return allFrameIcons; } /** Returns the list of all possible frame icons. */ public static void setFrameIcon(JFrame frame) { Method iconImageMethod = null; try { iconImageMethod = frame.getClass().getMethod("setIconImages", new Class[] { List.class }); } catch (Throwable e) { // ignore this and use single small icon below } if (iconImageMethod != null) { try { iconImageMethod.invoke(frame, new Object[] { allFrameIcons }); } catch (Throwable e) { // ignore this and use single small icon if (allFrameIcons.size() > 0) frame.setIconImage(allFrameIcons.get(0)); } } else { if (allFrameIcons.size() > 0) frame.setIconImage(allFrameIcons.get(0)); } } /** Returns the list of all possible frame icons. */ public static void setDialogIcon(JDialog dialog) { Method iconImageMethod = null; try { iconImageMethod = dialog.getClass().getMethod("setIconImages", new Class[] { List.class }); } catch (Throwable e) { // ignore this and use no icons or parent icon } if (iconImageMethod != null) { try { iconImageMethod.invoke(dialog, new Object[] { allFrameIcons }); } catch (Throwable e) { // ignore this and use no or parent icon } } } /** Creates a red gradient paint. */ public static GradientPaint makeRedPaint(double width, double height) { return new GradientPaint(0f, 0f, new Color(200,50,50), (float) width / 2, (float) height / 2, new Color(255,100,100), true); } /** Creates a blue gradient paint. */ public static GradientPaint makeBluePaint(double width, double height) { return new GradientPaint(0f, 0f, LIGHT_BLUE, (float) width / 2, (float) height / 2, LIGHTEST_BLUE, true); } /** Creates a yellow gradient paint. */ public static GradientPaint makeYellowPaint(double width, double height) { return new GradientPaint(0f, 0f, LIGHT_YELLOW, (float) width / 2, (float) height / 2, LIGHTEST_YELLOW, true); } private static final Map<String,ImageIcon> ICON_CACHE = new HashMap<String,ImageIcon>(); private static final Object ICON_LOCK = new Object(); /** Tries to load the icon for the given resource. Returns null (and writes a warning) if the * resource file cannot be loaded. This method automatically adds all icon paths specified since startup * time. The default /icons is always searched. Additional paths might be specified by {@link SwingTools#addIconStoragePath(String)}. * * The given names must contain '/' instead of backslashes! */ public static ImageIcon createIcon(String iconName) { if (RapidMiner.getExecutionMode().isHeadless()) { return null; } for (String path: iconPaths) { ImageIcon icon = createImage(path + iconName); if (icon != null) return icon; } return null; } /** * This method returns the path of the icon given. * @param iconName * @return */ public static String getIconPath(String iconName) { if (RapidMiner.getExecutionMode().isHeadless()) { return null; } for (String path: iconPaths) { ImageIcon icon = createImage(path + iconName); if (icon != null) return Tools.getResource(path + iconName).toString(); } return null; } /** * This method adds a path to the set of paths which are searched for icons if * the {@link SwingTools#createIcon(String)} is called. */ public static void addIconStoragePath(String path) { if (path.startsWith("/")) path = path.substring(1); if (!path.endsWith("/")) path = path + "/"; iconPaths.add(path); } /** Tries to load the image for the given resource. Returns null (and writes a warning) if the * resource file cannot be loaded. */ public static ImageIcon createImage(String imageName) { if (RapidMiner.getExecutionMode().isHeadless()) { return null; } synchronized (ICON_LOCK) { if (ICON_CACHE.containsKey(imageName)) { return ICON_CACHE.get(imageName); } else { URL url = Tools.getResource(imageName); if (url != null) { ImageIcon icon = new ImageIcon(url); ICON_CACHE.put(imageName, icon); return icon; } else { LogService.getRoot().fine("Cannot load image '" + imageName + "': icon will not be displayed"); return null; } } } } public static void loadIcons() { ResourceBundle guiBundle = I18N.getGUIBundle(); Enumeration<String> e = guiBundle.getKeys(); while (e.hasMoreElements()) { String key = e.nextElement(); if (key.endsWith(".icon")) { String resource = guiBundle.getString(key); if (!resource.isEmpty()) { if (Character.isDigit(resource.charAt(0))) { // We start with a number, size explicitly stated, so load directly createIcon(resource); } else { // Otherwise prepend sizes createIcon("16/"+resource); createIcon("24/"+resource); } } } } } /** This method transforms the given tool tip text into HTML. Lines are splitted at linebreaks * and additional line breaks are added after ca. {@link #TOOL_TIP_LINE_LENGTH} characters. */ public static String transformToolTipText(String description) { return transformToolTipText(description, true, TOOL_TIP_LINE_LENGTH); } /** This method transforms the given tool tip text into HTML. Lines are splitted at linebreaks * and additional line breaks are added after ca. {@link #TOOL_TIP_LINE_LENGTH} characters. * @param escapeSlashes Inidicates if forward slashes ("/") are escaped by the html code "/" */ public static String transformToolTipText(String description, boolean escapeSlashes) { return transformToolTipText(description, true, TOOL_TIP_LINE_LENGTH, escapeSlashes); } public static String transformToolTipText(String description, boolean addHTMLTags, int lineLength) { return transformToolTipText(description, addHTMLTags, lineLength, false); } /** This method transforms the given tool tip text into HTML. Lines are splitted at linebreaks * and additional line breaks are added after ca. lineLength characters. * @param escapeSlashes Inidicates if forward slashes ("/") are escaped by the html code "/" * TODO: Use <div style="width:XXXpx"> */ public static String transformToolTipText(String description, boolean addHTMLTags, int lineLength, boolean escapeSlashes) { String completeText = Tools.escapeHTML(description.trim()); if (escapeSlashes) { completeText = completeText.replaceAll("/", "/"); } StringBuffer result = new StringBuffer(); if (addHTMLTags) result.append("<html>"); // line.separator does not work here (transform and use \n) completeText = Tools.transformAllLineSeparators(completeText); String[] lines = completeText.split("\n"); for (String text : lines) { boolean first = true; while (text.length() > lineLength) { int spaceIndex = text.indexOf(" ", lineLength); if (!first) { result.append("<br>"); } first = false; if (spaceIndex >= 0) { result.append(text.substring(0, spaceIndex)); text = text.substring(spaceIndex + 1); } else { result.append(text); text = ""; } } if (!first && text.length() > 0) { result.append("<br>"); } result.append(text); result.append("<br>"); } if (addHTMLTags) result.append("</html>"); return result.toString(); } // /** Transforms the given class name array into a comma separated string of the short class names. */ // public static String getStringFromClassArray(Class[] classes) { // StringBuffer outputString = new StringBuffer(); // if (classes == null) // outputString.append("none"); // else { // for (int i = 0; i < classes.length; i++) { // if (i != 0) // outputString.append(", "); // outputString.append(Tools.classNameWOPackage(classes[i])); // } // } // if (outputString.length() == 0) // outputString.append("none"); // return outputString.toString(); // } /** Adds line breaks after {@link #TOOL_TIP_LINE_LENGTH} letters. */ public static String addLinebreaks(String message) { if (message == null) return null; String completeText = message.trim(); StringBuffer result = new StringBuffer(); // line.separator does not work here (transform and use \n) completeText = Tools.transformAllLineSeparators(completeText); String[] lines = completeText.split("\n"); for (String text : lines) { boolean first = true; while (text.length() > TOOL_TIP_LINE_LENGTH) { int spaceIndex = text.indexOf(" ", TOOL_TIP_LINE_LENGTH); if (!first) { result.append(Tools.getLineSeparator()); } first = false; if (spaceIndex >= 0) { result.append(text.substring(0, spaceIndex)); text = text.substring(spaceIndex + 1); } else { result.append(text); text = ""; } } if (!first && text.length() > 0) { result.append(Tools.getLineSeparator()); } result.append(text); result.append(Tools.getLineSeparator()); } return result.toString(); } /** * The key will be used for the properties gui.dialog.-key-.title and * gui.dialog.results.-key-.icon */ public static void showResultsDialog(final String i18nKey, JComponent results, Object...i18nArgs) { ResultViewDialog dialog = new ResultViewDialog(i18nKey, results, i18nArgs); dialog.setVisible(true); } /** * The key will be used for the properties gui.dialog.-key-.title and * gui.dialog.message.-key-.icon */ public static void showMessageDialog(final String key, Object...keyArguments) { MessageDialog dialog = new MessageDialog(key, keyArguments); dialog.setVisible(true); } /** * The key will be used for the properties gui.dialog.-key-.title and * gui.dialog.message.-key-.icon */ public static void showMessageDialog(final String key, JComponent component, Object...keyArguments) { MessageDialog dialog = new MessageDialog(key, component, keyArguments); dialog.setVisible(true); } /** * The key will be used for the properties gui.dialog.-key-.title and * gui.dialog.confirm.-key-.icon * * See {@link ConfirmDialog} for details on the mode options. */ public static int showConfirmDialog(final String key, int mode, Object...keyArguments) { ConfirmDialog dialog = new ConfirmDialog(key, mode, false, keyArguments); dialog.setVisible(true); return dialog.getReturnOption(); } /** * This method will present a dialog to enter a text. This text will be returned * if the user confirmed the edit. Otherwise null is returned. * The key will be used for the properties gui.dialog.input.-key-.title, gui.dialog.input.-key-.message and * gui.dialog.input.-key-.icon */ public static String showInputDialog(final String key, String text, Object...keyArguments) { InputDialog dialog = new InputDialog(key, text, keyArguments); dialog.setVisible(true); if (dialog.wasConfirmed()) { return dialog.getInputText(); } else { return null; } } /** * The key will be used for the properties gui.dialog.-key-.title and * gui.dialog.input.-key-.icon */ public static Object showInputDialog(final String key, Object[] selectionValues, Object initialSelectionVale, final Object...keyArguments) { SelectionInputDialog dialog = new SelectionInputDialog(key, selectionValues, initialSelectionVale, keyArguments); dialog.setVisible(true); if (dialog.wasConfirmed()) { return dialog.getInputSelection(); } else { return null; } } /** * This will open a simple input dialog, where a comboBox presents the given values. The Combobox might be editable depending * on parameter setting. * * The key will be used for the properties gui.dialog.-key-.title and * gui.dialog.input.-key-.icon */ public static Object showInputDialog(final String key, boolean editable, Object[] selectionValues, Object initialSelectionVale, final Object...keyArguments) { SelectionInputDialog dialog = new SelectionInputDialog(key, editable, selectionValues, initialSelectionVale, keyArguments); dialog.setVisible(true); if (dialog.wasConfirmed()) { return dialog.getInputSelection(); } else { return null; } } /** * Shows a very simple error message without any Java exception hints. * * @param key the I18n-key which will be used to display the internationalized message * @param arguments additional arguments for the internationalized message, which replace <code>{0}</code>, <code>{1}</code>, etcpp. */ public static void showVerySimpleErrorMessage(final String key, final Object... arguments) { if (SwingUtilities.isEventDispatchThread()) { ErrorDialog dialog = new ErrorDialog(key, arguments); dialog.setVisible(true); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ErrorDialog dialog = new ErrorDialog(key, arguments); dialog.setVisible(true); } }); } } /** * This is the normal method which could be used by GUI classes for errors caused by * some exception (e.g. IO issues). Of course these error message methods should never be * invoked by operators or similar. * * @param key the I18n-key which will be used to display the internationalized message * @param e the exception associated to this message * @param arguments additional arguments for the internationalized message, which replace <code>{0}</code>, <code>{1}</code>, etcpp. */ public static void showSimpleErrorMessage(final String key, final Throwable e, final Object... arguments) { showSimpleErrorMessage(key, e, true, arguments); } /** * This is the normal method which could be used by GUI classes for errors caused by * some exception (e.g. IO issues). Of course these error message methods should never be * invoked by operators or similar. * * @param key the I18n-key which will be used to display the internationalized message * @param e the exception associated to this message * @param displayExceptionMessage indicates if the exception message will be displayed in the dialog or just in the detailed panel * @param arguments additional arguments for the internationalized message, which replace <code>{0}</code>, <code>{1}</code>, etcpp. */ public static void showSimpleErrorMessage(final String key, final Throwable e, final boolean displayExceptionMessage, final Object... arguments) { // if debug mode is enabled, send exception to logger if ("true".equals(ParameterService.getParameterValue(RapidMiner.PROPERTY_RAPIDMINER_GENERAL_DEBUGMODE))) { LogService.getRoot().log(Level.WARNING, e.getMessage(), e); } if (SwingUtilities.isEventDispatchThread()) { ExtendedErrorDialog dialog = new ExtendedErrorDialog(key, e, displayExceptionMessage, arguments); dialog.setVisible(true); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ExtendedErrorDialog dialog = new ExtendedErrorDialog(key, e, displayExceptionMessage, arguments); dialog.setVisible(true); } }); } } /** * This is the normal method which could be used by GUI classes for errors caused by * some exception (e.g. IO issues). Of course these error message methods should never be * invoked by operators or similar. * The key is constructed as gui.dialog.error.-key- and uses .title and .icon properties * * @param key the I18n-key which will be used to display the internationalized message * @param errorMessage the error message associated to this message * @param displayExceptionMessage indicates if the exception message will be displayed in the dialog or just in the detailed panel * @param arguments additional arguments for the internationalized message, which replace <code>{0}</code>, <code>{1}</code>, etcpp. */ public static void showSimpleErrorMessage(final String key, final String errorMessage, final Object... arguments ) { if (SwingUtilities.isEventDispatchThread()) { ExtendedErrorDialog dialog = new ExtendedErrorDialog(key, errorMessage, arguments); dialog.setVisible(true); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ExtendedErrorDialog dialog = new ExtendedErrorDialog(key, errorMessage, arguments); dialog.setVisible(true); } }); } // if debug mode is enabled, print throwable into logger if (ParameterService.getParameterValue(RapidMiner.PROPERTY_RAPIDMINER_GENERAL_DEBUGMODE).equals("true")) { LogService.getRoot().log(Level.WARNING, errorMessage); } } // /** // * This is the normal method which could be used by GUI classes for errors caused by // * some exception (e.g. IO issues). Of course these error message methods should never be // * invoked by operators or similar. // * // * @param key the I18n-key which will be used to display the internationalized message // * @param errorMessage the error message associated to this message // * @param arguments additional arguments for the internationalized message, which replace <code>{0}</code>, <code>{1}</code>, etcpp. // */ // public static void showSimpleErrorMessage(final String key, final String errorMessage, final Object... arguments ) { // showSimpleErrorMessage(key, errorMessage, false, arguments); // } /** * Shows the final error message dialog. This dialog also allows to send a bug report if * the error was not (definitely) a user error. * * @param key the I18n-key which will be used to display the internationalized message * @param e the exception associated to this message * @param displayExceptionMessage indicates if the exception message will be displayed in the dialog or just in the detailed panel * @param arguments additional arguments for the internationalized message, which replace <code>{0}</code>, <code>{1}</code>, etcpp. */ public static void showFinalErrorMessage(String key, Throwable e, boolean displayExceptionMessage, Object...objects ) { // if debug modus is enabled, print throwable into logger if (ParameterService.getParameterValue(RapidMiner.PROPERTY_RAPIDMINER_GENERAL_DEBUGMODE).equals("true")) { LogService.getRoot().log(Level.SEVERE, e.getMessage(), e); } ExtendedErrorDialog dialog = new ExtendedErrorDialog( key, e, displayExceptionMessage, objects ); // dialog.setLocationRelativeTo(RapidMinerGUI.getMainFrame()); dialog.setVisible(true); } /** * Shows the final error message dialog. This dialog also allows to send a bug report if * the error was not (definitely) a user error. * * @param key the I18n-key which will be used to display the internationalized message * @param e the exception associated to this message * @param arguments additional arguments for the internationalized message, which replace <code>{0}</code>, <code>{1}</code>, etcpp. */ public static void showFinalErrorMessage(String key, Throwable e, Object...objects ) { showFinalErrorMessage(key, e, false, objects); } /** Opens a file chooser with a reasonable start directory. If the extension is null, no file filters will be used. */ public static File chooseFile(Component parent, File file, boolean open, String extension, String extensionDescription) { return chooseFile(parent, null, file, open, extension, extensionDescription); } public static File chooseFile(Component parent, String i18nKey, File file, boolean open, String extension, String extensionDescription) { return chooseFile(parent, i18nKey, file, open, false, extension, extensionDescription); } /** Opens a file chooser with a reasonable start directory. If the extension is null, no file filters will be used. * This method allows choosing directories. */ public static File chooseFile(Component parent, File file, boolean open, boolean onlyDirs, String extension, String extensionDescription) { return chooseFile(parent, null, file, open, onlyDirs, extension, extensionDescription); } public static File chooseFile(Component parent, String i18nKey, File file, boolean open, boolean onlyDirs, String extension, String extensionDescription) { return chooseFile(parent, i18nKey, file, open, onlyDirs, extension == null ? null : new String[] { extension }, extensionDescription == null ? null : new String[] { extensionDescription }); } public static File chooseFile(Component parent, String i18nKey, File file, boolean open, boolean onlyDirs, String extension, String extensionDescription, boolean acceptAllFiles) { return chooseFile(parent, i18nKey, file, open, onlyDirs, extension == null ? null : new String[] { extension }, extensionDescription == null ? null : new String[] { extensionDescription }, acceptAllFiles); } /** Returns the user selected file. */ public static File chooseFile(Component parent, File file, boolean open, boolean onlyDirs, String[] extensions, String[] extensionDescriptions) { return chooseFile(parent, null, file, open, onlyDirs, extensions, extensionDescriptions); } public static File chooseFile(Component parent, String i18nKey, File file, boolean open, boolean onlyDirs, String[] extensions, String[] extensionDescriptions) { FileFilter[] filters = null; if (extensions != null) { filters = new FileFilter[extensions.length]; for (int i = 0; i < extensions.length; i++) { filters[i] = new SimpleFileFilter(extensionDescriptions[i] + " (*." + extensions[i] + ")", "." + extensions[i]); } } return chooseFile(parent, i18nKey, file, open, onlyDirs, filters, true); } public static File chooseFile(Component parent, String i18nKey, File file, boolean open, boolean onlyDirs, String[] extensions, String[] extensionDescriptions, boolean acceptAllFiles) { FileFilter[] filters = null; if (extensions != null) { filters = new FileFilter[extensions.length]; for (int i = 0; i < extensions.length; i++) { filters[i] = new SimpleFileFilter(extensionDescriptions[i] + " (*." + extensions[i] + ")", "." + extensions[i]); } } return chooseFile(parent, i18nKey, file, open, onlyDirs, filters, acceptAllFiles); } /** * Opens a file chooser with a reasonable start directory. onlyDirs * indidcates if only files or only can be selected. * * @param file * The initially selected value of the file chooser dialog * @param open * Open or save dialog? * @param onlyDirs * Only allow directories to be selected * @param fileFilters * List of FileFilters to use */ private static File chooseFile(Component parent, String i18nKey, File file, boolean open, boolean onlyDirs, FileFilter[] fileFilters, boolean acceptAllFiles) { if (parent == null) parent = RapidMinerGUI.getMainFrame(); String key = "file_chooser." + (i18nKey != null ? i18nKey : open ? (onlyDirs ? "open_directory" : "open") : "save"); JFileChooser fileChooser = createFileChooser(key, file, onlyDirs, fileFilters); fileChooser.setAcceptAllFileFilterUsed(acceptAllFiles); int returnValue = open ? fileChooser.showOpenDialog(parent) : fileChooser.showSaveDialog(parent); switch (returnValue) { case JFileChooser.APPROVE_OPTION: // check extension File selectedFile = fileChooser.getSelectedFile(); FileFilter selectedFilter = fileChooser.getFileFilter(); String extension = null; if (selectedFilter instanceof SimpleFileFilter) { SimpleFileFilter simpleFF = (SimpleFileFilter)selectedFilter; extension = simpleFF.getExtension(); } if (extension != null) { if (!selectedFile.getAbsolutePath().toLowerCase().endsWith(extension.toLowerCase())) { selectedFile = new File(selectedFile.getAbsolutePath() + extension); } } if (selectedFile != null) { File parentFile = selectedFile.getParentFile(); if (parentFile != null) { List<Bookmark> bookmarks = null; File bookmarksFile = new File(FileSystemService.getUserRapidMinerDir(), ".bookmarks"); if (bookmarksFile.exists()) { bookmarks = BookmarkIO.readBookmarks(bookmarksFile); Iterator<Bookmark> b = bookmarks.iterator(); while (b.hasNext()) { Bookmark bookmark = b.next(); if (bookmark.getName().equals("--- Last Directory")) { b.remove(); } } bookmarks.add(new Bookmark("--- Last Directory", parentFile.getAbsolutePath())); Collections.sort(bookmarks); BookmarkIO.writeBookmarks(bookmarks, bookmarksFile); } } } return selectedFile; default: return null; } } /** * Creates file chooser with a reasonable start directory. You may use the * following code snippet in order to retrieve the file: * * <pre> * if (fileChooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) * File selectedFile = fileChooser.getSelectedFile(); * </pre> * * Usually, the method * {@link #chooseFile(Component, File, boolean, boolean, FileFilter[])} or * one of the convenience wrapper methods can be used to do this. This * method is only useful if one is interested, e.g., in the selected file * filter. * * @param file * The initially selected value of the file chooser dialog * @param onlyDirs * Only allow directories to be selected * @param fileFilters * List of FileFilters to use */ public static JFileChooser createFileChooser(String i18nKey, File file, boolean onlyDirs, FileFilter[] fileFilters) { File directory = null; if (file != null) { if (file.isDirectory()) { directory = file; } else { directory = file.getAbsoluteFile().getParentFile(); } } else { directory = FileSystemView.getFileSystemView().getDefaultDirectory(); } JFileChooser fileChooser = new ExtendedJFileChooser(i18nKey, directory); if (onlyDirs) fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); if (fileFilters != null) { fileChooser.setAcceptAllFileFilterUsed(true); for (FileFilter fileFilter : fileFilters) fileChooser.addChoosableFileFilter(fileFilter); } if (file != null) fileChooser.setSelectedFile(file); return fileChooser; } /** Creates a panel with title and text. The panel has a border layout and the text * is placed into the NORTH section. */ public static JPanel createTextPanel(String title, String text) { JPanel panel = new JPanel(new java.awt.BorderLayout()); JLabel label = new JLabel("<html><h3>" + title + "</h3>" + (text != null ? "<p>" + text + "</p>" : "") + "</html>"); label.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11)); label.setFont(label.getFont().deriveFont(java.awt.Font.PLAIN)); panel.add(label, java.awt.BorderLayout.NORTH); return panel; } // ================================================================================ // /** // * Replaces simple html tags and quotes by RapidMiner specific text elements. // * These can be used in XML files without confusing an XML parser. // */ // public static String html2RapidMinerText(String html) { // if (html == null) // return null; // String result = html.replaceAll("<", "#ylt#"); // result = result.replaceAll(">", "#ygt#"); // result = result.replaceAll("\"", "#yquot#"); // result = result.replaceAll(Tools.getLineSeparator(), ""); // return result; // } /** * Replaces the RapidMiner specific tag elements by normal HTML tags. * The given text is also embedded in an HTML and body tag with an * appropriated style sheet definition. * * Currently, the only replaced tag is <icon>NAME</icon> * which will be replaced by <img src="path/to/NAME"/>. * */ public static String text2DisplayHtml(String text) { String result = "<html><head><style type=text/css>body { font-family:sans-serif; font-size:12pt; }</style></head><body>" + text + "</body></html>"; result = text2SimpleHtml(result); //result = result.replaceAll("#yquot#", """); while (result.indexOf("<icon>") != -1) { int startIndex = result.indexOf("<icon>"); int endIndex = result.indexOf("</icon>"); String start = result.substring(0, startIndex); String end = result.substring(endIndex + 7); String icon = result.substring(startIndex + 6, endIndex).trim().toLowerCase(); java.net.URL url = Tools.getResource("icons/" + icon + ".png"); if (url != null) result = start + "<img src=\"" + url + "\"/>" + end; else result = start + end; } return result; } /** * Replaces the RapidMiner specific tag elements by normal HTML tags. This method * does not embed the given text in a root HTML tag. */ private static String text2SimpleHtml(String htmlText) { if (htmlText == null) return null; String replaceString = htmlText; // replaceString = htmlText.replaceAll("#ygt#", ">"); // replaceString = replaceString.replaceAll("#ylt#", "<"); StringBuffer result = new StringBuffer(); boolean afterClose = true; int currentLineLength = 0; for (int i = 0; i < replaceString.length(); i++) { char c = replaceString.charAt(i); // skip white space after close if (afterClose) if (c == ' ') continue; // opening bracket if (c == '<') { if (!afterClose) { result.append(Tools.getLineSeparator()); currentLineLength = 0; } } // append char afterClose = false; result.append(c); currentLineLength++; // break long lines if (currentLineLength > 70 && c == ' ') { result.append(Tools.getLineSeparator()); currentLineLength = 0; } // closing bracket if (c == '>') { result.append(Tools.getLineSeparator()); currentLineLength = 0; afterClose = true; } } return result.toString(); } /** * Returns a color equivalent to the value of <code>value</code>. The value * has to be normalized between 0 and 1. */ public static Color getPointColor(double value) { return new Color(Color.HSBtoRGB((float) (0.68 * (1.0d - value)), 1.0f, 1.0f)); // all // colors } /** * Returns a color equivalent to the value of <code>value</code>. The value * will be normalized between 0 and 1 using the parameters max and min. Which * are the minimum and maximum of the complete dataset. */ public static Color getPointColor(double value, double max, double min){ value = (value - min) / (max - min); return getPointColor(value); } /** Returns JEditTextArea defaults with adapted syntax color styles. */ public static TextAreaDefaults getTextAreaDefaults() { TextAreaDefaults defaults = TextAreaDefaults.getDefaults(); defaults.styles = getSyntaxStyles(); return defaults; } /** * Returns adapted syntax color and font styles matching RapidMiner colors. */ public static SyntaxStyle[] getSyntaxStyles() { SyntaxStyle[] styles = SyntaxUtilities.getDefaultSyntaxStyles(); styles[Token.COMMENT1] = new SyntaxStyle(new Color(0x990033), true, false); styles[Token.COMMENT2] = new SyntaxStyle(Color.black, true, false); styles[Token.KEYWORD1] = new SyntaxStyle(Color.black, false, true); styles[Token.KEYWORD2] = new SyntaxStyle(new Color(255,51,204), false, false); styles[Token.KEYWORD3] = new SyntaxStyle(new Color(255,51,204), false, false); styles[Token.LITERAL1] = new SyntaxStyle(new Color(51,51,255), false, false); styles[Token.LITERAL2] = new SyntaxStyle(new Color(51,51,255), false, false); styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033), false, true); styles[Token.OPERATOR] = new SyntaxStyle(Color.black, false, true); styles[Token.INVALID] = new SyntaxStyle(Color.red, false, true); return styles; } public static String toHTMLString(Ports<? extends Port> ports) { StringBuilder b = new StringBuilder(); boolean first = true; for (Port port : ports.getAllPorts()) { if (!first) { b.append(", "); } else { first = false; } b.append(port.getName()); String desc = port.getDescription(); if (desc.length() > 0) { b.append(": "); b.append(port.getDescription()); } } return b.toString(); } public static void showLongMessage(String i18nKey, final String message) { LongMessageDialog dialog = new LongMessageDialog(i18nKey, message); dialog.setVisible(true); } public static void setEnabledRecursive(Component c, boolean enabled) { c.setEnabled(enabled); if (c instanceof Container) { for (Component child : ((Container)c).getComponents()) { setEnabledRecursive(child, enabled); } } } public static void setOpaqueRecursive(Component c, boolean enabled) { if (c instanceof JComponent) { ((JComponent)c).setOpaque(enabled); } if (c instanceof Container) { for (Component child : ((Container)c).getComponents()) { setOpaqueRecursive(child, enabled); } } } public static void setProcessEditorsEnabled(boolean enabled) { MainFrame mainFrame = RapidMinerGUI.getMainFrame(); setEnabledRecursive(mainFrame.getProcessPanel().getComponent(), enabled); setEnabledRecursive(mainFrame.getPropertyPanel().getComponent(), enabled); setEnabledRecursive(mainFrame.getOperatorTree(), enabled); setEnabledRecursive(mainFrame.getProcessContextEditor().getComponent(), enabled); setEnabledRecursive(mainFrame.getXMLEditor(), enabled); mainFrame.getActions().enableActions(); } public static Color getOperatorColor(Operator operator) { return GROUP_TO_COLOR_MAP.get(operator.getOperatorDescription().getGroup()); } public static Color getOperatorColor(String operatorGroup) { return GROUP_TO_COLOR_MAP.get(operatorGroup); } /** * This method adds the colors of the given property file to the global group colors */ public static void registerAdditionalGroupColors(String groupProperties, String pluginName, ClassLoader classLoader) { try { GROUP_TO_COLOR_MAP.parseProperties(groupProperties, "group.", ".color", classLoader); } catch (IOException e) { LogService.getRoot().warning("Cannot load operator group colors for plugin " + pluginName + "."); } } }