/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.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.Component;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import com.rapidminer.BreakpointListener;
import com.rapidminer.Process;
import com.rapidminer.ProcessLocation;
import com.rapidminer.ProcessStorageListener;
import com.rapidminer.RapidMiner;
import com.rapidminer.RapidMiner.ExitMode;
import com.rapidminer.core.io.data.source.DataSourceFactoryRegistry;
import com.rapidminer.core.license.ProductConstraintManager;
import com.rapidminer.gui.actions.AboutAction;
import com.rapidminer.gui.actions.Actions;
import com.rapidminer.gui.actions.AutoWireAction;
import com.rapidminer.gui.actions.ExitAction;
import com.rapidminer.gui.actions.ExportProcessAction;
import com.rapidminer.gui.actions.ImportDataAction;
import com.rapidminer.gui.actions.ImportProcessAction;
import com.rapidminer.gui.actions.NewPerspectiveAction;
import com.rapidminer.gui.actions.PauseAction;
import com.rapidminer.gui.actions.PropagateRealMetaDataAction;
import com.rapidminer.gui.actions.RedoAction;
import com.rapidminer.gui.actions.RunAction;
import com.rapidminer.gui.actions.SaveAction;
import com.rapidminer.gui.actions.SaveAsAction;
import com.rapidminer.gui.actions.SettingsAction;
import com.rapidminer.gui.actions.StopAction;
import com.rapidminer.gui.actions.ToggleAction;
import com.rapidminer.gui.actions.UndoAction;
import com.rapidminer.gui.actions.ValidateAutomaticallyAction;
import com.rapidminer.gui.actions.ValidateProcessAction;
import com.rapidminer.gui.actions.export.ShowPrintAndExportDialogAction;
import com.rapidminer.gui.actions.startup.NewAction;
import com.rapidminer.gui.actions.startup.OpenAction;
import com.rapidminer.gui.dialog.UnknownParametersInfoDialog;
import com.rapidminer.gui.flow.ErrorTable;
import com.rapidminer.gui.flow.ProcessPanel;
import com.rapidminer.gui.flow.processrendering.model.ProcessRendererModel;
import com.rapidminer.gui.license.LicenseTools;
import com.rapidminer.gui.look.Colors;
import com.rapidminer.gui.operatortree.OperatorTree;
import com.rapidminer.gui.operatortree.OperatorTreePanel;
import com.rapidminer.gui.operatortree.actions.CutCopyPasteDeleteAction;
import com.rapidminer.gui.operatortree.actions.ToggleBreakpointItem;
import com.rapidminer.gui.osx.OSXAdapter;
import com.rapidminer.gui.osx.OSXQuitListener;
import com.rapidminer.gui.processeditor.ExtendedProcessEditor;
import com.rapidminer.gui.processeditor.MacroViewer;
import com.rapidminer.gui.processeditor.NewOperatorEditor;
import com.rapidminer.gui.processeditor.ProcessContextProcessEditor;
import com.rapidminer.gui.processeditor.ProcessEditor;
import com.rapidminer.gui.processeditor.XMLEditor;
import com.rapidminer.gui.processeditor.results.ResultDisplay;
import com.rapidminer.gui.processeditor.results.ResultDisplayTools;
import com.rapidminer.gui.properties.OperatorPropertyPanel;
import com.rapidminer.gui.security.PasswordManager;
import com.rapidminer.gui.tools.ProcessGUITools;
import com.rapidminer.gui.tools.ProgressThread;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.ResourceMenu;
import com.rapidminer.gui.tools.ResultWarningPreventionRegistry;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.SystemMonitor;
import com.rapidminer.gui.tools.bubble.BubbleWindow;
import com.rapidminer.gui.tools.dialogs.ConfirmDialog;
import com.rapidminer.gui.tools.dialogs.DecisionRememberingConfirmDialog;
import com.rapidminer.gui.tools.dialogs.wizards.dataimport.DataImportWizardFactory;
import com.rapidminer.gui.tools.dialogs.wizards.dataimport.DataImportWizardRegistry;
import com.rapidminer.gui.tools.ioobjectcache.IOObjectCacheViewer;
import com.rapidminer.gui.tools.logging.LogViewer;
import com.rapidminer.operator.IOContainer;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorChain;
import com.rapidminer.operator.UnknownParameterInformation;
import com.rapidminer.operator.ports.Port;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.repository.RepositoryLocation;
import com.rapidminer.repository.gui.RepositoryBrowser;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.Observable;
import com.rapidminer.tools.Observer;
import com.rapidminer.tools.ParameterService;
import com.rapidminer.tools.ProcessTools;
import com.rapidminer.tools.SystemInfoUtilities;
import com.rapidminer.tools.SystemInfoUtilities.OperatingSystem;
import com.rapidminer.tools.config.ConfigurationManager;
import com.rapidminer.tools.config.gui.ConfigurableDialog;
import com.rapidminer.tools.container.Pair;
import com.rapidminer.tutorial.Tutorial;
import com.rapidminer.tutorial.gui.TutorialBrowser;
import com.rapidminer.tutorial.gui.TutorialSelector;
import com.vlsolutions.swing.docking.DockGroup;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockingContext;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.toolbars.ToolBarContainer;
/**
* 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, Simon Fischer, Sebastian Land, Marius Helf, Jan Czogalla
*/
@SuppressWarnings("deprecation")
public class MainFrame extends ApplicationFrame implements WindowListener {
/**
* This listener takes care of changes relevant to the {@link MainFrame} itself, such as the
* {@link ProcessThread} and saving, loading or setting a process.
*
* @since 7.5
* @author Jan Czogalla
*/
private class MainProcessListener implements ExtendedProcessEditor, ProcessStorageListener {
private Process oldProcess;
@Override
public void processChanged(Process process) {
Process currentProcess = oldProcess;
oldProcess = process;
boolean firstProcess = currentProcess == null;
if (!firstProcess) {
currentProcess.removeObserver(processObserver);
if (currentProcess.getProcessState() != Process.PROCESS_STATE_STOPPED) {
if (processThread != null) {
processThread.stopProcess();
}
}
}
if (process != null) {
synchronized (process) {
processThread = new ProcessThread(process);
process.addObserver(processObserver, true);
process.addBreakpointListener(breakpointListener);
if (VALIDATE_AUTOMATICALLY_ACTION.isSelected()) {
// not running at this point!
validateProcess(true);
}
}
}
processPanel.getProcessRenderer().repaint();
setTitle();
getStatusBar().clearSpecialText();
processPanel.getViewPort().firePropertyChange(ProcessPanel.SCROLLER_UPDATE, false, true);
}
@Override
public void setSelection(List<Operator> selection) {
}
@Override
public void processUpdated(Process process) {
setTitle();
if (getProcess().getProcessLocation() != null) {
MainFrame.this.SAVE_ACTION.setEnabled(processModel.hasChanged());
}
processPanel.getProcessRenderer().repaint();
}
@Override
public void stored(Process process) {
SAVE_ACTION.setEnabled(false);
setTitle();
updateRecentFileList();
}
@Override
public void opened(Process process) {
if (process.getImportMessage() != null && process.getImportMessage().contains("error")) {
SwingTools.showLongMessage("import_message", process.getImportMessage());
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SAVE_ACTION.setEnabled(false);
setTitle();
}
});
List<UnknownParameterInformation> unknownParameters = null;
synchronized (process) {
RapidMinerGUI.useProcessFile(process);
unknownParameters = process.getUnknownParameters();
}
updateRecentFileList();
// show unsupported parameters info?
if (unknownParameters != null && unknownParameters.size() > 0) {
final UnknownParametersInfoDialog unknownParametersInfoDialog = new UnknownParametersInfoDialog(
MainFrame.this, unknownParameters);
if (SwingUtilities.isEventDispatchThread()) {
unknownParametersInfoDialog.setVisible(true);
} else {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
unknownParametersInfoDialog.setVisible(true);
}
});
} catch (Exception e) {
LogService.getRoot().log(Level.WARNING, "Error opening the unknown parameter dialog: " + e, e);
}
}
}
}
@Override
public void processViewChanged(Process process) {}
}
private static final long serialVersionUID = 1L;
/** 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 the " The maximum number of examples in a data set for which
* default plotter settings will be generated."
*/
public static final String PROPERTY_RAPIDMINER_GUI_PLOTTER_DEFAULT_MAXIMUM = "rapidminer.gui.plotter.default.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."
*
* @deprecated Since 7.5. Use {@link RapidMinerGUI#PROPERTY_RAPIDMINER_GUI_UNDOLIST_SIZE}
* instead
*/
@Deprecated
public static final String PROPERTY_RAPIDMINER_GUI_UNDOLIST_SIZE = RapidMinerGUI.PROPERTY_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 "Shows process info screen after loading?" */
public static final String PROPERTY_RAPIDMINER_GUI_PROCESSINFO_SHOW = "rapidminer.gui.processinfo.show";
public static final String PROPERTY_RAPIDMINER_GUI_SAVE_BEFORE_RUN = "rapidminer.gui.save_before_run";
public static final String PROPERTY_RAPIDMINER_GUI_SAVE_ON_PROCESS_CREATION = "rapidminer.gui.save_on_process_creation";
/**
* The property determining whether or not to switch to result view when results are produced.
*/
public static final String PROPERTY_RAPIDMINER_GUI_AUTO_SWITCH_TO_RESULTVIEW = "rapidminer.gui.auto_switch_to_resultview";
/** Log level of the LoggingViewer. */
public static final String PROPERTY_RAPIDMINER_GUI_LOG_LEVEL = "rapidminer.gui.log_level";
private static final int MAX_LOCATION_TITLE_LENGTH = 150;
private static final String getFrameTitle() {
return "RapidMiner Studio "
+ LicenseTools.translateProductEdition(ProductConstraintManager.INSTANCE.getActiveLicense()) + " "
+ RapidMiner.getLongVersion();
}
// --------------------------------------------------------------------------------
public static final int EDIT_MODE = 0;
public static final int RESULTS_MODE = 1;
public final transient Action AUTO_WIRE = new AutoWireAction(this);
public final transient Action NEW_ACTION = new NewAction();
public final transient Action OPEN_ACTION = new OpenAction();
public final transient SaveAction SAVE_ACTION = new SaveAction();
public final transient Action SAVE_AS_ACTION = new SaveAsAction();
public final transient ToggleAction PROPAGATE_REAL_METADATA_ACTION = new PropagateRealMetaDataAction(this);
private final transient Action importDataAction = new ImportDataAction();
public final transient Action IMPORT_PROCESS_ACTION = new ImportProcessAction();
public final transient Action EXPORT_PROCESS_ACTION = new ExportProcessAction();
// ---------- Export as Image/Print actions -----------------
public final transient Action EXPORT_ACTION = new ShowPrintAndExportDialogAction(false);
public final transient Action EXIT_ACTION = new ExitAction(this);
public final transient RunAction RUN_ACTION = new RunAction(this);
public final transient Action PAUSE_ACTION = new PauseAction(this);
public final transient Action STOP_ACTION = new StopAction(this);
public final transient Action VALIDATE_ACTION = new ValidateProcessAction(this);
public final transient ToggleAction VALIDATE_AUTOMATICALLY_ACTION = new ValidateAutomaticallyAction();
private transient JButton runRemoteToolbarButton;
public final transient Action NEW_PERSPECTIVE_ACTION = new NewPerspectiveAction(this);
public final transient Action SETTINGS_ACTION = new SettingsAction();
public final transient Action UNDO_ACTION = new UndoAction(this);
public final transient Action REDO_ACTION = new RedoAction(this);
public final transient Action MANAGE_CONFIGURABLES_ACTION = new ResourceAction(true, "manage_configurables") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(final ActionEvent e) {
ConfigurableDialog dialog = new ConfigurableDialog(getProcess());
dialog.setVisible(true);
}
};
// --------------------------------------------------------------------------------
// DOCKING
public static final DockGroup DOCK_GROUP_ROOT = new DockGroup("root");
public static final DockGroup DOCK_GROUP_RESULTS = new DockGroup("results");
private final DockingContext dockingContext = new DockingContext();
private final DockingDesktop dockingDesktop = new DockingDesktop("mainDesktop", dockingContext);
private final MainProcessListener mainProcessListener = new MainProcessListener();
private final Actions actions = new Actions(this);
private final OperatorDocumentationBrowser operatorDocumentationBrowser = new OperatorDocumentationBrowser();
private final OperatorTreePanel operatorTree = new OperatorTreePanel(this);
private final ErrorTable errorTable = new ErrorTable(this);
private final OperatorPropertyPanel propertyPanel = new OperatorPropertyPanel(this);
private final XMLEditor xmlEditor = new XMLEditor(this);
private final ProcessContextProcessEditor processContextEditor = new ProcessContextProcessEditor();
private final ProcessPanel processPanel = new ProcessPanel(this);
private final ProcessRendererModel processModel = processPanel.getProcessRenderer().getModel();
private final NewOperatorEditor newOperatorEditor = new NewOperatorEditor(
processPanel.getProcessRenderer().getDragListener());
private final RepositoryBrowser repositoryBrowser = new RepositoryBrowser(
processPanel.getProcessRenderer().getDragListener());
private final MacroViewer macroViewer = new MacroViewer();
private final ResultDisplay resultDisplay = ResultDisplayTools.makeResultDisplay();
private final LogViewer logViewer = new LogViewer(this);
private final IOObjectCacheViewer ioobjectCacheViewer = new IOObjectCacheViewer(RapidMiner.getGlobalIOObjectCache());
private final SystemMonitor systemMonitor = new SystemMonitor();
private final PerspectiveController perspectiveController = new PerspectiveController(dockingContext);
private final TutorialSelector tutorialSelector = new TutorialSelector(this, perspectiveController.getModel());
private final TutorialBrowser tutorialBrowser = new TutorialBrowser(tutorialSelector);
/**
* @deprecated use {@link #perspectiveController} instead
*/
@Deprecated
private final Perspectives perspectives = new Perspectives(perspectiveController);
/** the bubble which displays a warning that no result ports are connected */
private BubbleWindow noResultConnectionBubble;
/** the bubble which displays a warning that a port must receive input but is not connected */
private BubbleWindow missingInputBubble;
/**
* the bubble which displays a warning that a parameter must be set as he has no default value
*/
private BubbleWindow missingParameterBubble;
private final JMenuBar menuBar;
private final MainToolBar toolBar;
private final JMenu fileMenu;
private final JMenu editMenu;
private final JMenu processMenu;
private final JMenu settingsMenu;
private final JMenu connectionsMenu;
private final JMenu viewMenu;
private final JMenu helpMenu;
private final JMenu extensionsMenu;
private DockableMenu dockableMenu;
private final JMenu recentFilesMenu = new ResourceMenu("recent_files");
private Insets menuBarInsets = new Insets(0, 0, 0, 5);
/**
* The host name 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 ProcessThread processThread;
private final MetaDataUpdateQueue metaDataUpdateQueue = new MetaDataUpdateQueue(this);
@Deprecated
private transient final DataImportWizardRegistry importWizardRegistry = new DataImportWizardRegistry() {
private final List<DataImportWizardFactory> factories = new ArrayList<>();
@Override
public void register(DataImportWizardFactory factory) {
if (factory == null) {
throw new IllegalArgumentException("factory must not be null");
}
synchronized (factories) {
factories.add(factory);
}
}
@Override
public List<DataImportWizardFactory> getFactories() {
synchronized (factories) {
return new ArrayList<>(factories);
}
}
};
// --------------------------------------------------------------------------------
// LISTENERS And OBSERVERS
private final PerspectiveChangeListener perspectiveChangeListener = new PerspectiveChangeListener() {
@Override
public void perspectiveChangedTo(Perspective perspective) {
// check all ConditionalActions on perspective switch
getActions().enableActions();
// try to request focus for the process renderer so actions are enabled after
// perspective switch and
// ProcessRenderer is visible
if (getProcessPanel().getProcessRenderer().isShowing()) {
getProcessPanel().getProcessRenderer().requestFocusInWindow();
}
}
};
private long lastUpdate = 0;
private final Timer updateTimer = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
updateProcessNow();
}
}) {
private static final long serialVersionUID = 1L;
{
setRepeats(false);
}
};
/**
* @deprecated since 7.5; a view is automatically pushed on the undo stack when using
* {@link ProcessRendererModel#setDisplayedChain(OperatorChain)}
*/
@Deprecated
public void addViewSwitchToUndo() {}
/** Lets the process model check for changes in the process */
private void updateProcessNow() {
lastUpdate = System.currentTimeMillis();
if (processModel.checkForNewUndoStep()) {
validateProcess(false);
}
processPanel.getProcessRenderer().repaint();
}
public void validateProcess(final boolean force) {
if (force || getProcessState() != Process.PROCESS_STATE_RUNNING) {
metaDataUpdateQueue.validate(getProcess(), force || VALIDATE_AUTOMATICALLY_ACTION.isSelected());
} else {
processModel.fireProcessUpdated();
}
}
public boolean isProcessRendererFocused() {
return processPanel.getProcessRenderer().hasFocus();
}
private transient final Observer<Process> processObserver = new Observer<Process>() {
@Override
public void update(final Observable<Process> observable, final Process arg) {
// if (process.getProcessState() == Process.PROCESS_STATE_RUNNING) {
// return;
// }
if (System.currentTimeMillis() - lastUpdate > 500) {
updateProcessNow();
} else {
if (getProcessState() == Process.PROCESS_STATE_RUNNING) {
if (!updateTimer.isRunning()) {
updateTimer.start();
}
} else {
updateProcessNow();
}
}
}
};
private transient final BreakpointListener breakpointListener = new BreakpointListener() {
@Override
public void breakpointReached(final Process process, final Operator operator, final IOContainer ioContainer,
final int location) {
if (process.equals(getProcess())) {
RUN_ACTION.setState(process.getProcessState());
ProcessThread.beep("breakpoint");
MainFrame.this.toFront();
MainFrame.this.selectAndShowOperator(operator, true);
resultDisplay.showData(ioContainer,
"Breakpoint in " + operator.getName() + ", application " + operator.getApplyCount());
}
}
/** Since the mainframe triggers the resume itself this method does nothing. */
@Override
public void resume() {
RUN_ACTION.setState(getProcessState());
}
};
private JToolBar buttonToolbar;
// --------------------------------------------------------------------------------
/** Creates a new main frame containing the RapidMiner GUI. */
public MainFrame() {
super(getFrameTitle());
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(this);
if (SystemInfoUtilities.getOperatingSystem() == OperatingSystem.OSX) {
// load dock icon from resources and adapt UI via OSXAdapter class
try {
OSXQuitListener quitListener = new OSXQuitListener() {
@Override
public void quit() {
RapidMiner.quit(ExitMode.NORMAL);
}
};
OSXAdapter.adaptUI(this, SETTINGS_ACTION, new AboutAction(this), quitListener);
} catch (Throwable t) {
// catch everything - in case the OSX adapter is called without being on a OS X
// system
// or the Java classes have been removed from OS X JRE it will just log an error
// instead of breaking the program start-up
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.MainFrame.could_not_adapt_OSX_look_and_feel", t);
}
}
addProcessListeners();
SwingTools.setFrameIcon(this);
// load perspectives now because otherwise the WSDesktop class does not know the nodes and
// won't restore the user customized perspective
perspectiveController.loadAll();
dockingContext.addDesktop(dockingDesktop);
dockingDesktop.registerDockable(repositoryBrowser);
dockingDesktop.registerDockable(operatorTree);
dockingDesktop.registerDockable(propertyPanel);
dockingDesktop.registerDockable(processPanel);
dockingDesktop.registerDockable(xmlEditor);
dockingDesktop.registerDockable(newOperatorEditor);
dockingDesktop.registerDockable(errorTable);
dockingDesktop.registerDockable(resultDisplay);
dockingDesktop.registerDockable(logViewer);
dockingDesktop.registerDockable(ioobjectCacheViewer);
dockingDesktop.registerDockable(systemMonitor);
dockingDesktop.registerDockable(operatorDocumentationBrowser);
dockingDesktop.registerDockable(processContextEditor);
dockingDesktop.registerDockable(processPanel.getProcessRenderer().getOverviewPanel());
dockingDesktop.registerDockable(macroViewer);
dockingDesktop.registerDockable(tutorialBrowser);
// Test
ToolBarContainer toolBarContainer = ToolBarContainer.createDefaultContainer(true, true, true, true);
toolBarContainer.setBorder(BorderFactory.createEmptyBorder(6, 3, 0, 3));
toolBarContainer.setOpaque(true);
toolBarContainer.setBackground(Colors.WINDOW_BACKGROUND);
getContentPane().add(toolBarContainer, BorderLayout.CENTER);
toolBarContainer.add(dockingDesktop, BorderLayout.CENTER);
systemMonitor.startMonitorThread();
resultDisplay.getDockKey().setCloseEnabled(false);
resultDisplay.getDockKey().setAutoHideEnabled(false);
resultDisplay.init(this);
// menu bar
menuBar = new JMenuBar();
setJMenuBar(menuBar);
fileMenu = new ResourceMenu("file");
fileMenu.setMargin(menuBarInsets);
fileMenu.add(NEW_ACTION);
fileMenu.add(OPEN_ACTION);
updateRecentFileList();
fileMenu.add(recentFilesMenu);
fileMenu.addSeparator();
fileMenu.add(SAVE_ACTION);
fileMenu.add(SAVE_AS_ACTION);
fileMenu.addSeparator();
fileMenu.add(importDataAction);
fileMenu.add(IMPORT_PROCESS_ACTION);
fileMenu.add(EXPORT_PROCESS_ACTION);
menuBar.add(fileMenu);
// edit menu
((ResourceAction) actions.INFO_OPERATOR_ACTION).addToActionMap(JComponent.WHEN_FOCUSED, true, true, null,
getProcessPanel().getProcessRenderer(), getOperatorTree());
((ResourceAction) actions.TOGGLE_ACTIVATION_ITEM).addToActionMap(JComponent.WHEN_FOCUSED, true, true, null,
getProcessPanel().getProcessRenderer(), getOperatorTree());
((ResourceAction) actions.RENAME_OPERATOR_ACTION).addToActionMap(JComponent.WHEN_FOCUSED, true, true, null,
getProcessPanel().getProcessRenderer(), getOperatorTree());
// not added for ProcessRenderer because there the DELETE_SELECTED_CONNECTION action is
// active
((ResourceAction) actions.DELETE_OPERATOR_ACTION).addToActionMap(JComponent.WHEN_FOCUSED, true, true, null,
getOperatorTree());
((ResourceAction) actions.TOGGLE_ALL_BREAKPOINTS).addToActionMap(JComponent.WHEN_FOCUSED, true, true, null,
getProcessPanel().getProcessRenderer(), getOperatorTree());
editMenu = new ResourceMenu("edit");
editMenu.setMargin(menuBarInsets);
editMenu.add(UNDO_ACTION);
editMenu.add(REDO_ACTION);
editMenu.addSeparator();
editMenu.add(actions.INFO_OPERATOR_ACTION);
editMenu.add(actions.TOGGLE_ACTIVATION_ITEM.createMenuItem());
editMenu.add(actions.RENAME_OPERATOR_ACTION);
editMenu.addSeparator();
editMenu.add(CutCopyPasteDeleteAction.CUT_ACTION);
editMenu.add(CutCopyPasteDeleteAction.COPY_ACTION);
editMenu.add(CutCopyPasteDeleteAction.PASTE_ACTION);
editMenu.add(CutCopyPasteDeleteAction.DELETE_ACTION);
editMenu.addSeparator();
for (ToggleBreakpointItem item : actions.TOGGLE_BREAKPOINT) {
editMenu.add(item.createMenuItem());
}
editMenu.add(actions.TOGGLE_ALL_BREAKPOINTS.createMenuItem());
// editMenu.add(actions.MAKE_DIRTY_ACTION);
menuBar.add(editMenu);
// process menu
processMenu = new ResourceMenu("process");
processMenu.setMargin(menuBarInsets);
processMenu.add(RUN_ACTION);
processMenu.add(PAUSE_ACTION);
processMenu.add(STOP_ACTION);
processMenu.addSeparator();
processMenu.add(PROPAGATE_REAL_METADATA_ACTION.createMenuItem());
processMenu.add(VALIDATE_ACTION);
processMenu.add(VALIDATE_AUTOMATICALLY_ACTION.createMenuItem());
processMenu.addSeparator();
processMenu.add(AUTO_WIRE);
processMenu.add(processPanel.getFlowVisualizer().ALTER_EXECUTION_ORDER.createMenuItem());
JMenu layoutMenu = new ResourceMenu("process_layout");
layoutMenu.add(processPanel.getProcessRenderer().getArrangeOperatorsAction());
layoutMenu.add(processPanel.getProcessRenderer().getAutoFitAction());
processMenu.add(layoutMenu);
menuBar.add(processMenu);
// view menu
viewMenu = new ResourceMenu("view");
viewMenu.setMargin(menuBarInsets);
viewMenu.add(new PerspectiveMenu(perspectiveController));
viewMenu.add(NEW_PERSPECTIVE_ACTION);
viewMenu.add(dockableMenu = new DockableMenu(dockingContext));
viewMenu.add(perspectiveController.getRestoreDefaultAction());
menuBar.add(viewMenu);
// create settings menu (will be added in finishInitialization())
settingsMenu = new ResourceMenu("settings");
settingsMenu.setMargin(menuBarInsets);
// connections menu
connectionsMenu = new ResourceMenu("connections");
connectionsMenu.setMargin(menuBarInsets);
menuBar.add(connectionsMenu);
// help menu
helpMenu = new ResourceMenu("help");
// extensions menu
extensionsMenu = new ResourceMenu("extensions");
extensionsMenu.setMargin(menuBarInsets);
// main tool bar
toolBar = new MainToolBar(this);
getStatusBar().setBackground(Colors.WINDOW_BACKGROUND);
getContentPane().add(toolBar, BorderLayout.NORTH);
getContentPane().add(getStatusBar(), BorderLayout.SOUTH);
getStatusBar().startClockThread();
setProcess(new Process(), true);
perspectiveController.getModel().addPerspectiveChangeListener(perspectiveChangeListener);
pack();
metaDataUpdateQueue.start();
}
/**
* Adds all relevant {@link ProcessEditor ProcessEditors}. Adds the {@link MainProcessListener}
* as first.
*/
private void addProcessListeners() {
processModel.addProcessEditor(mainProcessListener);
processModel.addProcessStorageListener(mainProcessListener);
processModel.addProcessEditor(actions);
processModel.addProcessEditor(xmlEditor);
processModel.addProcessEditor(propertyPanel);
processModel.addProcessEditor(operatorTree);
processModel.addProcessEditor(operatorDocumentationBrowser);
processModel.addProcessEditor(processPanel);
processModel.addProcessEditor(errorTable);
processModel.addProcessEditor(processContextEditor);
processModel.addProcessEditor(getStatusBar());
processModel.addProcessEditor(resultDisplay);
processModel.addProcessEditor(macroViewer);
}
/**
* Finishes the MainFrame initialization. Should be called after all extension have been
* initialized.
*/
public void finishInitialization() {
// Configurators (if they exist)
if (!ConfigurationManager.getInstance().isEmpty()) {
connectionsMenu.addSeparator();
connectionsMenu.add(MANAGE_CONFIGURABLES_ACTION);
}
// add export and exit as last file menu actions
fileMenu.addSeparator();
fileMenu.add(EXPORT_ACTION);
fileMenu.addSeparator();
fileMenu.add(EXIT_ACTION);
// Password Manager
settingsMenu.add(PasswordManager.OPEN_WINDOW);
if (SystemInfoUtilities.getOperatingSystem() != OperatingSystem.OSX || !OSXAdapter.isAdapted()) {
settingsMenu.add(SETTINGS_ACTION);
}
// add settings menu as second last menu or third last if there are entries in the help menu
menuBar.add(settingsMenu);
// add extensions menu as last menu or second last if there are entries in the help
// menu
menuBar.add(extensionsMenu);
// Add Help menu as last entry if it is not empty
if (helpMenu.getItemCount() > 0) {
helpMenu.setMargin(menuBarInsets);
menuBar.add(helpMenu);
}
}
public OperatorPropertyPanel getPropertyPanel() {
return propertyPanel;
}
/**
* Returns a registry for {@link DataImportWizardFactory} instances. The factories are used to
* populate menus such as the main import menu.
*
* @return the registry
* @deprecated Use {@link DataSourceFactoryRegistry} instead. Registering a
* {@link DataImportWizardRegistry} will not have an effect anymore.
*/
@Deprecated
public DataImportWizardRegistry getDataImportWizardRegistry() {
return importWizardRegistry;
}
public LogViewer getLogViewer() {
return logViewer;
}
public NewOperatorEditor getNewOperatorEditor() {
return newOperatorEditor;
}
public OperatorTree getOperatorTree() {
return operatorTree.getOperatorTree();
}
public Actions getActions() {
return actions;
}
public ResultDisplay getResultDisplay() {
return resultDisplay;
}
/**
* @return the toolbar button for running processes on the Server
*/
public JButton getRunRemoteToolbarButton() {
return runRemoteToolbarButton;
}
public int getProcessState() {
Process process = getProcess();
if (process == null) {
return Process.PROCESS_STATE_UNKNOWN;
} else {
return process.getProcessState();
}
}
/**
* @deprecated Use {@link #getProcess()} instead
*/
@Deprecated
public final Process getExperiment() {
return getProcess();
}
public final Process getProcess() {
return processModel.getProcess();
}
// ====================================================
// M A I N A C T I O N S
// ===================================================
/**
* Creates a new process. If there are unsaved changes, the user will be asked to save their
* work.
*/
public void newProcess() {
newProcess(true);
}
/**
* Creates a new process. Depending on the given parameter, the user will or will not be asked
* to save unsaved changes.
*
* @param checkforUnsavedWork
* Iff {@code true} the user is asked to save their unsaved work (if any), otherwise
* unsaved work is discarded without warning.
*/
public void newProcess(final boolean checkforUnsavedWork) {
// ask for confirmation before stopping the currently running process and opening a new one!
if (getProcessState() == Process.PROCESS_STATE_RUNNING || getProcessState() == Process.PROCESS_STATE_PAUSED) {
if (SwingTools.showConfirmDialog("close_running_process",
ConfirmDialog.YES_NO_OPTION) != ConfirmDialog.YES_OPTION) {
return;
}
}
ProgressThread newProgressThread = new ProgressThread("new_process") {
@Override
public void run() {
// Invoking close() will ask the user to save their work if there are unsaved
// changes. This method can be skipped if it is already clear that changes should be
// discarded.
boolean resetProcess = checkforUnsavedWork ? close(false) : true;
if (resetProcess) {
// process changed -> clear undo history
stopProcess();
setProcess(new Process(), true);
if (!"false"
.equals(ParameterService.getParameterValue(PROPERTY_RAPIDMINER_GUI_SAVE_ON_PROCESS_CREATION))) {
SaveAction.saveAsync(getProcess());
}
// always have save action enabled. If process is not yet associated with
// location SaveAs will be used
SAVE_ACTION.setEnabled(true);
}
}
};
newProgressThread.setIndeterminate(true);
newProgressThread.setCancelable(false);
newProgressThread.start();
}
/**
* Runs or resumes the current process. If the process is started, checks for potential errors
* first and prevents execution unless the user has disabled the pre-run check.
*/
public void runProcess() {
runProcess(true);
}
/**
* Runs or resumes the current process.
*
* @param precheckBeforeExecution
* if {@code true} and the process is started, checks for potential errors first and
* prevents execution unless the user has disabled the pre-run check
*/
public void runProcess(boolean precheckBeforeExecution) {
if (getProcessState() == Process.PROCESS_STATE_STOPPED) {
// Run
if (isChanged() || getProcess().getProcessLocation() == null) {
if (DecisionRememberingConfirmDialog.confirmAction("save_before_run",
PROPERTY_RAPIDMINER_GUI_SAVE_BEFORE_RUN)) {
SaveAction.saveAsync(getProcess());
}
}
// don't run process if showstoppers are present
// this only returns true if the user did not disable the strict process check in the
// preferences
if (precheckBeforeExecution && doesProcessContainShowstoppers()) {
return;
}
processThread = new ProcessThread(getProcess());
try {
processThread.start();
} catch (Exception t) {
SwingTools.showSimpleErrorMessage("cannot_start_process", t);
}
} else {
getProcess().resume();
}
}
/**
* 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().getLogger().info("Process stopped. Completing current operator.");
getStatusBar().setSpecialText("Process stopped. Completing current operator.");
if (processThread != null) {
if (processThread.isAlive()) {
processThread.setPriority(Thread.MIN_PRIORITY);
processThread.stopProcess();
}
}
}
}
public void pauseProcess() {
if (getProcessState() == Process.PROCESS_STATE_RUNNING) {
getProcess().getLogger().info("Process paused. Completing current operator.");
getStatusBar().setSpecialText("Process paused. Completing current operator.");
if (processThread != null) {
processThread.pauseProcess();
}
}
}
/** Will be invoked from the process thread after the process was successfully ended. */
void processEnded(final Process process, final IOContainer results) {
if (process.equals(getProcess())) {
if (results != null) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
MainFrame.this.toFront();
}
});
}
}
if (process.equals(getProcess())) {
if (results != null) {
resultDisplay.showData(results, "Process results");
}
}
}
/**
* 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 void setExperiment(final Process process) {
setProcess(process, true);
}
/**
* Sets a (new) process.
*
* @param process
* the process to be set
* @param newProcess
* whether the process should be treated as new
*/
public void setProcess(final Process process, final boolean newProcess) {
setOrOpenProcess(process, newProcess, false);
}
/**
* Sets or loads a (new) process. Resets the undo stack for the model if necessary. <br/>
* Note: It should hold: open => newProcess
*/
private void setOrOpenProcess(final Process process, final boolean newProcess, final boolean open) {
boolean firstProcess = getProcess() == null;
processModel.setProcess(process, newProcess, open);
if (newProcess) {
enableUndoAction();
if (!firstProcess) {
// VLDocking appears to get nervous when applying two perspectives while the
// window is not yet visible. So to avoid that we set design and then welcome
// during startup, avoid applying design if this is the first process we create.
perspectiveController.showPerspective(PerspectiveModel.DESIGN);
}
}
updateProcessNow();
}
/**
* Sets a new process and registers the MainFrame's listeners.
*
* @deprecated Since 7.5. Use {@link #setProcess(Process, boolean)} instead, since undo steps
* are automatically added when necessary.
*/
@Deprecated
public void setProcess(final Process process, final boolean newProcess, final boolean addToUndoList) {
setProcess(process, newProcess);
}
/**
* 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.
*
* @deprecated this method is no longer necessary (and does nothing) since the MainFrame
* observes the process using an Observer pattern. See {@link #processObserver}.
*/
@Deprecated
public void processChanged() {}
/** Returns true if the process has changed since the last save. */
public boolean isChanged() {
return processModel.hasChanged();
}
public void undo() {
Exception e = processModel.undo();
if (e != null) {
SwingTools.showSimpleErrorMessage("while_changing_process", e);
}
}
public void redo() {
Exception e = processModel.redo();
if (e != null) {
SwingTools.showSimpleErrorMessage("while_changing_process", e);
}
}
public void enableUndoAction() {
UNDO_ACTION.setEnabled(processModel.hasUndoSteps());
REDO_ACTION.setEnabled(processModel.hasRedoSteps());
}
/**
* Returns <code>true</code> if the current process has undo steps available.
*
* @return
*/
public boolean hasUndoSteps() {
return processModel.hasUndoSteps();
}
/**
* Returns <code>true</code> if the current process has redo steps available.
*
* @return
*/
public boolean hasRedoSteps() {
return processModel.hasRedoSteps();
}
/**
* Sets the window title (RapidMiner + filename + an asterisk if process was modified.
*/
public void setTitle() {
if (hostname == null) {
try {
hostname = " @ " + InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
hostname = "";
}
}
Process process = getProcess();
if (process != null) {
ProcessLocation loc = process.getProcessLocation();
String locString;
if (loc != null) {
locString = loc.toString();
// location string exceeding arbitrary number will be cut into repository name +
// /.../ + process name
if (locString.length() > MAX_LOCATION_TITLE_LENGTH) {
locString = RepositoryLocation.REPOSITORY_PREFIX + process.getRepositoryLocation().getRepositoryName()
+ RepositoryLocation.SEPARATOR + "..." + RepositoryLocation.SEPARATOR + loc.getShortName();
}
} else {
locString = "<new process";
}
setTitle(locString + (isChanged() ? "*" : "") + (loc == null ? ">" : "") + " \u2013 " + getFrameTitle()
+ hostname);
} else {
setTitle(getFrameTitle() + hostname);
}
}
// //////////////////// File menu actions ////////////////////
/**
* Closes the current process
*
* @param askForConfirmation
* if <code>true</code>, will prompt the user if he really wants to close the current
* process
* @return
*/
public boolean close(final boolean askForConfirmation) {
if (!isChanged()) {
return true;
}
ProcessLocation loc = getProcess().getProcessLocation();
String locName;
if (loc != null) {
locName = loc.getShortName();
} else {
locName = "unnamed";
}
switch (SwingTools.showConfirmDialog("save", ConfirmDialog.YES_NO_CANCEL_OPTION, locName)) {
case ConfirmDialog.YES_OPTION:
SaveAction.save(getProcess());
// it may happen that save() does not actually save the process, because the
// user hits cancel in the
// saveAs dialog or an error occurs. In this case the process won't be marked as
// unchanged. Thus,
// we return the process changed status.
return !isChanged();
case ConfirmDialog.NO_OPTION:
// ask for confirmation before stopping the currently running process (if
// askForConfirmation=true)
if (askForConfirmation) {
if (RapidMinerGUI.getMainFrame().getProcessState() == Process.PROCESS_STATE_RUNNING
|| RapidMinerGUI.getMainFrame().getProcessState() == Process.PROCESS_STATE_PAUSED) {
if (SwingTools.showConfirmDialog("close_running_process",
ConfirmDialog.YES_NO_OPTION) != ConfirmDialog.YES_OPTION) {
return false;
}
}
}
if (getProcessState() != Process.PROCESS_STATE_STOPPED) {
synchronized (processThread) {
processThread.stopProcess();
}
}
return true;
default: // cancel
return false;
}
}
public boolean close() {
return close(true);
}
/**
* @deprecated Since 7.5. Use {@link #setOpenedProcess(Process)}, the other parameters are
* irrelevant.
*/
@Deprecated
public void setOpenedProcess(final Process process, final boolean showInfo, final String sourceName) {
setOpenedProcess(process);
}
/** Opens the specified process. */
public void setOpenedProcess(final Process process) {
setOrOpenProcess(process, true, true);
}
public void exit(final boolean relaunch) {
if (isChanged()) {
ProcessLocation loc = getProcess().getProcessLocation();
String locName;
if (loc != null) {
locName = loc.getShortName();
} else {
locName = "unnamed";
}
switch (SwingTools.showConfirmDialog("save", ConfirmDialog.YES_NO_CANCEL_OPTION, locName)) {
case ConfirmDialog.YES_OPTION:
SaveAction.save(getProcess());
if (isChanged()) {
return;
}
break;
case ConfirmDialog.NO_OPTION:
break;
case ConfirmDialog.CANCEL_OPTION:
default:
return;
}
} else {
if (!relaunch) { // in this case we have already confirmed
// ask for special confirmation before exiting RapidMiner while a process is
// running!
if (getProcessState() == Process.PROCESS_STATE_RUNNING
|| getProcessState() == Process.PROCESS_STATE_PAUSED) {
if (SwingTools.showConfirmDialog("exit_despite_running_process",
ConfirmDialog.YES_NO_OPTION) == ConfirmDialog.NO_OPTION) {
return;
}
} else {
int answer = ConfirmDialog.showConfirmDialog(ApplicationFrame.getApplicationFrame(), "exit",
ConfirmDialog.YES_NO_OPTION, RapidMinerGUI.PROPERTY_CONFIRM_EXIT, ConfirmDialog.YES_OPTION);
if (answer != ConfirmDialog.YES_OPTION) {
return;
}
}
}
}
stopProcess();
dispose();
RapidMiner.quit(relaunch ? RapidMiner.ExitMode.RELAUNCH : RapidMiner.ExitMode.NORMAL);
}
/** Updates the list of recently used files. */
public void updateRecentFileList() {
recentFilesMenu.removeAll();
List<ProcessLocation> recentFiles = RapidMinerGUI.getRecentFiles();
int j = 1;
for (final ProcessLocation recentLocation : recentFiles) {
JMenuItem menuItem = new JMenuItem(j + " " + recentLocation.toMenuString());
menuItem.setMnemonic('0' + j);
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (RapidMinerGUI.getMainFrame().close()) {
com.rapidminer.gui.actions.OpenAction.open(recentLocation, true);
}
}
});
recentFilesMenu.add(menuItem);
j++;
}
}
/**
* Update the elements of the main tool bar.
*/
public void updateToolbar() {
toolBar.update();
}
@Override
public void windowOpened(final WindowEvent e) {}
@Override
public void windowClosing(final WindowEvent e) {
exit(false);
}
@Override
public void windowClosed(final WindowEvent e) {}
@Override
public void windowIconified(final WindowEvent e) {}
@Override
public void windowDeiconified(final WindowEvent e) {}
@Override
public void windowActivated(final WindowEvent e) {}
@Override
public void windowDeactivated(final WindowEvent e) {}
/**
* This methods provide plugins the possibility to modify the menus
*/
public void removeMenu(final int index) {
menuBar.remove(menuBar.getMenu(index));
}
public void removeMenuItem(final int menuIndex, final int itemIndex) {
menuBar.getMenu(menuIndex).remove(itemIndex);
}
public void addMenuItem(final int menuIndex, final int itemIndex, final JMenuItem item) {
menuBar.getMenu(menuIndex).add(item, itemIndex);
}
public void addMenu(int menuIndex, final JMenu menu) {
menu.setMargin(menuBarInsets);
if (menuIndex < -1 || menuIndex >= menuBar.getComponentCount()) {
menuIndex = -1;
}
menuBar.add(menu, menuIndex);
}
public void addMenuSeparator(final int menuIndex) {
menuBar.getMenu(menuIndex).addSeparator();
}
// / LISTENERS
public List<Operator> getSelectedOperators() {
return processModel.getSelectedOperators();
}
public Operator getFirstSelectedOperator() {
List<Operator> selectedOperators = processModel.getSelectedOperators();
return selectedOperators.isEmpty() ? null : selectedOperators.get(0);
}
/**
* @deprecated use {@link #addExtendedProcessEditor(ExtendedProcessEditor)} instead.
*/
@Deprecated
public void addProcessEditor(final ProcessEditor p) {
processModel.addProcessEditor(p);
}
/**
* Adds the given {@link ExtendedProcessEditor} listener.
*/
public void addExtendedProcessEditor(final ExtendedProcessEditor p) {
processModel.addProcessEditor(p);
}
/**
* @deprecated use {@link #removeExtendedProcessEditor(ExtendedProcessEditor)} instead.
*/
@Deprecated
public void removeProcessEditor(final ProcessEditor p) {
processModel.removeProcessEditor(p);
}
/**
* Removes the given {@link ExtendedProcessEditor} listener.
*/
public void removeExtendedProcessEditor(final ExtendedProcessEditor p) {
processModel.removeProcessEditor(p);
}
public void addProcessStorageListener(final ProcessStorageListener listener) {
processModel.addProcessStorageListener(listener);
}
public void removeProcessStorageListener(final ProcessStorageListener listener) {
processModel.removeProcessStorageListener(listener);
}
/**
* Selects and shows a single given operator. Will switch the displayed chain either to the
* parent or the selected chain, depending on the provided flag. This can be used to easily
* update the view. Convenience method.
*
* @param currentlySelected
* @param showParent
* @since 7.5
* @see #selectOperators(List)
* @see #selectOperator(Operator)
*/
public void selectAndShowOperator(Operator currentlySelected, boolean showParent) {
if (currentlySelected == null) {
currentlySelected = getProcess().getRootOperator();
}
OperatorChain parent = currentlySelected.getParent();
// if this is not a chain, it can not be displayed as such!
showParent |= !(currentlySelected instanceof OperatorChain);
// root chain has no parent
showParent &= parent != null;
OperatorChain dispChain = showParent ? parent : (OperatorChain) currentlySelected;
processModel.setDisplayedChainAndFire(dispChain);
selectOperators(Collections.singletonList(currentlySelected));
}
public void selectOperator(Operator currentlySelected) {
if (currentlySelected == null) {
currentlySelected = getProcess().getRootOperator();
}
selectOperators(Collections.singletonList(currentlySelected));
}
public void selectOperators(List<Operator> currentlySelected) {
if (currentlySelected == null) {
currentlySelected = Collections.<Operator> singletonList(getProcess().getRootOperator());
}
for (Operator op : currentlySelected) {
Process selectedProcess = op.getProcess();
if (selectedProcess == null || selectedProcess != getProcess()) {
SwingTools.showVerySimpleErrorMessage("op_deleted", op.getName());
return;
}
}
processModel.clearOperatorSelection();
processModel.addOperatorsToSelection(currentlySelected);
processModel.fireOperatorSelectionChanged(currentlySelected);
}
public void fireProcessUpdated() {
processModel.fireProcessUpdated();
}
public DockingDesktop getDockingDesktop() {
return dockingDesktop;
}
/**
* @deprecated use {@link #getPerspectiveController()} instead
*/
@Deprecated
public Perspectives getPerspectives() {
return perspectives;
}
public PerspectiveController getPerspectiveController() {
return perspectiveController;
}
public void handleBrokenProxessXML(final ProcessLocation location, final String xml, final Exception e) {
SwingTools.showSimpleErrorMessage("while_loading", e, location.toString(), e.getMessage());
Process process = new Process();
process.setProcessLocation(location);
setProcess(process, true);
perspectiveController.showPerspective(PerspectiveModel.DESIGN);
xmlEditor.setText(xml);
}
public OperatorDocumentationBrowser getOperatorDocViewer() {
return operatorDocumentationBrowser;
}
public ProcessPanel getProcessPanel() {
return processPanel;
}
public void registerDockable(final Dockable dockable) {
dockingDesktop.registerDockable(dockable);
}
public void processHasBeenSaved() {
processModel.processHasBeenSaved();
}
public ProcessContextProcessEditor getProcessContextEditor() {
return processContextEditor;
}
public RepositoryBrowser getRepositoryBrowser() {
return repositoryBrowser;
}
public Component getXMLEditor() {
return xmlEditor;
}
/**
* This returns the file menu to change menu entries
*/
public JMenu getFileMenu() {
return fileMenu;
}
/**
* This returns the connections menu to change menu entries
*/
public JMenu getConnectionsMenu() {
return connectionsMenu;
}
/**
* This returns the settings menu to change menu entries.
*
* @deprecated the tools menu was split into multiple menus. Use {@link #getConnectionsMenu()}
* or {@link #getSettingsMenu()} instead
*/
@Deprecated
public JMenu getToolsMenu() {
return settingsMenu;
}
/**
* This returns the settings menu to change menu entries
*
* @return the settings menu
*/
public JMenu getSettingsMenu() {
return settingsMenu;
}
/**
* This returns the edit menu to change menu entries
*/
public JMenu getEditMenu() {
return editMenu;
}
/**
* This returns the process menu to change menu entries
*/
public JMenu getProcessMenu() {
return processMenu;
}
/**
* This returns the help menu to change menu entries
*/
public JMenu getHelpMenu() {
return helpMenu;
}
/**
* This returns the extensions menu to change menu entries
*
* @since 7.0.0
*/
public JMenu getExtensionsMenu() {
return extensionsMenu;
}
public DockableMenu getDockableMenu() {
return dockableMenu;
}
/**
*
* @return the toolbar containing e.g. process run buttons
*/
public JToolBar getButtonToolbar() {
return buttonToolbar;
}
/**
* The {@link TutorialSelector} holds the selected {@link Tutorial}.
*
* @return the registered tutorial selector
* @since 7.0.0
*/
public TutorialSelector getTutorialSelector() {
return tutorialSelector;
}
/**
* Checks the current process for potential problems. If a problem is deemed big enough (e.g. an
* operator that requires input but is not connected), returns {@code true}. If no showstoppers
* are found, returns {@code false}. This method also alerts the user about the problems so
* after it returns, nothing else needs to be done.
*
* @return {@code true} if the process contains a problem which should prevent process
* execution; {@code false} otherwise
*/
private boolean doesProcessContainShowstoppers() {
// prevent two bubbles on top of each other
getProcessPanel().getOperatorWarningHandler().killWarningBubble();
// if any operator has a mandatory parameter with no value and no default value. As it
// cannot predict execution behavior (e.g. Branch operators), this may turn up problems
// which would not occur during process execution
Process process = getProcess();
Pair<Operator, ParameterType> missingParamPair = ProcessTools.getOperatorWithoutMandatoryParameter(process);
if (missingParamPair != null) {
// if there is already one of these, kill
if (missingParameterBubble != null) {
missingParameterBubble.killBubble(true);
}
missingParameterBubble = ProcessGUITools.displayPrecheckMissingMandatoryParameterWarning(
missingParamPair.getFirst(), missingParamPair.getSecond());
return true;
}
// if any port needs data but is not connected. As it cannot predict execution behavior
// (e.g. Branch operators), this may turn up problems which would not occur during
// process execution
Port missingInputPort = ProcessTools.getPortWithoutMandatoryConnection(process);
if (missingInputPort != null) {
// if there is already one of these, kill
if (missingInputBubble != null) {
missingInputBubble.killBubble(true);
}
missingInputBubble = ProcessGUITools.displayPrecheckInputPortDisconnectedWarning(missingInputPort);
return true;
}
// if there is already one of these, kill
if (noResultConnectionBubble != null) {
noResultConnectionBubble.killBubble(true);
}
// if the process has no connected result ports and the last executed
// process root child operator does not prevent a warning bubble we need
// to notify the user that no output port is connected
boolean isWarnOnNoResultProcess = Boolean
.parseBoolean(ParameterService.getParameterValue(RapidMinerGUI.PROPERTY_SHOW_NO_RESULT_WARNING));
if (isWarnOnNoResultProcess) {
boolean connectedResultPort = ProcessTools.isProcessConnectedToResultPort(process);
Operator lastExecutedProcessRootChild = ProcessTools.getLastExecutedRootChild(process);
if (!connectedResultPort && lastExecutedProcessRootChild != null
&& !ResultWarningPreventionRegistry.isResultWarningSuppressed(lastExecutedProcessRootChild)) {
noResultConnectionBubble = ProcessGUITools.displayPrecheckNoResultPortInformation(process);
return true;
}
}
// no showstopper
return false;
}
}