/*
* 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.Dimension;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import javax.swing.JOptionPane;
import javax.swing.LookAndFeel;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import com.rapidminer.NoOpUserError;
import com.rapidminer.Process;
import com.rapidminer.RapidMiner;
import com.rapidminer.gui.dialog.InitialSettingsDialog;
import com.rapidminer.gui.dialog.ResultHistory;
import com.rapidminer.gui.look.RapidLookAndFeel;
import com.rapidminer.gui.look.fc.BookmarkIO;
import com.rapidminer.gui.tools.CommunityUpdateManager;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.UpdateManager;
import com.rapidminer.gui.tools.VersionNumber;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.OperatorService;
import com.rapidminer.tools.ParameterService;
import com.rapidminer.tools.Tools;
import com.rapidminer.tools.plugin.Plugin;
/**
* The main class if RapidMiner is startet in GUI mode. This class keeps the
* {@link MainFrame} and some other GUI relevant informations and methods.
*
* @author Ingo Mierswa, Simon Fischer
* @version $Id: RapidMinerGUI.java,v 1.43 2008/09/15 09:31:56 ingomierswa Exp $
*/
public class RapidMinerGUI extends RapidMiner {
public static final String PROPERTY_GEOMETRY_X = "rapidminer.gui.geometry.x";
public static final String PROPERTY_GEOMETRY_Y = "rapidminer.gui.geometry.y";
public static final String PROPERTY_GEOMETRY_WIDTH = "rapidminer.gui.geometry.width";
public static final String PROPERTY_GEOMETRY_HEIGHT = "rapidminer.gui.geometry.height";
public static final String PROPERTY_GEOMETRY_DIVIDER_MAIN = "rapidminer.gui.geometry.divider.main";
public static final String PROPERTY_GEOMETRY_DIVIDER_EDITOR = "rapidminer.gui.geometry.divider.editor";;
public static final String PROPERTY_GEOMETRY_DIVIDER_LOGGING = "rapidminer.gui.geometry.divider.logging";
public static final String PROPERTY_GEOMETRY_DIVIDER_GROUPSELECTION = "rapidminer.gui.geometry.divider.groupselection";
public static final String PROPERTY_EXPERT_MODE = "rapidminer.gui.expertmode";
// --- Properties ---
public static final String PROPERTY_RAPIDMINER_GUI_UPDATE_CHECK = "rapidminer.gui.update.check";
public static final String PROPERTY_RAPIDMINER_GUI_LOOK_AND_FEEL = "rapidminer.gui.look";
public static final String PROPERTY_RAPIDMINER_GUI_MAX_STATISTICS_ROWS = "rapidminer.gui.max_statistics_rows";
public static final String PROPERTY_RAPIDMINER_GUI_MAX_SORTABLE_ROWS = "rapidminer.gui.max_sortable_rows";
public static final String[] LOOK_AND_FEELS = {
"modern",
"classic"
};
public static final int LOOK_AND_FEEL_MODERN = 0;
public static final int LOOK_AND_FEEL_CLASSIC = 1;
static {
RapidMiner.registerRapidMinerProperty(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_UPDATE_CHECK, "Check for new RapidMiner versions at start up time?", true));
RapidMiner.registerRapidMinerProperty(new ParameterTypeCategory(PROPERTY_RAPIDMINER_GUI_LOOK_AND_FEEL, "Indicates which look and feel should be used (you have to restart RapidMiner in order to see changes).", LOOK_AND_FEELS, LOOK_AND_FEEL_MODERN));
RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_MAX_STATISTICS_ROWS, "Indicates the maximum number of rows for the automatic calculation of statistics and other time intensive data viewing actions.", 1, Integer.MAX_VALUE, 100000));
RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_MAX_SORTABLE_ROWS, "Indicates the maximum number of rows for sortable tables.", 1, Integer.MAX_VALUE, 100000));
}
private static final int NUMBER_OF_RECENT_FILES = 8;
private static MainFrame mainFrame;
private static LinkedList<File> recentFiles = new LinkedList<File>();
private static ResultHistory resultHistory = new ResultHistory();
/**
* This thread listens for System shutdown and cleans up after shutdown.
* This included saving the recent file list and other GUI properties.
*/
protected static class ShutdownHook extends Thread {
public void run() {
LogService.getGlobal().log("Running shutdown sequence.", LogService.INIT);
RapidMinerGUI.saveRecentFileList();
RapidMinerGUI.saveGUIProperties();
}
}
private static UpdateManager updateManager = new CommunityUpdateManager();
public void run(File file) throws Exception {
// check if resources were copied
URL logoURL = Tools.getResource("rapidminer_logo.png");
if (logoURL == null) {
System.err.println("ERROR: cannot find resources. Probably the ant target 'copy-resources' must be performed!");
RapidMiner.quit(1);
}
RapidMiner.showSplash();
RapidMiner.splashMessage("Basic Initialization");
RapidMiner.init();
// set locale fix to US
RapidMiner.splashMessage("Using US Local");
Locale.setDefault(Locale.US);
JOptionPane.setDefaultLocale(Locale.US);
// check if this version is started for the first time
RapidMiner.splashMessage("Workspace Initialization");
performInitialSettings();
RapidMiner.splashMessage("Setting up Look and Feel");
setupToolTipManager();
setupGUI();
RapidMiner.splashMessage("Loading History");
loadRecentFileList();
RapidMiner.splashMessage("Creating Frame");
setMainFrame(new MainFrame());
RapidMiner.splashMessage("Initialize Global Logging");
LogService.getGlobal().initGUI();
RapidMiner.splashMessage("Loading GUI Properties");
loadGUIProperties(mainFrame);
RapidMiner.splashMessage("Initialize Plugin GUI");
Plugin.initPluginGuis(mainFrame);
RapidMiner.splashMessage("Showing Frame");
mainFrame.setVisible(true);
RapidMiner.splashMessage("Initialize Process Logging");
mainFrame.getProcess().getLog().initGUI();
RapidMiner.splashMessage("Initialize Checks");
Plugin.initFinalChecks();
RapidMiner.splashMessage("Ready.");
RapidMiner.hideSplash();
// file from command line or Welcome Dialog
if (file != null) {
mainFrame.open(file);
mainFrame.changeMode(MainFrame.EDIT_MODE);
} else {
mainFrame.changeMode(MainFrame.WELCOME_MODE);
}
// check for updates
Plugin.initPluginUpdateManager();
String updateProperty = System.getProperty(PROPERTY_RAPIDMINER_GUI_UPDATE_CHECK);
if (Tools.booleanValue(updateProperty, true)) {
boolean check = true;
Calendar lastCheck = loadLastUpdateCheckDate();
if (lastCheck != null) {
Calendar currentDate = Calendar.getInstance();
currentDate.add(Calendar.DAY_OF_YEAR, -7);
if (!lastCheck.before(currentDate))
check = false;
}
if (check) {
checkForUpdates(false);
}
}
}
private void setupToolTipManager() {
// setup tool tip text manager
ToolTipManager manager = ToolTipManager.sharedInstance();
manager.setDismissDelay(25000); // original: 4000
manager.setInitialDelay(1500); // original: 750
manager.setReshowDelay(50); // original: 500
}
/** This default implementation only setup the tool tip durations. Subclasses might
* override this method. */
protected void setupGUI() throws NoOpUserError {
String lookAndFeelString = System.getProperty(PROPERTY_RAPIDMINER_GUI_LOOK_AND_FEEL);
int lookAndFeel = LOOK_AND_FEEL_MODERN;
if (lookAndFeelString != null) {
try {
lookAndFeel = Integer.parseInt(lookAndFeelString);
} catch (NumberFormatException e) {
LogService.getGlobal().log("Cannot setup look and feel ('" + lookAndFeelString + "'), using default.", LogService.INIT);
}
}
if (lookAndFeel == LOOK_AND_FEEL_CLASSIC) {
try {
Class<?> clazz = Class.forName("com.jgoodies.looks.plastic.PlasticLookAndFeel");
Method method = clazz.getMethod("setTabStyle", String.class);
method.invoke( null, new Object[]{ "Metal" } );
Class themeClazz = Class.forName("com.jgoodies.looks.plastic.PlasticTheme");
Class skyBluerClazz = Class.forName("com.jgoodies.looks.plastic.theme.SkyBluer");
Object theme = skyBluerClazz.newInstance();
method = clazz.getMethod("setPlasticTheme", themeClazz );
method.invoke( null, new Object[]{ theme } );
Class lafClazz = Class.forName("com.jgoodies.looks.plastic.Plastic3DLookAndFeel");
Object lafInstance = lafClazz.newInstance();
UIManager.setLookAndFeel((LookAndFeel) lafInstance);
SwingTools.setIconType(LOOK_AND_FEELS[LOOK_AND_FEEL_CLASSIC]);
OperatorService.reloadIcons();
} catch (Throwable e) {
LogService.getGlobal().log("Cannot setup classic look and feel, using default.", LogService.INIT);
}
} else if (lookAndFeel == LOOK_AND_FEEL_MODERN) {
try {
System.setProperty(BookmarkIO.PROPERTY_BOOKMARKS_DIR, ParameterService.getUserRapidMinerDir().getAbsolutePath());
System.setProperty(BookmarkIO.PROPERTY_BOOKMARKS_FILE, ".bookmarks");
UIManager.setLookAndFeel(new RapidLookAndFeel());
SwingTools.setIconType(LOOK_AND_FEELS[LOOK_AND_FEEL_MODERN]);
OperatorService.reloadIcons();
} catch (Throwable e) {
e.printStackTrace();
LogService.getGlobal().log("Cannot setup modern look and feel, using default.", LogService.INIT);
}
} else {
LogService.getGlobal().log("Cannot setup look and feel ('" + lookAndFeel + "'), using default.", LogService.INIT);
}
}
public static void setMainFrame(MainFrame mf) {
mainFrame = mf;
}
public static MainFrame getMainFrame() {
return mainFrame;
}
private void performInitialSettings() {
boolean firstStart = false;
VersionNumber lastVersionNumber = null;
VersionNumber currentVersionNumber = new VersionNumber(getLongVersion());
File lastVersionFile = new File(ParameterService.getUserRapidMinerDir(), "lastversion");
if (!lastVersionFile.exists()) {
firstStart = true;
} else {
String versionString = null;
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(lastVersionFile));
versionString = in.readLine();
} catch (IOException e) {
LogService.getGlobal().logWarning("Cannot read global version file of last used version.");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
LogService.getGlobal().logError("Cannnot close stream to file " + lastVersionFile);
}
}
}
if (versionString != null) {
lastVersionNumber = new VersionNumber(versionString);
if (currentVersionNumber.compareTo(lastVersionNumber) > 0) {
firstStart = true;
}
} else {
firstStart = true;
}
}
// init this version (workspace etc.)
if (firstStart) {
performFirstInitialization(lastVersionNumber, currentVersionNumber);
}
// write version file
writeLastVersion(lastVersionFile);
}
private void performFirstInitialization(VersionNumber lastVersion, VersionNumber currentVersion) {
if (currentVersion != null)
LogService.getGlobal().logNote("Performing upgrade" + (lastVersion != null ? " from version " + lastVersion : "") + " to version " + currentVersion);
// copy old settings to new version file
ParameterService.copyMainUserConfigFile(lastVersion, currentVersion);
// create workspace selection dialog
File oldWorkspace = ParameterService.getUserWorkspace();
String lookAndFeelString = System.getProperty(PROPERTY_RAPIDMINER_GUI_LOOK_AND_FEEL);
int lookAndFeel = LOOK_AND_FEEL_MODERN;
if (lookAndFeelString != null) {
try {
lookAndFeel = Integer.parseInt(lookAndFeelString);
} catch (NumberFormatException e) {
LogService.getGlobal().log("Cannot setup look and feel ('" + lookAndFeelString + "'), using default.", LogService.INIT);
}
}
InitialSettingsDialog dialog = new InitialSettingsDialog(getSplashScreenFrame(), oldWorkspace, "rm_workspace", null, ParameterService.getRapidMinerHome(), lookAndFeel, true);
dialog.setVisible(true);
if (!dialog.isOk()) {
SwingTools.showVerySimpleErrorMessage("RapidMiner needs a workspace directory in order to properly work.\nUsing default, you can change the workspace in the file menu.");
}
String newPath = dialog.getWorkspacePath();
File newWorkspace = new File(newPath);
ParameterService.setUserWorkspace(newWorkspace);
int selectedLookAndFeel = dialog.getSelectedLookAndFeel();
ParameterService.writePropertyIntoMainUserConfigFile(PROPERTY_RAPIDMINER_GUI_LOOK_AND_FEEL, selectedLookAndFeel + "");
}
private void writeLastVersion(File versionFile) {
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(versionFile));
out.println(getLongVersion());
} catch (IOException e) {
LogService.getGlobal().logWarning("Cannot write current version into property file.");
} finally {
if (out != null)
out.close();
}
}
public static void useProcessFile(Process process) {
File file = process.getProcessFile();
file = new File(file.getAbsolutePath());
if (recentFiles.contains(file)) {
recentFiles.remove(file);
}
recentFiles.addFirst(file);
while (recentFiles.size() > NUMBER_OF_RECENT_FILES)
recentFiles.removeLast();
}
public static ResultHistory getResultHistory() {
return resultHistory;
}
public static List<File> getRecentFiles() {
return recentFiles;
}
private static void loadRecentFileList() {
File file = ParameterService.getUserConfigFile("history");
if (!file.exists())
return;
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
recentFiles.clear();
String line = null;
while ((line = in.readLine()) != null) {
recentFiles.add(new File(line));
}
} catch (IOException e) {
// cannot happen
SwingTools.showSimpleErrorMessage("Cannot read history file", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
SwingTools.showSimpleErrorMessage("Cannot close connection to history file.", e);
}
}
}
}
private static void saveRecentFileList() {
File file = ParameterService.getUserConfigFile("history");
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(file));
Iterator i = recentFiles.iterator();
while (i.hasNext()) {
out.println(((File) i.next()).getAbsolutePath());
}
} catch (IOException e) {
SwingTools.showSimpleErrorMessage("Cannot write history file", e);
} finally {
if (out != null) {
out.close();
}
}
}
public static void saveLastUpdateCheckDate() {
File file = ParameterService.getUserConfigFile("updatecheck.date");
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(file));
Calendar currentDate = Calendar.getInstance();
out.println(currentDate.get(Calendar.YEAR));
out.println(currentDate.get(Calendar.MONTH));
out.println(currentDate.get(Calendar.DAY_OF_MONTH));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
private static Calendar loadLastUpdateCheckDate() {
File file = ParameterService.getUserConfigFile("updatecheck.date");
if (!file.exists())
return null;
Calendar lastCheck = null;
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
String yearLine = in.readLine();
String monthLine = in.readLine();
String dayLine = in.readLine();
int year = 2001;
if (yearLine != null)
year = Integer.parseInt(yearLine.trim());
int month = 1;
if (monthLine != null)
month = Integer.parseInt(monthLine.trim());
int day = 1;
if (dayLine != null)
day = Integer.parseInt(dayLine.trim());
lastCheck = Calendar.getInstance();
lastCheck.set(Calendar.YEAR, year);
lastCheck.set(Calendar.MONTH, month);
lastCheck.set(Calendar.DAY_OF_MONTH, day);
} catch (IOException e) {
LogService.getGlobal().logWarning("Cannot read last date of update check.");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// cannot happen
}
}
}
return lastCheck;
}
public static void checkForUpdates(boolean showFailureDialog) {
updateManager.checkForUpdates(getMainFrame(), showFailureDialog);
}
private static void saveGUIProperties() {
Properties properties = new Properties();
MainFrame mainFrame = getMainFrame();
if (mainFrame != null) {
properties.setProperty(PROPERTY_GEOMETRY_X, "" + (int) mainFrame.getLocation().getX());
properties.setProperty(PROPERTY_GEOMETRY_Y, "" + (int) mainFrame.getLocation().getY());
properties.setProperty(PROPERTY_GEOMETRY_WIDTH, "" + mainFrame.getWidth());
properties.setProperty(PROPERTY_GEOMETRY_HEIGHT, "" + mainFrame.getHeight());
properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_MAIN, "" + mainFrame.getMainDividerLocation());
properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_EDITOR, "" + mainFrame.getEditorDividerLocation());
properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_LOGGING, "" + mainFrame.getLoggingDividerLocation());
properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_GROUPSELECTION, "" + mainFrame.getGroupSelectionDividerLocation());
properties.setProperty(PROPERTY_EXPERT_MODE, "" + mainFrame.getPropertyTable().isExpertMode());
File file = ParameterService.getUserConfigFile("gui.properties");
OutputStream out = null;
try {
out = new FileOutputStream(file);
properties.store(out, "RapidMiner GUI properties");
} catch (IOException e) {
LogService.getGlobal().logWarning("Cannot write GUI properties: " + e.getMessage());
} finally {
try {
if (out != null)
out.close();
} catch (IOException e) {
throw new Error(e); // should not occur
}
}
}
}
private static void loadGUIProperties(MainFrame mainFrame) {
Properties properties = new Properties();
File file = ParameterService.getUserConfigFile("gui.properties");
if (file.exists()) {
InputStream in = null;
try {
in = new FileInputStream(file);
properties.load(in);
} catch (IOException e) {
setDefaultGUIProperties();
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
throw new Error(e); // should not occur
}
}
try {
mainFrame.setLocation(Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_X)), Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_Y)));
mainFrame.setSize(new Dimension(Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_WIDTH)), Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_HEIGHT))));
mainFrame.setDividerLocations(
Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_DIVIDER_MAIN)),
Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_DIVIDER_EDITOR)),
Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_DIVIDER_LOGGING)),
Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_DIVIDER_GROUPSELECTION)));
mainFrame.getPropertyTable().setExpertMode(Boolean.valueOf(properties.getProperty(PROPERTY_EXPERT_MODE)).booleanValue());
mainFrame.updateToggleExpertModeIcon();
} catch (NumberFormatException e) {
setDefaultGUIProperties();
}
} else {
setDefaultGUIProperties();
}
}
/** This method sets some default GUI properties. This method can be invoked if the properties
* file was not found or produced any error messages (which might happen after version changes).
*/
private static void setDefaultGUIProperties() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
mainFrame.setLocation((int)(0.05d * screenSize.getWidth()), (int)(0.05d * screenSize.getHeight()));
mainFrame.setSize((int)(0.9d * screenSize.getWidth()), (int)(0.9d * screenSize.getHeight()));
mainFrame.setDividerLocations((int)(0.6d * screenSize.getHeight()), (int)(0.2d * screenSize.getWidth()), (int)(0.75d * screenSize.getWidth()), (int)(0.4d * screenSize.getWidth()));
mainFrame.getPropertyTable().setExpertMode(false);
mainFrame.updateToggleExpertModeIcon();
}
public static void main(String[] args) throws Exception {
Runtime.getRuntime().addShutdownHook(new ShutdownHook());
File file = null;
if (args.length > 0) {
if (args.length != 1) {
System.out.println("java " + RapidMinerGUI.class.getName() + " [processfile]");
return;
}
file = new File(args[0]);
if (!file.exists()) {
System.err.println("File '" + args[0] + "' not found.");
return;
}
if (!file.canRead()) {
System.err.println("Cannot read file '" + args[0] + "'.");
return;
}
}
RapidMiner.setInputHandler(new GUIInputHandler());
new RapidMinerGUI().run(file);
}
public static void registerUpdateManager(UpdateManager manager) {
RapidMinerGUI.updateManager = manager;
}
}