/* * RapidMiner * * Copyright (C) 2001-2008 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; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.imageio.ImageIO; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.Icon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.JToolBar; import org.freehep.util.export.ExportDialog; import com.rapidminer.BreakpointListener; import com.rapidminer.Process; import com.rapidminer.RapidMiner; import com.rapidminer.gui.actions.AnovaCalculatorAction; import com.rapidminer.gui.actions.AttributeDescriptionFileWizardAction; import com.rapidminer.gui.actions.AttributeEditorAction; import com.rapidminer.gui.actions.BoxViewerAction; import com.rapidminer.gui.actions.CheckForJDBCDriversAction; import com.rapidminer.gui.actions.CheckForUpdatesAction; import com.rapidminer.gui.actions.EditModeAction; import com.rapidminer.gui.actions.ExitAction; import com.rapidminer.gui.actions.ExportViewAction; import com.rapidminer.gui.actions.ManageBuildingBlocksAction; import com.rapidminer.gui.actions.ManageTemplatesAction; import com.rapidminer.gui.actions.NewAction; import com.rapidminer.gui.actions.OpenAction; import com.rapidminer.gui.actions.PageSetupAction; import com.rapidminer.gui.actions.PrintAction; import com.rapidminer.gui.actions.PrintPreviewAction; import com.rapidminer.gui.actions.RedoAction; import com.rapidminer.gui.actions.ResultHistoryAction; import com.rapidminer.gui.actions.ResultsModeAction; import com.rapidminer.gui.actions.RunResumeAction; import com.rapidminer.gui.actions.SaveAction; import com.rapidminer.gui.actions.SaveAsAction; import com.rapidminer.gui.actions.SaveAsTemplateAction; import com.rapidminer.gui.actions.SettingsAction; import com.rapidminer.gui.actions.StopAction; import com.rapidminer.gui.actions.SwitchWorkspaceAction; import com.rapidminer.gui.actions.ToggleExpertModeAction; import com.rapidminer.gui.actions.ToggleLoggingViewerItem; import com.rapidminer.gui.actions.ToggleSystemMonitorItem; import com.rapidminer.gui.actions.TutorialAction; import com.rapidminer.gui.actions.UndoAction; import com.rapidminer.gui.actions.ValidateProcessAction; import com.rapidminer.gui.actions.WizardAction; import com.rapidminer.gui.dialog.Browser; import com.rapidminer.gui.dialog.ProcessInfoScreen; import com.rapidminer.gui.dialog.RequestSaveDialog; import com.rapidminer.gui.dialog.Tutorial; import com.rapidminer.gui.operatortree.OperatorTree; import com.rapidminer.gui.plotter.PlotterPanel; import com.rapidminer.gui.processeditor.MainProcessEditor; import com.rapidminer.gui.processeditor.ResultDisplay; import com.rapidminer.gui.properties.OperatorPropertyTable; import com.rapidminer.gui.templates.SaveAsTemplateDialog; import com.rapidminer.gui.templates.Template; import com.rapidminer.gui.tools.AboutBox; import com.rapidminer.gui.tools.ComponentPrinter; import com.rapidminer.gui.tools.ExtendedJScrollPane; import com.rapidminer.gui.tools.ExtendedJToolBar; import com.rapidminer.gui.tools.IconSize; import com.rapidminer.gui.tools.LoggingViewer; import com.rapidminer.gui.tools.StatusBar; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.gui.tools.SystemMonitor; import com.rapidminer.gui.tools.WelcomeScreen; import com.rapidminer.operator.IOContainer; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorChain; import com.rapidminer.parameter.ParameterTypeBoolean; import com.rapidminer.parameter.ParameterTypeColor; import com.rapidminer.parameter.ParameterTypeInt; import com.rapidminer.tools.LogService; import com.rapidminer.tools.ParameterService; import com.rapidminer.tools.Tools; import com.rapidminer.tools.XMLException; import com.rapidminer.tools.plugin.Plugin; import de.java.print.PreviewDialog; /** * The main component class of the RapidMiner GUI. The class holds a lot of Actions * that can be used for the tool bar and for the menu bar. MainFrame has methods * for handling the process (saving, opening, creating new). It keeps track * of the state of the process and enables/disables buttons. It must be * notified whenever the process changes and propagates this event to its * children. Most of the code is enclosed within the Actions. * * @author Ingo Mierswa * @version $Id: MainFrame.java,v 1.35 2008/07/12 16:53:15 ingomierswa Exp $ */ public class MainFrame extends JFrame implements WindowListener, BreakpointListener { /** The property name for "The pixel size of each plot in matrix plots." */ public static final String PROPERTY_RAPIDMINER_GUI_PLOTTER_MATRIXPLOT_SIZE = "rapidminer.gui.plotter.matrixplot.size"; /** The property name for "The maximum number of rows used for a plotter, using only a sample of this size if more rows are available." */ public static final String PROPERTY_RAPIDMINER_GUI_PLOTTER_ROWS_MAXIMUM = "rapidminer.gui.plotter.rows.maximum"; /** The property name for "Limit number of displayed classes plotter legends. -1 for no limit." */ public static final String PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_CLASSLIMIT = "rapidminer.gui.plotter.legend.classlimit"; /** The property name for "The color for minimum values of the plotter legend." */ public static final String PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MINCOLOR = "rapidminer.gui.plotter.legend.mincolor"; /** The property name for "The color for maximum values of the plotter legend." */ public static final String PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MAXCOLOR = "rapidminer.gui.plotter.legend.maxcolor"; /** The property name for "Limit number of displayed classes for colorized plots. -1 for no limit." */ public static final String PROPERTY_RAPIDMINER_GUI_PLOTTER_COLORS_CLASSLIMIT = "rapidminer.gui.plotter.colors.classlimit"; /** The property name for "Maximum number of states in the undo list." */ public static final String PROPERTY_RAPIDMINER_GUI_UNDOLIST_SIZE = "rapidminer.gui.undolist.size"; /** The property name for "Maximum number of examples to use for the attribute editor. -1 for no limit." */ public static final String PROPERTY_RAPIDMINER_GUI_ATTRIBUTEEDITOR_ROWLIMIT = "rapidminer.gui.attributeeditor.rowlimit"; /** The property name for "Beep on process success?" */ public static final String PROPERTY_RAPIDMINER_GUI_BEEP_SUCCESS = "rapidminer.gui.beep.success"; /** The property name for "Beep on error?" */ public static final String PROPERTY_RAPIDMINER_GUI_BEEP_ERROR = "rapidminer.gui.beep.error"; /** The property name for "Beep when breakpoint reached?" */ public static final String PROPERTY_RAPIDMINER_GUI_BEEP_BREAKPOINT = "rapidminer.gui.beep.breakpoint"; /** The property name for "Limit number of displayed rows in the message viewer. -1 for no limit." */ public static final String PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_ROWLIMIT = "rapidminer.gui.messageviewer.rowlimit"; /** The property name for "The color for notes in the message viewer." */ public static final String PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_NOTES = "rapidminer.gui.messageviewer.highlight.notes"; /** The property name for "The color for warnings in the message viewer." */ public static final String PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_WARNINGS = "rapidminer.gui.messageviewer.highlight.warnings"; /** The property name for "The color for errors in the message viewer." */ public static final String PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_ERRORS = "rapidminer.gui.messageviewer.highlight.errors"; /** The property name for "The color for the logging service indicator in the message viewer." */ public static final String PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_LOGSERVICE = "rapidminer.gui.messageviewer.highlight.logservice"; /** The property name for "Shows process info screen after loading?" */ public static final String PROPERTY_RAPIDMINER_GUI_PROCESSINFO_SHOW = "rapidminer.gui.processinfo.show"; /** The property name for "Shows process info screen after loading?" */ public static final String PROPERTY_RAPIDMINER_GUI_SAVEDIALOG = "rapidminer.gui.savedialog"; private static final long serialVersionUID = -1602076945350148969L; private static final String NEWS_TEXT = "Toggle between the Edit Mode (edit process setup)" + Tools.getLineSeparator() + "and the Results Mode via the upper right icons (or F9)" + Tools.getLineSeparator() + "" + Tools.getLineSeparator() + "Arrange and add operators in the tree via drag and drop" + Tools.getLineSeparator() + "" + Tools.getLineSeparator() + "Clustering is now part of the RapidMiner core (former plugin)" + Tools.getLineSeparator() + "" + Tools.getLineSeparator() + "Are you interested in support, consulting, or other" + Tools.getLineSeparator() + "professional services? Visit: http://rapid-i.com/"; private static final String HISTORY_ICON_NAME = "24/history2.png"; private static final String ABOUT_ICON_NAME = "24/about.png"; private static final String HELP_ICON_NAME = "24/help2.png"; private static final String SUPPORT_ICON_NAME = "24/lifebelt.png"; private static final String PLUGINS_ICON_NAME = "24/plug.png"; private static final String RAPID_MINER_LOGO_NAME = "rapidminer_logo.png"; private static Icon historyIcon = null; private static Icon aboutIcon = null; private static Icon helpIcon = null; private static Icon supportIcon = null; private static Icon pluginsIcon = null; private static Image rapidMinerLogo = null; static { historyIcon = SwingTools.createIcon(HISTORY_ICON_NAME); aboutIcon = SwingTools.createIcon(ABOUT_ICON_NAME); helpIcon = SwingTools.createIcon(HELP_ICON_NAME); supportIcon = SwingTools.createIcon(SUPPORT_ICON_NAME); pluginsIcon = SwingTools.createIcon(PLUGINS_ICON_NAME); URL url = Tools.getResource(RAPID_MINER_LOGO_NAME); if (url != null) { try { rapidMinerLogo = ImageIO.read(url); } catch (IOException e) { rapidMinerLogo = null; } } else { rapidMinerLogo = null; } } /** * Registers all RapidMiner GUI properties. This must often be done centrally in * mainframe to ensure that the properties are set when the GUI is started. */ static { RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_PLOTTER_MATRIXPLOT_SIZE, "The pixel size of each plot in matrix plots.", 1, Integer.MAX_VALUE, 200)); RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_PLOTTER_ROWS_MAXIMUM, "The maximum number of rows used for a plotter, using only a sample of this size if more rows are available.", 1, Integer.MAX_VALUE, PlotterPanel.DEFAULT_MAX_NUMBER_OF_DATA_POINTS)); RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_CLASSLIMIT, "Limit number of displayed classes plotter legends. -1 for no limit.", -1, Integer.MAX_VALUE, 10)); RapidMiner.registerRapidMinerProperty(new ParameterTypeColor(PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MINCOLOR, "The color for minimum values of the plotter legend.", java.awt.Color.blue)); RapidMiner.registerRapidMinerProperty(new ParameterTypeColor(PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MAXCOLOR, "The color for maximum values of the plotter legend.", java.awt.Color.red)); RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_PLOTTER_COLORS_CLASSLIMIT, "Limit number of displayed classes for colorized plots. -1 for no limit.", -1, Integer.MAX_VALUE, 10)); RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_UNDOLIST_SIZE, "Maximum number of states in the undo list.", 1, Integer.MAX_VALUE, 10)); RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_ATTRIBUTEEDITOR_ROWLIMIT, "Maximum number of examples to use for the attribute editor. -1 for no limit.", -1, Integer.MAX_VALUE, 50)); RapidMiner.registerRapidMinerProperty(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_BEEP_SUCCESS, "Beep on process success?", false)); RapidMiner.registerRapidMinerProperty(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_BEEP_ERROR, "Beep on error?", false)); RapidMiner.registerRapidMinerProperty(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_BEEP_BREAKPOINT, "Beep when breakpoint reached?", false)); RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_ROWLIMIT, "Limit number of displayed rows in the message viewer. -1 for no limit.", -1, Integer.MAX_VALUE, 1000)); RapidMiner.registerRapidMinerProperty(new ParameterTypeColor(PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_NOTES, "The color for notes in the message viewer.", new java.awt.Color(51,151,51))); RapidMiner.registerRapidMinerProperty(new ParameterTypeColor(PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_WARNINGS, "The color for warnings in the message viewer.", new java.awt.Color(51,51,255))); RapidMiner.registerRapidMinerProperty(new ParameterTypeColor(PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_ERRORS, "The color for errors in the message viewer.", new java.awt.Color(255,51,204))); RapidMiner.registerRapidMinerProperty(new ParameterTypeColor(PROPERTY_RAPIDMINER_GUI_MESSAGEVIEWER_HIGHLIGHT_LOGSERVICE, "The color for the logging service indicator in the message viewer.", new java.awt.Color(184,184,184))); RapidMiner.registerRapidMinerProperty(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_PROCESSINFO_SHOW, "Shows process info screen after loading?", true)); RapidMiner.registerRapidMinerProperty(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_SAVEDIALOG, "Shows a dialog asking for saving the current process before the process is started?", true)); } /** The title of the frame. */ public static final String TITLE = "RapidMiner"; // -------------------------------------------------------------------------------- public static final int EDIT_MODE = 0; public static final int RESULTS_MODE = 1; public static final int WELCOME_MODE = 2; public static final String EDIT_MODE_NAME = "edit"; public static final String RESULTS_MODE_NAME = "results"; public static final String WELCOME_MODE_NAME = "welcome"; public final transient Action NEW_ACTION_24 = new NewAction(this, IconSize.SMALL); public final transient Action NEW_ACTION_32 = new NewAction(this, IconSize.MIDDLE); public final transient Action OPEN_ACTION_24 = new OpenAction(this, IconSize.SMALL); public final transient Action OPEN_ACTION_32 = new OpenAction(this, IconSize.MIDDLE); public final transient Action SAVE_ACTION_24 = new SaveAction(this, IconSize.SMALL); public final transient Action SAVE_ACTION_32 = new SaveAction(this, IconSize.MIDDLE); public final transient Action SAVE_AS_ACTION_24 = new SaveAsAction(this, IconSize.SMALL); public final transient Action SAVE_AS_ACTION_32 = new SaveAsAction(this, IconSize.MIDDLE); public final transient Action SAVE_AS_TEMPLATE_ACTION_24 = new SaveAsTemplateAction(this, IconSize.SMALL); public final transient Action SAVE_AS_TEMPLATE_ACTION_32 = new SaveAsTemplateAction(this, IconSize.MIDDLE); public final transient Action MANAGE_TEMPLATES_ACTION_24 = new ManageTemplatesAction(this, IconSize.SMALL); public final transient Action MANAGE_TEMPLATES_ACTION_32 = new ManageTemplatesAction(this, IconSize.MIDDLE); public final transient Action MANAGE_BUILDING_BLOCKS_ACTION_24 = new ManageBuildingBlocksAction(this, IconSize.SMALL); public final transient Action MANAGE_BUILDING_BLOCKS_ACTION_32 = new ManageBuildingBlocksAction(this, IconSize.MIDDLE); public final transient Action PRINT_ACTION_24 = new PrintAction(this, IconSize.SMALL); public final transient Action PRINT_ACTION_32 = new PrintAction(this, IconSize.MIDDLE); public final transient Action PRINT_PREVIEW_ACTION_24 = new PrintPreviewAction(this, IconSize.SMALL); public final transient Action PRINT_PREVIEW_ACTION_32 = new PrintPreviewAction(this, IconSize.MIDDLE); public final transient Action PAGE_SETUP_ACTION_24 = new PageSetupAction(this, IconSize.SMALL); public final transient Action PAGE_SETUP_ACTION_32 = new PageSetupAction(this, IconSize.MIDDLE); public final transient Action EXPORT_ACTION_24 = new ExportViewAction(this, IconSize.SMALL); public final transient Action EXPORT_ACTION_32 = new ExportViewAction(this, IconSize.MIDDLE); public final transient Action EXIT_ACTION_24 = new ExitAction(this, IconSize.SMALL); public final transient Action EXIT_ACTION_32 = new ExitAction(this, IconSize.MIDDLE); public final transient RunResumeAction RUN_RESUME_ACTION_24 = new RunResumeAction(this, IconSize.SMALL); public final transient RunResumeAction RUN_RESUME_ACTION_32 = new RunResumeAction(this, IconSize.MIDDLE); public final transient Action STOP_ACTION_24 = new StopAction(this, IconSize.SMALL); public final transient Action STOP_ACTION_32 = new StopAction(this, IconSize.MIDDLE); public final transient Action VALIDATE_ACTION_24 = new ValidateProcessAction(this, IconSize.SMALL); public final transient Action VALIDATE_ACTION_32 = new ValidateProcessAction(this, IconSize.MIDDLE); public final transient Action WIZARD_ACTION_24 = new WizardAction(this, IconSize.SMALL); public final transient Action WIZARD_ACTION_32 = new WizardAction(this, IconSize.MIDDLE); public final transient Action SETTINGS_ACTION_24 = new SettingsAction(this, IconSize.SMALL); public final transient Action SETTINGS_ACTION_32 = new SettingsAction(this, IconSize.MIDDLE); public final transient Action TOGGLE_EXPERT_MODE_ACTION_24 = new ToggleExpertModeAction(this, IconSize.SMALL); public final transient Action TOGGLE_EXPERT_MODE_ACTION_32 = new ToggleExpertModeAction(this, IconSize.MIDDLE); public final transient Action TUTORIAL_ACTION_24 = new TutorialAction(this, IconSize.SMALL); public final transient Action TUTORIAL_ACTION_32 = new TutorialAction(this, IconSize.MIDDLE); public final transient Action UNDO_ACTION_24 = new UndoAction(this, IconSize.SMALL); public final transient Action UNDO_ACTION_32 = new UndoAction(this, IconSize.MIDDLE); public final transient Action REDO_ACTION_24 = new RedoAction(this, IconSize.SMALL); public final transient Action REDO_ACTION_32 = new RedoAction(this, IconSize.MIDDLE); public final transient Action ATTRIBUTE_EDITOR_ACTION_24 = new AttributeEditorAction(this, IconSize.SMALL); public final transient Action ATTRIBUTE_EDITOR_ACTION_32 = new AttributeEditorAction(this, IconSize.MIDDLE); public final transient Action ANOVA_CALCULATOR_ACTION_24 = new AnovaCalculatorAction(this, IconSize.SMALL); public final transient Action ANOVA_CALCULATOR_ACTION_32 = new AnovaCalculatorAction(this, IconSize.MIDDLE); public final transient Action CHECK_FOR_UPDATES_ACTION_24 = new CheckForUpdatesAction(IconSize.SMALL); public final transient Action CHECK_FOR_UPDATES_ACTION_32 = new CheckForUpdatesAction(IconSize.MIDDLE); public final transient Action CHECK_FOR_JDBC_DRIVERS_ACTION_24 = new CheckForJDBCDriversAction(this, IconSize.SMALL); public final transient Action CHECK_FOR_JDBC_DRIVERS_ACTION_32 = new CheckForJDBCDriversAction(this, IconSize.MIDDLE); public final transient Action EDIT_MODE_24 = new EditModeAction(this, IconSize.SMALL); public final transient Action EDIT_MODE_32 = new EditModeAction(this, IconSize.MIDDLE); public final transient Action RESULTS_MODE_24 = new ResultsModeAction(this, IconSize.SMALL); public final transient Action RESULTS_MODE_32 = new ResultsModeAction(this, IconSize.MIDDLE); public final transient Action BOX_VIEW_24 = new BoxViewerAction(this, IconSize.SMALL); public final transient Action BOX_VIEW_32 = new BoxViewerAction(this, IconSize.MIDDLE); public final transient Action RESULT_HISTORY_24 = new ResultHistoryAction(this, IconSize.SMALL); public final transient Action RESULT_HISTORY_32 = new ResultHistoryAction(this, IconSize.MIDDLE); public final transient Action ATTRIBUTE_DESCRIPTION_FILE_WIZARD_24 = new AttributeDescriptionFileWizardAction(IconSize.SMALL); public final transient Action ATTRIBUTE_DESCRIPTION_FILE_WIZARD_32 = new AttributeDescriptionFileWizardAction(IconSize.MIDDLE); public final transient Action SWITCH_WORKSPACE_24 = new SwitchWorkspaceAction(IconSize.SMALL); public final transient Action SWITCH_WORKSPACE_32 = new SwitchWorkspaceAction(IconSize.MIDDLE); public final JCheckBoxMenuItem TOGGLE_LOGGING_VIEWER = new ToggleLoggingViewerItem(this, IconSize.SMALL); public final JCheckBoxMenuItem TOGGLE_SYSTEM_MONITOR = new ToggleSystemMonitorItem(this, IconSize.SMALL); // -------------------------------------------------------------------------------- private CardLayout mainCardLayout = new CardLayout(); private JPanel mainPanel = new JPanel(mainCardLayout); private WelcomeScreen welcomeScreen = null; private ResultDisplay resultDisplay = new ResultDisplay(); private LoggingViewer messageViewer = new LoggingViewer(); private SystemMonitor systemMonitor = new SystemMonitor(); private StatusBar statusBar = new StatusBar(); private MainProcessEditor mainEditor = new MainProcessEditor(this); private JToolBar toolBar = new ExtendedJToolBar(); private JSplitPane splitPaneV; private JSplitPane loggingSplitPaneH; private int lastLoggingDividerLocation = -1; private int lastMonitorDividerLocation = -1; private int currentMode = WELCOME_MODE; private JMenu recentFilesMenu = new JMenu("Recent Files"); private transient PrinterJob printerJob = PrinterJob.getPrinterJob(); private transient PageFormat pageFormat = printerJob.defaultPage(); private boolean changed = false; private boolean tutorialMode = false; private LinkedList<String> undoList = new LinkedList<String>(); private int undoIndex; private boolean noActualChange = false; /** The hostname of the system. Might be empty (no host name will be shown) and will be initialized * in the first call of {@link #setTitle()}. */ private String hostname = null; private transient Process process = null; private transient ProcessThread processThread; private JMenuBar menuBar; // -------------------------------------------------------------------------------- /** Creates a new main frame containing the RapidMiner GUI. */ public MainFrame() { super(TITLE); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); addWindowListener(this); // set frame icons SwingTools.setFrameIcon(this); // create and set new process setup setProcess(new Process(), true); // create main editor and result display welcomeScreen = new WelcomeScreen(this, NEWS_TEXT); mainPanel.setBorder(null); mainPanel.add(mainEditor, EDIT_MODE_NAME); mainPanel.add(resultDisplay, RESULTS_MODE_NAME); mainPanel.add(new ExtendedJScrollPane(welcomeScreen), WELCOME_MODE_NAME); mainEditor.setBorder(null); resultDisplay.setBorder(null); // create message viewer and memory monitor systemMonitor.setBorder(BorderFactory.createEtchedBorder()); loggingSplitPaneH = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, messageViewer, systemMonitor); loggingSplitPaneH.setBorder(null); // will cause the system monitor half to keep fixed size during resizing loggingSplitPaneH.setResizeWeight(1.0); systemMonitor.startMonitorThread(); // add main panel and message viewer splitPaneV = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mainPanel, loggingSplitPaneH); splitPaneV.setBorder(null); // will cause the logging message viewer half to keep fixed size during resizing splitPaneV.setResizeWeight(1.0); splitPaneV.setBorder(null); getContentPane().add(splitPaneV, BorderLayout.CENTER); // menu bar menuBar = new JMenuBar(); setJMenuBar(menuBar); // file menu JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F); fileMenu.add(NEW_ACTION_24); fileMenu.add(WIZARD_ACTION_24); fileMenu.add(OPEN_ACTION_24); updateRecentFileList(); recentFilesMenu.setIcon(historyIcon); fileMenu.add(recentFilesMenu); fileMenu.addSeparator(); fileMenu.add(SAVE_ACTION_24); fileMenu.add(SAVE_AS_ACTION_24); fileMenu.add(SAVE_AS_TEMPLATE_ACTION_24); fileMenu.addSeparator(); fileMenu.add(PRINT_ACTION_24); fileMenu.add(PRINT_PREVIEW_ACTION_24); fileMenu.add(PAGE_SETUP_ACTION_24); fileMenu.add(EXPORT_ACTION_24); fileMenu.addSeparator(); fileMenu.add(SWITCH_WORKSPACE_24); fileMenu.addSeparator(); fileMenu.add(EXIT_ACTION_24); menuBar.add(fileMenu); // edit menu JMenu editMenu = new JMenu("Edit"); editMenu.setMnemonic(KeyEvent.VK_E); editMenu.add(UNDO_ACTION_24); editMenu.add(REDO_ACTION_24); editMenu.addSeparator(); editMenu.add(mainEditor.getOperatorTree().NEW_OPERATOR_ACTION_24); editMenu.add(mainEditor.getOperatorTree().NEW_BUILDING_BLOCK_ACTION_24); editMenu.addSeparator(); mainEditor.getOperatorTree().addOperatorMenuItems(editMenu); menuBar.add(editMenu); // view menu JMenu viewMenu = new JMenu("View"); viewMenu.setMnemonic(KeyEvent.VK_V); viewMenu.add(EDIT_MODE_24); viewMenu.add(RESULTS_MODE_24); viewMenu.addSeparator(); viewMenu.add(TOGGLE_EXPERT_MODE_ACTION_24); viewMenu.addSeparator(); viewMenu.add(BOX_VIEW_24); viewMenu.add(RESULT_HISTORY_24); viewMenu.addSeparator(); viewMenu.add(TOGGLE_LOGGING_VIEWER); viewMenu.add(TOGGLE_SYSTEM_MONITOR); menuBar.add(viewMenu); // process menu JMenu expMenu = new JMenu("Process"); expMenu.setMnemonic(KeyEvent.VK_P); expMenu.add(RUN_RESUME_ACTION_24); expMenu.add(STOP_ACTION_24); menuBar.add(expMenu); // tools menu JMenu toolsMenu = new JMenu("Tools"); toolsMenu.setMnemonic(KeyEvent.VK_T); toolsMenu.add(VALIDATE_ACTION_24); toolsMenu.add(mainEditor.getOperatorTree().INFO_OPERATOR_ACTION_24); toolsMenu.addSeparator(); toolsMenu.add(ATTRIBUTE_DESCRIPTION_FILE_WIZARD_24); toolsMenu.add(ATTRIBUTE_EDITOR_ACTION_24); toolsMenu.add(ANOVA_CALCULATOR_ACTION_24); toolsMenu.add(messageViewer.CLEAR_MESSAGE_VIEWER_ACTION_24); toolsMenu.addSeparator(); toolsMenu.add(MANAGE_TEMPLATES_ACTION_24); toolsMenu.add(MANAGE_BUILDING_BLOCKS_ACTION_24); toolsMenu.addSeparator(); toolsMenu.add(CHECK_FOR_UPDATES_ACTION_24); toolsMenu.addSeparator(); toolsMenu.add(CHECK_FOR_JDBC_DRIVERS_ACTION_24); toolsMenu.add(SETTINGS_ACTION_24); menuBar.add(toolsMenu); // help menu JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic(KeyEvent.VK_H); JMenuItem aboutItem = new JMenuItem("About RapidMiner...", aboutIcon); aboutItem.setMnemonic(KeyEvent.VK_A); aboutItem.setToolTipText("Display information about RapidMiner"); aboutItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new AboutBox(MainFrame.this, RapidMiner.getLongVersion(), rapidMinerLogo).setVisible(true); } }); helpMenu.add(aboutItem); helpMenu.add(TUTORIAL_ACTION_24); JMenuItem contents = new JMenuItem("RapidMiner GUI Manual...", helpIcon); contents.setMnemonic(KeyEvent.VK_G); contents.setToolTipText("Browse the RapidMiner GUI Manual."); contents.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { URL manualResource = Tools.getResource("manual/RapidMinerGUIManual.html"); if (manualResource != null) Browser.showDialog(manualResource); else SwingTools.showVerySimpleErrorMessage("Cannot load GUI manual: file not found."); } }); helpMenu.add(contents); JMenuItem needSupport = new JMenuItem("Need Support?", supportIcon); needSupport.setMnemonic(KeyEvent.VK_S); needSupport.setToolTipText("Learn more about the possibilities of getting professional support for RapidMiner."); needSupport.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String message = "Do you need professional support? Do you want" + Tools.getLineSeparator() + "to optimize the achieved results? Do you need" + Tools.getLineSeparator() + "professional training which enables you to find" + Tools.getLineSeparator() + "better data mining solutions in shorter times?" + Tools.getLineSeparators(2) + "Check out the offers on" + Tools.getLineSeparators(2) + " http://rapid-i.com" + Tools.getLineSeparators(2) + " including" + Tools.getLineSeparators(2) + " - improved software versions, e.g. the Enterprise Edition of RapidMiner," + Tools.getLineSeparator() + " - professional support and other services," + Tools.getLineSeparator() + " - courses for data mining with RapidMiner," + Tools.getLineSeparator() + " - and individual solutions and extensions." + Tools.getLineSeparators(2); JOptionPane.showMessageDialog(MainFrame.this, message, "Need Support?", JOptionPane.INFORMATION_MESSAGE); } }); helpMenu.add(needSupport); List allPlugins = Plugin.getAllPlugins(); boolean addedSeparator = false; if (allPlugins.size() > 0) { Iterator i = allPlugins.iterator(); while (i.hasNext()) { final Plugin plugin = (Plugin) i.next(); if (plugin.showAboutBox()) { if (!addedSeparator) { addedSeparator = true; helpMenu.addSeparator(); } JMenuItem aboutPluginItem = new JMenuItem("About " + plugin.getName() + "...", pluginsIcon); aboutPluginItem.setToolTipText("Display information about " + plugin.getName()); aboutPluginItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { plugin.createAboutBox(MainFrame.this, rapidMinerLogo).setVisible(true); } }); helpMenu.add(aboutPluginItem); } } } menuBar.add(helpMenu); // Tool Bar /* toolBar.add(NEW_ACTION_32); toolBar.add(OPEN_ACTION_32); toolBar.add(SAVE_ACTION_32); toolBar.add(SAVE_AS_ACTION_32); toolBar.add(PRINT_ACTION_32); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(UNDO_ACTION_32); toolBar.add(REDO_ACTION_32); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(mainEditor.getOperatorTree().NEW_OPERATOR_ACTION_32); toolBar.add(mainEditor.getOperatorTree().DELETE_OPERATOR_ACTION_32); toolBar.add(mainEditor.getOperatorTree().NEW_BUILDING_BLOCK_ACTION_32); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(RUN_RESUME_ACTION_32); toolBar.add(STOP_ACTION_32); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(VALIDATE_ACTION_32); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(TOGGLE_EXPERT_MODE_ACTION_32); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(Box.createHorizontalGlue()); toolBar.add(EDIT_MODE_32); toolBar.add(RESULTS_MODE_32); */ toolBar.add(NEW_ACTION_24); toolBar.add(OPEN_ACTION_24); toolBar.add(SAVE_ACTION_24); toolBar.add(SAVE_AS_ACTION_24); toolBar.add(PRINT_ACTION_24); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(UNDO_ACTION_24); toolBar.add(REDO_ACTION_24); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(mainEditor.getOperatorTree().NEW_OPERATOR_ACTION_24); toolBar.add(mainEditor.getOperatorTree().DELETE_OPERATOR_ACTION_24); toolBar.add(mainEditor.getOperatorTree().NEW_BUILDING_BLOCK_ACTION_24); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(RUN_RESUME_ACTION_24); toolBar.add(STOP_ACTION_24); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(VALIDATE_ACTION_24); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(TOGGLE_EXPERT_MODE_ACTION_24); toolBar.addSeparator(); toolBar.addSeparator(); toolBar.add(Box.createHorizontalGlue()); toolBar.add(EDIT_MODE_24); toolBar.add(RESULTS_MODE_24); getContentPane().add(toolBar, BorderLayout.NORTH); getContentPane().add(statusBar, BorderLayout.SOUTH); statusBar.startClockThread(); addToUndoList(); enableActions(); pack(); changeMode(WELCOME_MODE); } protected Object readResolve() { this.printerJob = PrinterJob.getPrinterJob(); this.pageFormat = printerJob.defaultPage(); return this; } public void changeMode(int mode) { this.currentMode = mode; switch (this.currentMode) { case EDIT_MODE: mainCardLayout.show(mainPanel, EDIT_MODE_NAME); EDIT_MODE_24.setEnabled(false); EDIT_MODE_32.setEnabled(false); RESULTS_MODE_24.setEnabled(true); RESULTS_MODE_32.setEnabled(true); break; case RESULTS_MODE: mainCardLayout.show(mainPanel, RESULTS_MODE_NAME); EDIT_MODE_24.setEnabled(true); EDIT_MODE_32.setEnabled(true); RESULTS_MODE_24.setEnabled(false); RESULTS_MODE_32.setEnabled(false); break; case WELCOME_MODE: mainCardLayout.show(mainPanel, WELCOME_MODE_NAME); EDIT_MODE_24.setEnabled(true); EDIT_MODE_32.setEnabled(true); RESULTS_MODE_24.setEnabled(false); RESULTS_MODE_32.setEnabled(false); } } public void startTutorial() { if (close()) { new Tutorial(MainFrame.this).setVisible(true); } } public void setTutorialMode(boolean mode) { this.tutorialMode = mode; if (tutorialMode) { SAVE_ACTION_24.setEnabled(false); SAVE_ACTION_32.setEnabled(false); SAVE_AS_ACTION_24.setEnabled(false); SAVE_AS_ACTION_32.setEnabled(false); } else { SAVE_ACTION_24.setEnabled(false); SAVE_ACTION_32.setEnabled(false); SAVE_AS_ACTION_24.setEnabled(true); SAVE_AS_ACTION_32.setEnabled(true); } } public boolean isTutorialMode() { return this.tutorialMode; } public void setSystemMonitor(SystemMonitor newSystemMonitor) { loggingSplitPaneH.remove(this.systemMonitor); this.systemMonitor = newSystemMonitor; this.systemMonitor.setBorder(BorderFactory.createEtchedBorder()); loggingSplitPaneH.add(newSystemMonitor); } public void print() { switch (this.currentMode) { case EDIT_MODE: printerJob.setPrintable(new ComponentPrinter(mainEditor)); break; case RESULTS_MODE: printerJob.setPrintable(new ComponentPrinter(resultDisplay.getCurrentlyDisplayedComponent())); break; } if (printerJob.printDialog()) { try { printerJob.print(); } catch (PrinterException pe) { SwingTools.showSimpleErrorMessage("Printer error", pe); } } } public void printPreview() { Printable printer = null; switch (this.currentMode) { case EDIT_MODE: printer = new ComponentPrinter(mainEditor); break; case RESULTS_MODE: printer = new ComponentPrinter(resultDisplay.getCurrentlyDisplayedComponent()); break; } PreviewDialog dialog = new PreviewDialog("Print Preview", this, printer, pageFormat, 1); Component[] dialogComponents = dialog.getContentPane().getComponents(); for (Component c : dialogComponents) { if (c instanceof JToolBar) ((JToolBar)c).setFloatable(false); } dialog.pack(); dialog.setLocationRelativeTo(this); dialog.setVisible(true); } public void pageSetup() { this.pageFormat = printerJob.pageDialog(this.pageFormat); } public void export() { ExportDialog exportDialog = new ExportDialog("RapidMiner"); switch (this.currentMode) { case EDIT_MODE: exportDialog.showExportDialog(MainFrame.this, "Export", getOperatorTree(), getBaseName()); break; case RESULTS_MODE: exportDialog.showExportDialog(MainFrame.this, "Export", resultDisplay, getBaseName()); break; } } public int getEditorDividerLocation() { return mainEditor.getDividerLocation(); } public int getGroupSelectionDividerLocation() { return mainEditor.getGroupSelectionDivider(); } public int getMainDividerLocation() { if (this.lastLoggingDividerLocation >= 0) return splitPaneV.getHeight() - this.lastLoggingDividerLocation; else return splitPaneV.getDividerLocation(); } public int getLoggingDividerLocation() { if (this.lastMonitorDividerLocation >= 0) return loggingSplitPaneH.getWidth() - this.lastMonitorDividerLocation; else return loggingSplitPaneH.getDividerLocation(); } /** The first one is for the horizontal main divider, the second for the vertical * editor divider, for the logging divider, and the last one for the new operator * group selection divider. */ public void setDividerLocations(int mainH, int editorV, int loggingV, int groupSelectionV) { mainEditor.setDividerLocation(editorV); mainEditor.setGroupSelectionDivider(groupSelectionV); splitPaneV.setDividerLocation(mainH); loggingSplitPaneH.setDividerLocation(loggingV); } public void toggleLoggingViewer(boolean show) { if (show) { splitPaneV.add(loggingSplitPaneH); splitPaneV.setDividerLocation(splitPaneV.getHeight() - this.lastLoggingDividerLocation); this.lastLoggingDividerLocation = -1; } else { this.lastLoggingDividerLocation = splitPaneV.getHeight() - splitPaneV.getDividerLocation(); splitPaneV.remove(loggingSplitPaneH); } } public void toggleSystemMonitor(boolean show) { if (show) { loggingSplitPaneH.add(systemMonitor); loggingSplitPaneH.setDividerLocation(loggingSplitPaneH.getWidth() - this.lastMonitorDividerLocation); this.lastMonitorDividerLocation = -1; } else { this.lastMonitorDividerLocation = loggingSplitPaneH.getWidth() - loggingSplitPaneH.getDividerLocation(); loggingSplitPaneH.remove(systemMonitor); } } public void toggleExpertMode() { MainFrame.this.getPropertyTable().toggleExpertMode(); updateToggleExpertModeIcon(); } protected void updateToggleExpertModeIcon() { ((ToggleExpertModeAction) TOGGLE_EXPERT_MODE_ACTION_24).updateIcon(); ((ToggleExpertModeAction) TOGGLE_EXPERT_MODE_ACTION_32).updateIcon(); } public OperatorPropertyTable getPropertyTable() { return mainEditor.getPropertyTable(); } public LoggingViewer getMessageViewer() { return messageViewer; } public OperatorTree getOperatorTree() { return mainEditor.getOperatorTree(); } public MainProcessEditor getMainProcessEditor() { return this.mainEditor; } public ResultDisplay getResultDisplay() { return resultDisplay; } private synchronized void setProcessState(int state) { synchronized (process) { process.setProcessState(state); } } public synchronized int getProcessState() { if (process == null) { return Process.PROCESS_STATE_UNKNOWN; } else { synchronized (process) { return process.getProcessState(); } } } /** * @deprecated Use {@link #getProcess()} instead */ @Deprecated public synchronized final Process getExperiment() { return getProcess(); } public synchronized final Process getProcess() { return this.process; } // ==================================================== // M A I N A C T I O N S // =================================================== /** Creates a new process. */ public void newProcess() { if (close()) { stopProcess(); resultDisplay.clear(); setProcess(new Process(), true); addToUndoList(); changeMode(EDIT_MODE); } } /** Runs or resumes the current process. */ public void runProcess() { if (getProcessState() == Process.PROCESS_STATE_STOPPED) { // Run if ((isChanged() || (getProcess().getProcessFile() == null)) && !isTutorialMode()) { String saveDialogProperty = System.getProperty(PROPERTY_RAPIDMINER_GUI_SAVEDIALOG); boolean showSaveDialog = true; if (saveDialogProperty != null) showSaveDialog = Tools.booleanValue(saveDialogProperty, true); if (showSaveDialog) { RequestSaveDialog saveDialog = new RequestSaveDialog("Save Process?", "Save process before start?"); saveDialog.setVisible(true); if (saveDialog.shouldNotAskAgain()) { System.setProperty(PROPERTY_RAPIDMINER_GUI_SAVEDIALOG, "false"); ParameterService.writePropertyIntoMainUserConfigFile(PROPERTY_RAPIDMINER_GUI_SAVEDIALOG, "false"); } if (saveDialog.isOk()) { save(); } } } setProcessState(Process.PROCESS_STATE_RUNNING); enableActions(); synchronized (processThread) { synchronized (this.process) { MainFrame.this.process.getRootOperator().clearListeners(); processThread = new ProcessThread(MainFrame.this.process); MainFrame.this.process.getRootOperator().addProcessListener(statusBar); } } try { resultDisplay.clear(); synchronized (processThread) { processThread.start(); } } catch (Throwable t) { SwingTools.showSimpleErrorMessage("Cannot start process!", t); } finally { getOperatorTree().refresh(); } } else { // Resume // must be synchronized, otherwise this thread is not owner and resume will not work // due to this synchronization all other accesses to the thread must also be synchronized, // hence, the thread must not be null ! synchronized (processThread) { setProcessState(Process.PROCESS_STATE_RUNNING); enableActions(); processThread.notifyAll(); } } } /** Can be used to stop the currently running process. Please note that * the ProcessThread will still be running in the background until the current * operator is finished. */ public void stopProcess() { if (getProcessState() != Process.PROCESS_STATE_STOPPED) { getProcess().getLog().log("Process stopped. Completing current operator...", LogService.INIT); statusBar.setSpecialText("Process stopped. Completing current operator..."); if (processThread != null) { synchronized (processThread) { if (processThread.isAlive()) { processThread.setPriority(Thread.MIN_PRIORITY); processThread.stopProcess(); processThread.notifyAll(); } setProcess((Process)MainFrame.this.process.clone(), false); setProcessState(Process.PROCESS_STATE_STOPPED); enableActions(); statusBar.processEnded(); } } } } /** Will be invoked from the process thread after the process was successfully ended. */ void processEnded(IOContainer results) { if (results != null) { resultDisplay.setData(results, "Process results:"); changeMode(RESULTS_MODE); resultDisplay.showSomething(); File file = this.process.getProcessFile(); String resultName = file != null ? file.getName() : "unnamed"; RapidMinerGUI.getResultHistory().addResults(resultName, this.process.getRootOperator(), results); } statusBar.processEnded(); setProcessState(Process.PROCESS_STATE_STOPPED); enableActions(); } public void breakpointReached(Operator operator, IOContainer ioContainer, int location) { resultDisplay.setData(ioContainer, "Results in application " + operator.getApplyCount() + " of " + operator.getName() + ":"); changeMode(RESULTS_MODE); resultDisplay.showSomething(); ProcessThread.beep("breakpoint"); setProcessState(Process.PROCESS_STATE_PAUSED); enableActions(); } /** Since the mainframe toggles the resume itself this method does nothing. */ public void resume() {} /** Sets a new process and registers the MainFrame listener. Please note * that this method does not invoke {@link #processChanged()}. Do so * if necessary. * @deprecated Use {@link #setProcess(Process, boolean)} instead*/ @Deprecated public synchronized void setExperiment(Process process) { setProcess(process, true); } /** Sets a new process and registers the MainFrame listener. Please note * that this method only invoke {@link #processChanged()} if the parameter * newProcess is true. */ public synchronized void setProcess(Process process, boolean newProcess) { if (getProcessState() != Process.PROCESS_STATE_STOPPED) { if (processThread != null) { synchronized (processThread) { processThread.stopProcess(); } } } synchronized (process) { this.process = process; this.process.getRootOperator().clearListeners(); this.processThread = new ProcessThread(this.process); this.process.addBreakpointListener(this); this.process.addBreakpointListener(statusBar); this.process.getRootOperator().addProcessListener(statusBar); if (!newProcess) noActualChange = true; setOperator(this.process.getRootOperator(), newProcess); noActualChange = false; enableActions(); } setTitle(); } /** * Sets the root operator for all editors. This method should not be used to * change the process. Please use the method * {@link #setProcess(Process)} for this purpose which also sets some * listeners etc. */ private void setOperator(Operator root, boolean newProcess) { mainEditor.processChanged(root); } /** * Must be called when the process changed (such that is different from * the process before). Enables the correct actions if the process * can be saved to disk. */ public void processChanged() { if (!noActualChange) { boolean oldValue = this.changed; this.changed = true; addToUndoList(); if (!oldValue) { setTitle(); } synchronized (process) { if ((this.process.getProcessFile() != null) && !tutorialMode) { this.SAVE_ACTION_24.setEnabled(true); this.SAVE_ACTION_32.setEnabled(true); } // changes the XML text after each process change mainEditor.getXMLEditor().processChanged(this.process.getRootOperator()); } } } /** Returns true if the process has changed since the last save. */ public boolean isChanged() { return changed; } /** Notifies the main editor of the change of the currently selected operator. */ public void notifyEditorsOfChange(Operator currentlySelected) { mainEditor.setCurrentOperator(currentlySelected); } private void addToUndoList() { String lastStateXML = undoList.size() != 0 ? (String) undoList.get(undoList.size() - 1) : null; String currentStateXML = null; synchronized (process) { currentStateXML = this.process.getRootOperator().getXML(""); } if (currentStateXML != null) { if ((lastStateXML == null) || (!lastStateXML.equals(currentStateXML))) { if (undoIndex < (undoList.size() - 1)) { while (undoList.size() > (undoIndex + 1)) undoList.removeLast(); } undoList.add(currentStateXML); String maxSizeProperty = System.getProperty(PROPERTY_RAPIDMINER_GUI_UNDOLIST_SIZE); int maxSize = 20; try { if (maxSizeProperty != null) maxSize = Integer.parseInt(maxSizeProperty); } catch (NumberFormatException e) { LogService.getGlobal().log("Bad integer format for property 'rapidminer.gui.undolist.size', using default size of 20.", LogService.WARNING); } while (undoList.size() > maxSize) undoList.removeFirst(); undoIndex = undoList.size() - 1; enableUndoAction(); } } } public void undo() { if (undoIndex > 0) { undoIndex--; setProcessIntoStateAt(undoIndex); } enableUndoAction(); } public void redo() { if (undoIndex < undoList.size()) { undoIndex++; setProcessIntoStateAt(undoIndex); } enableUndoAction(); } private void enableUndoAction() { if (undoIndex > 0) { UNDO_ACTION_24.setEnabled(true); UNDO_ACTION_32.setEnabled(true); } else { UNDO_ACTION_24.setEnabled(false); UNDO_ACTION_32.setEnabled(false); } if (undoIndex < undoList.size() - 1){ REDO_ACTION_24.setEnabled(true); REDO_ACTION_32.setEnabled(true); } else { REDO_ACTION_24.setEnabled(false); REDO_ACTION_32.setEnabled(false); } } private void setProcessIntoStateAt(int undoIndex) { String stateXML = undoList.get(undoIndex); try { synchronized (process) { this.process.setupFromXML(stateXML); setProcess(this.process, true); // cannot use method processChanged() because this would add the // old state to the undo stack! this.changed = true; setTitle(); if ((this.process.getProcessFile() != null) && !tutorialMode) { this.SAVE_ACTION_24.setEnabled(true); this.SAVE_ACTION_32.setEnabled(true); } } } catch (Exception e) { SwingTools.showSimpleErrorMessage("While changing process:", e); } } /** * Sets the window title (RapidMiner + filename + an asterisk if process was * modified. */ private void setTitle() { if (hostname == null) { try { hostname = "@" + InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { hostname = ""; } } File file = null; if (this.process != null) { synchronized (process) { file = this.process.getProcessFile(); } } if (file != null) { setTitle(TITLE + hostname + " (" + file.getName() + (changed ? "*" : "") + ")"); } else { setTitle(TITLE + hostname); } } public String getBaseName() { if (this.process != null) { File file = null; synchronized (process) { file = this.process.getProcessFile(); } if (file != null) { String base = file.getName(); int dot = base.lastIndexOf("."); if (dot == -1) return base; else return base.substring(0, dot); } else { return "unnamed"; } } else { return "unnamed"; } } // //////////////////// File menu actions //////////////////// private boolean close() { if (changed) { File file = null; synchronized (process) { file = this.process.getProcessFile(); } if (file == null) { file = new File("unnamed.xml"); } int choice = JOptionPane.showConfirmDialog(this, "Save changes to '" + file + "'?", "Save changes", JOptionPane.YES_NO_CANCEL_OPTION); switch (choice) { case JOptionPane.YES_OPTION: save(); return true; case JOptionPane.NO_OPTION: if (getProcessState() != Process.PROCESS_STATE_STOPPED) { synchronized (processThread) { processThread.stopProcess(); } } return true; default: // cancel return false; } } else { return true; } } public void open() { if (close()) { File file = SwingTools.chooseFile(MainFrame.this, null, true, "xml", "process file"); if (file == null) { return; } open(file); } } public void open(File file) { open(file, true); } public void open(File file, boolean showInfo) { stopProcess(); try { Process process = RapidMiner.readProcessFile(file); setProcess(process, true); changeMode(EDIT_MODE); } catch (XMLException ex) { SwingTools.showSimpleErrorMessage("While loading '" + file + "'", ex); Process process = new Process(); process.setProcessFile(file); setProcess(process, true); changeMode(EDIT_MODE); mainEditor.changeToXMLEditor(); try { mainEditor.getXMLEditor().setText(Tools.readOutput(new BufferedReader(new FileReader(file)))); } catch (FileNotFoundException e) { SwingTools.showSimpleErrorMessage("While loading '" + file + "'", e); return; } catch (IOException e) { SwingTools.showSimpleErrorMessage("While loading '" + file + "'", e); return; } } catch (InstantiationException e) { SwingTools.showSimpleErrorMessage("While loading '" + file + "'", e); return; } catch (IllegalAccessException e) { SwingTools.showSimpleErrorMessage("While loading '" + file + "'", e); return; } catch (IOException e) { SwingTools.showSimpleErrorMessage("While loading '" + file + "'", e); return; } resultDisplay.clear(); SAVE_ACTION_24.setEnabled(false); SAVE_ACTION_32.setEnabled(false); changed = false; synchronized (process) { RapidMinerGUI.useProcessFile(this.process); updateRecentFileList(); addToUndoList(); setTitle(); if (showInfo && Tools.booleanValue(System.getProperty(PROPERTY_RAPIDMINER_GUI_PROCESSINFO_SHOW), true)) { String text = this.process.getRootOperator().getUserDescription(); if ((text != null) && (text.length() != 0)) { ProcessInfoScreen infoScreen = new ProcessInfoScreen(this, file.getName(), text); infoScreen.setVisible(true); } } } } public void save() { try { synchronized (process) { File file = this.process.getProcessFile(); if (file == null) { file = SwingTools.chooseFile(this, new File("."), false, "xml", "process file"); if (file == null) return; else this.process.setProcessFile(file); } this.process.save(); SAVE_ACTION_24.setEnabled(false); SAVE_ACTION_32.setEnabled(false); changed = false; setTitle(); RapidMinerGUI.useProcessFile(this.process); updateRecentFileList(); //mainEditor.processChanged(this.process.getRootOperator()); } } catch (IOException ex) { SwingTools.showSimpleErrorMessage("Cannot save process file!", ex); } } public void saveAs() { File file = SwingTools.chooseFile(MainFrame.this, null, false, "xml", "process file"); if (file != null) { getProcess().setProcessFile(file); save(); } } public void saveAsTemplate() { synchronized (process) { Operator rootOperator = MainFrame.this.process.getRootOperator(); SaveAsTemplateDialog dialog = new SaveAsTemplateDialog(MainFrame.this, rootOperator); dialog.setVisible(true); if (dialog.isOk()) { Template template = dialog.getTemplate(rootOperator); String name = template.getName(); try { File templateFile = ParameterService.getUserConfigFile(name + ".template"); template.save(templateFile); File templateXmlFile = ParameterService.getUserConfigFile(name + ".xml"); MainFrame.this.process.save(templateXmlFile); } catch (IOException ioe) { SwingTools.showSimpleErrorMessage("Cannot write template files:", ioe); } } } } public void exit() { if (changed) { File file = null; synchronized (process) { file = this.process.getProcessFile(); } if (file == null) { file = new File("unnamed.xml"); } switch (JOptionPane.showConfirmDialog(this, "Save changes to '" + file + "'?", "Save changes", JOptionPane.YES_NO_CANCEL_OPTION)) { case JOptionPane.YES_OPTION: save(); if (changed) return; break; case JOptionPane.NO_OPTION: break; case JOptionPane.CANCEL_OPTION: default: return; } } else if (JOptionPane.showConfirmDialog(this, "Really exit?", "Exit RapidMiner", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { return; } stopProcess(); dispose(); RapidMinerGUI.quit(0); //System.exit(0); } /** Updates the list of recently used files. */ public void updateRecentFileList() { recentFilesMenu.removeAll(); List recentFiles = RapidMinerGUI.getRecentFiles(); Iterator i = recentFiles.iterator(); int j = 1; while (i.hasNext()) { final File recentFile = (File) i.next(); JMenuItem menuItem = new JMenuItem(j + " " + recentFile.getPath()); menuItem.setMnemonic('0' + j); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { open(recentFile); } }); recentFilesMenu.add(menuItem); j++; } } public void windowOpened(WindowEvent e) {} public void windowClosing(WindowEvent e) { exit(); } public void windowClosed(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowActivated(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} /** * Enables and disables all actions according to the current state * (process running, operator selected... */ public void enableActions() { // must be invoked first since it might be that conditions change RUN_RESUME_ACTION_24.updateState(); RUN_RESUME_ACTION_32.updateState(); boolean[] currentStates = new boolean[ConditionalAction.NUMBER_OF_CONDITIONS]; Operator op = mainEditor.getOperatorTree().getSelectedOperator(); if (op != null) { currentStates[ConditionalAction.OPERATOR_SELECTED] = true; if (op instanceof OperatorChain) currentStates[ConditionalAction.OPERATOR_CHAIN_SELECTED] = true; if (op.getParent() == null) currentStates[ConditionalAction.ROOT_SELECTED] = true; else if (op.getParent().getNumberOfOperators() > 1) currentStates[ConditionalAction.SIBLINGS_EXIST] = true; } // important: if whole method is synchronized this will lead to dead locks... synchronized (process) { currentStates[ConditionalAction.PROCESS_STOPPED] = getProcessState() == Process.PROCESS_STATE_STOPPED; currentStates[ConditionalAction.PROCESS_PAUSED] = getProcessState() == Process.PROCESS_STATE_PAUSED; currentStates[ConditionalAction.PROCESS_RUNNING] = ((getProcessState() == Process.PROCESS_STATE_RUNNING) || (getProcessState() == Process.PROCESS_STATE_PAUSED)); } currentStates[ConditionalAction.CLIPBOARD_FILLED] = mainEditor.getOperatorTree().getClipBoard() != null; currentStates[ConditionalAction.XML_VIEW] = mainEditor.isXMLViewActive(); currentStates[ConditionalAction.DESCRIPTION_VIEW] = mainEditor.isDescriptionViewActive(); ConditionalAction.updateAll(currentStates); } /** * This methods provide plugins the possibility to modify the menus */ public void removeMenu(int index) { menuBar.remove(menuBar.getMenu(index)); } public void removeMenuItem(int menuIndex, int itemIndex) { menuBar.getMenu(menuIndex).remove(itemIndex); } public void addMenuItem(int menuIndex, int itemIndex, JMenuItem item) { menuBar.getMenu(menuIndex).add(item, itemIndex); } public void addMenu(int menuIndex, JMenu menu) { menuBar.add(menu, menuIndex); } public void addMenuSeparator(int menuIndex) { menuBar.getMenu(menuIndex).addSeparator(); } }