http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context.xml ---------------------------------------------------------------------- diff --git a/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context.xml b/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context.xml new file mode 100644 index 0000000..c3adf1f --- /dev/null +++ b/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="UpdateMenuAction" + class="net.sf.taverna.t2.workbench.update.impl.menu.UpdateMenuAction"> + <property name="updateManager" ref="updateManager" /> + </bean> + +</beans>
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/pom.xml ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/pom.xml b/taverna-workbench-workbench-impl/pom.xml new file mode 100644 index 0000000..5b52d3a --- /dev/null +++ b/taverna-workbench-workbench-impl/pom.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>workbench-impl</artifactId> + <packaging>bundle</packaging> + <name>Workbench UI implementation</name> + <description>The main workbench ui</description> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Embed-Dependency>osxapplication</Embed-Dependency> + <Import-Package>com.apple.eawt;resolution:=optional,*</Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>configuration-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.commons</groupId> + <artifactId>taverna-plugin-api</artifactId> + <version>${taverna.commons.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + <version>${scufl2.version}</version> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.simplericity.macify</groupId> + <artifactId>macify</artifactId> + <version>1.6</version> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/DataflowEditsListener.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/DataflowEditsListener.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/DataflowEditsListener.java new file mode 100644 index 0000000..4c493d1 --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/DataflowEditsListener.java @@ -0,0 +1,93 @@ +package net.sf.taverna.t2.workbench.ui.impl; + +import static uk.org.taverna.scufl2.api.container.WorkflowBundle.generateIdentifier; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowRedoEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowUndoEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.DataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workflow.edits.UpdateDataflowInternalIdentifierEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Listens out for any edits on a dataflow and changes its internal id (or back + * to the old one in the case of redo/undo). Is first created when the workbench + * is initialised. + * + * @author Ian Dunlop + */ +public class DataflowEditsListener implements Observer<EditManagerEvent> { + private static Logger logger = Logger + .getLogger(DataflowEditsListener.class); + + private Map<Edit<?>, URI> dataflowEditMap; + + public DataflowEditsListener() { + super(); + dataflowEditMap = new HashMap<>(); + } + + /** + * Receives {@link EditManagerEvent}s from the {@link EditManager} and + * changes the id of the {@link Dataflow} to a new one or back to its old + * one depending on whether it is a do/undo/redo event. Stores the actual + * edit and the pre-edit dataflow id in a Map and changes the id when it + * gets further actions against this same edit + */ + @Override + public void notify(Observable<EditManagerEvent> observable, + EditManagerEvent event) throws Exception { + Edit<?> edit = event.getEdit(); + WorkflowBundle dataFlow = ((AbstractDataflowEditEvent) event) + .getDataFlow(); + + if (event instanceof DataflowEditEvent) { + /* + * the dataflow has been edited in some way so change its internal + * id and store the old one against the edit that is changing + * 'something' + */ + URI internalIdentifier = dataFlow.getGlobalBaseURI(); + dataflowEditMap.put(edit, internalIdentifier); + URI newIdentifier = generateIdentifier(); + new UpdateDataflowInternalIdentifierEdit(dataFlow, newIdentifier) + .doEdit(); + logger.debug("Workflow edit, id changed from: " + + internalIdentifier + " to " + newIdentifier); + } else if (event instanceof DataFlowRedoEvent) { + /* + * change the id back to the old one and store the new one in case + * we want to change it back + */ + URI newId = dataFlow.getGlobalBaseURI(); + URI oldId = dataflowEditMap.get(edit); + dataflowEditMap.put(edit, newId); + new UpdateDataflowInternalIdentifierEdit(dataFlow, oldId).doEdit(); + logger.debug("Workflow edit, id changed from: " + newId + " to " + + oldId); + } else if (event instanceof DataFlowUndoEvent) { + /* + * change the id back to the old one and store the new one in case + * we want to change it back + */ + URI newId = dataFlow.getGlobalBaseURI(); + URI oldId = dataflowEditMap.get(edit); + dataflowEditMap.put(edit, newId); + new UpdateDataflowInternalIdentifierEdit(dataFlow, oldId).doEdit(); + logger.debug("Workflow edit, id changed from: " + newId + " to " + + oldId); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/LoggerStream.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/LoggerStream.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/LoggerStream.java new file mode 100644 index 0000000..fe13d4d --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/LoggerStream.java @@ -0,0 +1,136 @@ +/*************************************** + * * + * JBoss: The OpenSource J2EE WebOS * + * * + * Distributable under LGPL license. * + * See terms of license at gnu.org. * + * * + * Modified by Stian Soiland-Reyes * + * * + ***************************************/ +package net.sf.taverna.t2.workbench.ui.impl; + +import java.io.IOException; +import java.io.PrintStream; + +import org.apache.log4j.Logger; +import org.apache.log4j.Level; + +/** + * A subclass of PrintStream that redirects its output to a log4j Logger. + * + * <p>This class is used to map PrintStream/PrintWriter oriented logging onto + * the log4j Categories. Examples include capturing System.out/System.err + * + * @version <tt>$Revision: 1.1.1.1 $</tt> + * @author <a href="mailto:scott.st...@jboss.org">Scott Stark</a>. + * @author <a href="mailto:ja...@planet57.com">Jason Dillon</a> + */ +//FIXME Replace this class entirely +class LoggerStream extends PrintStream { + /** + * Default flag to enable/disable tracing println calls. from the system + * property <tt>net.sf.taverna.t2.workbench.ui.impl.LoggerStream.trace</tt> + * or if not set defaults to <tt>false</tt>. + */ + public static final boolean TRACE = + getBoolean(LoggerStream.class.getName() + ".trace", false); + + /** + * Helper to get boolean value from system property or use default if not + * set. + */ + private static boolean getBoolean(String name, boolean defaultValue) { + String value = System.getProperty(name, null); + if (value == null) + return defaultValue; + return new Boolean(value).booleanValue(); + } + + private Logger logger; + private Level level; + private boolean issuedWarning; + + /** + * Redirect logging to the indicated logger using Level.INFO + */ + public LoggerStream(Logger logger) { + this(logger, Level.INFO, System.out); + } + + /** + * Redirect logging to the indicated logger using the given level. The ps is + * simply passed to super but is not used. + */ + public LoggerStream(Logger logger, Level level, PrintStream ps) { + super(ps); + this.logger = logger; + this.level = level; + } + + @Override + public void println(String msg) { + if (msg == null) + msg = "null"; + byte[] bytes = msg.getBytes(); + write(bytes, 0, bytes.length); + } + + @Override + public void println(Object msg) { + if (msg == null) + msg = "null"; + byte[] bytes = msg.toString().getBytes(); + write(bytes, 0, bytes.length); + } + + public void write(byte b) { + byte[] bytes = { b }; + write(bytes, 0, 1); + } + + private ThreadLocal<Boolean> recursiveCheck = new ThreadLocal<>(); + + @Override + public void write(byte[] b, int off, int len) { + Boolean recursed = recursiveCheck.get(); + if (recursed != null && recursed) { + /* + * There is a configuration error that is causing looping. Most + * likely there are two console appenders so just return to prevent + * spinning. + */ + if (issuedWarning == false) { + String msg = "ERROR: invalid log settings detected, console capturing is looping"; + // out.write(msg.getBytes()); + new Exception(msg).printStackTrace((PrintStream) out); + issuedWarning = true; + } + try { + out.write(b, off, len); + } catch (IOException e) { + } + return; + } + + // Remove the end of line chars + while (len > 0 && (b[len - 1] == '\n' || b[len - 1] == '\r') + && len > off) + len--; + + /* + * HACK, something is logging exceptions line by line (including + * blanks), but I can't seem to find it, so for now just ignore empty + * lines... they aren't very useful. + */ + if (len != 0) { + String msg = new String(b, off, len); + recursiveCheck.set(true); + if (TRACE) + logger.log(level, msg, new Throwable()); + else + logger.log(level, msg); + recursiveCheck.set(false); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/SetConsoleLoggerStartup.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/SetConsoleLoggerStartup.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/SetConsoleLoggerStartup.java new file mode 100644 index 0000000..a8bdfdd --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/SetConsoleLoggerStartup.java @@ -0,0 +1,62 @@ +package net.sf.taverna.t2.workbench.ui.impl; + +import static org.apache.log4j.Level.ERROR; +import static org.apache.log4j.Level.WARN; + +import java.io.PrintStream; + +import net.sf.taverna.t2.workbench.StartupSPI; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; + +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.Logger; + +public class SetConsoleLoggerStartup implements StartupSPI { + private static final PrintStream originalErr = System.err; + private static final PrintStream originalOut = System.out; + + private final WorkbenchConfiguration workbenchConfiguration; + + public SetConsoleLoggerStartup(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + @Override + public int positionHint() { + /* + * Must be <b>after</b> PrepareLoggerStarup in file-translator -- + * otherwise Taverna 1 libraries will cause double logging + */ + return 10; + } + + @Override + public boolean startup() { + setSystemOutCapture(); + return true; + } + + public void setSystemOutCapture() { + if (!workbenchConfiguration.getCaptureConsole()) { + System.setOut(originalOut); + System.setErr(originalErr); + return; + } + Logger systemOutLogger = Logger.getLogger("System.out"); + Logger systemErrLogger = Logger.getLogger("System.err"); + + try { + /* + * This logger stream not loop with log4j > 1.2.13, which has + * getFollow method + */ + ConsoleAppender.class.getMethod("getFollow"); + System.setOut(new LoggerStream(systemOutLogger, WARN, originalOut)); + } catch (SecurityException e) { + } catch (NoSuchMethodException e) { + System.err.println("Not capturing System.out, use log4j >= 1.2.13"); + } + + System.setErr(new LoggerStream(systemErrLogger, ERROR, originalErr)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/StoreWindowStateOnShutdown.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/StoreWindowStateOnShutdown.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/StoreWindowStateOnShutdown.java new file mode 100644 index 0000000..2537f0b --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/StoreWindowStateOnShutdown.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (C) 2008-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl; + +import org.apache.log4j.Logger; + +import net.sf.taverna.t2.workbench.ShutdownSPI; +import net.sf.taverna.t2.workbench.ui.Workbench; + +/** + * Store Workbench window size and perspectives, so that settings can be used on + * next startup. + * + * @author Stian Soiland-Reyes + */ +public class StoreWindowStateOnShutdown implements ShutdownSPI { + private static Logger logger = Logger + .getLogger(StoreWindowStateOnShutdown.class); + + private Workbench workbench; + + @Override + public int positionHint() { + return 1000; + } + + @Override + public boolean shutdown() { + try { + workbench.storeSizeAndLocationPrefs(); + } catch (Exception ex) { + logger.error("Error saving the Workbench size and position", ex); + } + return true; + } + + public void setWorkbench(Workbench workbench) { + this.workbench = workbench; + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationData.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationData.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationData.java new file mode 100644 index 0000000..3e32f7b --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationData.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl; + +public class UserRegistrationData { + private String tavernaVersion = ""; + private String firstName = ""; + private String lastName = ""; + private String emailAddress = ""; + private String institutionOrCompanyName = ""; + private String industry = ""; + private String field = ""; + private String purposeOfUsingTaverna = ""; + private boolean keepMeInformed = false; + + public void setTavernaVersion(String tavernaVersion) { + this.tavernaVersion = tavernaVersion; + } + + public String getTavernaVersion() { + return tavernaVersion; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getFirstName() { + return firstName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getLastName() { + return lastName; + } + + public void setEmailAddress(String emailAddress) { + this.emailAddress = emailAddress; + } + + public String getEmailAddress() { + return emailAddress; + } + + public void setInstitutionOrCompanyName(String institutionOrCompanyName) { + this.institutionOrCompanyName = institutionOrCompanyName; + } + + public String getInstitutionOrCompanyName() { + return institutionOrCompanyName; + } + + public void setIndustry(String industry) { + this.industry = industry; + } + + public String getIndustry() { + return industry; + } + + public void setField(String field) { + this.field = field; + } + + public String getField() { + return field; + } + + public void setPurposeOfUsingTaverna(String purposeOfUsingTaverna) { + this.purposeOfUsingTaverna = purposeOfUsingTaverna; + } + + public String getPurposeOfUsingTaverna() { + return purposeOfUsingTaverna; + } + + public void setKeepMeInformed(boolean keepMeInformed) { + this.keepMeInformed = keepMeInformed; + } + + public boolean getKeepMeInformed() { + return keepMeInformed; + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationForm.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationForm.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationForm.java new file mode 100644 index 0000000..97f831f --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationForm.java @@ -0,0 +1,995 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.Color.LIGHT_GRAY; +import static java.awt.Color.WHITE; +import static java.awt.FlowLayout.LEFT; +import static java.awt.Font.BOLD; +import static java.awt.GridBagConstraints.FIRST_LINE_START; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.LINE_START; +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.WEST; +import static java.awt.event.KeyEvent.VK_ENTER; +import static java.awt.event.KeyEvent.VK_TAB; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.SwingConstants.BOTTOM; +import static javax.swing.SwingConstants.TOP; +import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.tavernaCogs32x32Icon; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.ConnectException; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Properties; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.border.Border; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.Document; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.StyleSheet; + +import net.sf.taverna.t2.lang.ui.DialogTextArea; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; +/** + * User registration form. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class UserRegistrationForm extends HelpEnabledDialog { + private static final String FAILED = "User registration failed: "; + + private static final String REGISTRATION_FAILED_MSG = "User registration failed. Please try again later."; + + private static final String REGISTRATION_URL = "http://www.mygrid.org.uk/taverna/registration/"; + + public static final String TAVERNA_VERSION_PROPERTY_NAME = "Taverna version"; + public static final String FIRST_NAME_PROPERTY_NAME = "First name"; + public static final String LAST_NAME_PROPERTY_NAME = "Last name"; + public static final String EMAIL_ADDRESS_PROPERTY_NAME = "Email address"; + public static final String INSTITUTION_OR_COMPANY_PROPERTY_NAME = "Institution or company name"; + public static final String INDUSTRY_PROPERTY_NAME = "Industry"; + public static final String FIELD_PROPERTY_NAME = "Field of investigation"; + public static final String PURPOSE_PROPERTY_NAME = "Purpose of using Taverna"; + public static final String KEEP_ME_INFORMED_PROPERTY_NAME = "Keep me informed by email"; + + public static final String TAVERNA_REGISTRATION_POST_PARAMETER_NAME = "taverna_registration"; + public static final String TAVERNA_VERSION_POST_PARAMETER_NAME = "taverna_version"; + public static final String FIRST_NAME_POST_PARAMETER_NAME = "first_name"; + public static final String LAST_NAME_POST_PARAMETER_NAME = "last_name"; + public static final String EMAIL_ADDRESS_POST_PARAMETER_NAME = "email"; + public static final String INSTITUTION_OR_COMPANY_POST_PARAMETER_NAME = "institution_or_company"; + public static final String INDUSTRY_TYPE_POST_PARAMETER_NAME = "industry_type"; + public static final String FIELD_POST_PARAMETER_NAME = "field"; + public static final String PURPOSE_POST_PARAMETER_NAME = "purpose"; + public static final String KEEP_ME_INFORMED_POST_PARAMETER_PROPERTY_NAME = "keep_me_informed"; + + private static String TRUE = Boolean.TRUE.toString(); + private static String FALSE = Boolean.FALSE.toString(); + + private static final String WELCOME = "Welcome to the Taverna User Registration Form"; + private static final String PLEASE_FILL_IN_THIS_REGISTRATION_FORM = "Please fill in this registration form to let us know that you are using Taverna"; + + private static final String WE_DO = "Note that by registering:\n" + + " \u25CF We do not have access to your data\n" + + " \u25CF We do not have access to your service usage\n" + + " \u25CF You will not be monitored\n" + + " \u25CF We do record the information you provide\n" + + " at registration time"; + + private static final String WHY_REGISTER = "By registering you will:\n" + + " \u25CF Allow us to support you better; future plans will be\n" + + " directed towards solutions Taverna users require\n" + + " \u25CF Help sustain Taverna development; our continued\n" + + " funding relies on us showing usage\n" + + " \u25CF (Optionally) Hear about news and product updates"; + + private static final String FIRST_NAME = "*First name:"; + private static final String LAST_NAME = "*Last name:"; + private static final String EMAIL_ADDRESS = "*Email address:"; + private static final String KEEP_ME_INFORMED = "Keep me informed of news and product updates via email"; + private static final String INSTITUTION_COMPANY_NAME = "*Institution/Company name:"; + private static final String FIELD_OF_INVESTIGATION = " Field of investigation:\n" + + " (e.g. bioinformatics)"; + private static final String WHY_YOU_INTEND_TO_USE_TAVERNA = " A brief description of how you intend\n" + + " to use Taverna: (e.g. genome analysis\n" + + " for bacterial strain identification)"; + + private static String[] industryTypes = { "", "Academia - Life Sciences", + "Academia - Social Sciences", "Academia - Physical Sciences", + "Academia - Environmental Sciences", "Academia - Other", + "Industry - Biotechnology", "Industry - Pharmaceutical", + "Industry - Engineering", "Industry - Other", + "Healthcare Services", "Goverment and Public Sector", "Other" }; + + private static final String I_AGREE_TO_THE_TERMS_AND_CONDITIONS = "I agree to the terms and conditions of registration at"; + private static final String TERMS_AND_CONDITIONS_URL = "http://www.taverna.org.uk/legal/terms"; + + private Logger logger = Logger.getLogger(UserRegistrationForm.class); + private UserRegistrationData previousRegistrationData; + private JTextField firstNameTextField; + private JTextField lastNameTextField; + private JTextField emailTextField; + private JCheckBox keepMeInformedCheckBox; + private JTextField institutionOrCompanyTextField; + private JComboBox<String> industryTypeTextField; + private JTextField fieldTextField; + private JTextArea purposeTextArea; + private JCheckBox termsAndConditionsCheckBox; + + private final File registrationDataFile; + private final File remindMeLaterFile; + private final File doNotRegisterMeFile; + private final String appName; + + public UserRegistrationForm(String appName, File registrationDataFile, + File doNotRegisterMeFile, File remindMeLaterFile) { + super((Frame) null, "Taverna User Registration", true); + this.appName = appName; + this.registrationDataFile = registrationDataFile; + this.doNotRegisterMeFile = doNotRegisterMeFile; + this.remindMeLaterFile = remindMeLaterFile; + initComponents(); + } + + public UserRegistrationForm(String appName, + File previousRegistrationDataFile, File registrationDataFile, + File doNotRegisterMeFile, File remindMeLaterFile) { + super((Frame) null, "Taverna User Registration", true); + this.appName = appName; + this.registrationDataFile = registrationDataFile; + this.doNotRegisterMeFile = doNotRegisterMeFile; + this.remindMeLaterFile = remindMeLaterFile; + previousRegistrationData = loadUserRegistrationData(previousRegistrationDataFile); + initComponents(); + } + + // For testing only + // public static void main(String[] args) throws ClassNotFoundException, + // InstantiationException, IllegalAccessException, + // UnsupportedLookAndFeelException{ + // WorkbenchImpl.setLookAndFeel(); + // UserRegistrationForm form = new UserRegistrationForm(); + // form.setVisible(true); + // } + + private void initComponents() { + JPanel mainPanel = new JPanel(new GridBagLayout()); + + // Base font for all components on the form + Font baseFont = new JLabel("base font").getFont().deriveFont(11f); + + // Title panel + JPanel titlePanel = new JPanel(new FlowLayout(LEFT)); + titlePanel.setBackground(WHITE); + // titlePanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + JLabel titleLabel = new JLabel(WELCOME); + titleLabel.setFont(baseFont.deriveFont(BOLD, 13.5f)); + // titleLabel.setBorder(new EmptyBorder(10, 10, 0, 10)); + JLabel titleIcon = new JLabel(tavernaCogs32x32Icon); + // titleIcon.setBorder(new EmptyBorder(10, 10, 10, 10)); + DialogTextArea titleMessage = new DialogTextArea( + PLEASE_FILL_IN_THIS_REGISTRATION_FORM); + titleMessage.setMargin(new Insets(0, 20, 0, 10)); + titleMessage.setFont(baseFont); + titleMessage.setEditable(false); + titleMessage.setFocusable(false); + // titlePanel.setBorder( new EmptyBorder(10, 10, 0, 10)); + JPanel messagePanel = new JPanel(new BorderLayout()); + messagePanel.add(titleLabel, NORTH); + messagePanel.add(titleMessage, CENTER); + messagePanel.setBackground(WHITE); + titlePanel.add(titleIcon); + titlePanel.add(messagePanel); + addDivider(titlePanel, BOTTOM, true); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.gridwidth = 2; + // gbc.insets = new Insets(5, 10, 0, 0); + mainPanel.add(titlePanel, gbc); + + // Registration messages + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 1; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 2; + // gbc.insets = new Insets(5, 0, 0, 10); + DialogTextArea registrationMessage1 = new DialogTextArea(WHY_REGISTER); + registrationMessage1.setMargin(new Insets(0, 10, 0, 0)); + registrationMessage1.setFont(baseFont); + registrationMessage1.setEditable(false); + registrationMessage1.setFocusable(false); + registrationMessage1.setBackground(getBackground()); + + DialogTextArea registrationMessage2 = new DialogTextArea(WE_DO); + registrationMessage2.setMargin(new Insets(0, 10, 0, 10)); + registrationMessage2.setFont(baseFont); + registrationMessage2.setEditable(false); + registrationMessage2.setFocusable(false); + registrationMessage2.setBackground(getBackground()); + JPanel registrationMessagePanel = new JPanel(new FlowLayout( + FlowLayout.CENTER)); + registrationMessagePanel.add(registrationMessage1); + registrationMessagePanel.add(registrationMessage2); + addDivider(registrationMessagePanel, BOTTOM, true); + mainPanel.add(registrationMessagePanel, gbc); + + // Mandatory label + // JLabel mandatoryLabel = new JLabel("* Mandatory fields"); + // mandatoryLabel.setFont(baseFont); + // gbc.weightx = 0.0; + // gbc.weighty = 0.0; + // gbc.gridx = 0; + // gbc.gridy = 3; + // gbc.fill = NONE; + // gbc.anchor = GridBagConstraints.EAST; + // gbc.gridwidth = 2; + // gbc.insets = new Insets(0, 10, 0, 20); + // mainPanel.add(mandatoryLabel, gbc); + + // First name + JLabel firstNameLabel = new JLabel(FIRST_NAME); + firstNameLabel.setFont(baseFont); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 4; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(0, 10, 0, 10); + mainPanel.add(firstNameLabel, gbc); + + firstNameTextField = new JTextField(); + firstNameTextField.setFont(baseFont); + if (previousRegistrationData != null) + firstNameTextField.setText(previousRegistrationData.getFirstName()); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 4; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(firstNameTextField, gbc); + + // Last name + JLabel lastNameLabel = new JLabel(LAST_NAME); + lastNameLabel.setFont(baseFont); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 5; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(0, 10, 0, 10); + mainPanel.add(lastNameLabel, gbc); + + lastNameTextField = new JTextField(); + lastNameTextField.setFont(baseFont); + if (previousRegistrationData != null) + lastNameTextField.setText(previousRegistrationData.getLastName()); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 5; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(lastNameTextField, gbc); + + // Email address + JLabel emailLabel = new JLabel(EMAIL_ADDRESS); + emailLabel.setFont(baseFont); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 6; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(emailLabel, gbc); + + emailTextField = new JTextField(); + emailTextField.setFont(baseFont); + if (previousRegistrationData != null) + emailTextField.setText(previousRegistrationData.getEmailAddress()); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 6; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(emailTextField, gbc); + + // Keep me informed + keepMeInformedCheckBox = new JCheckBox(KEEP_ME_INFORMED); + keepMeInformedCheckBox.setFont(baseFont); + if (previousRegistrationData != null) + keepMeInformedCheckBox.setSelected(previousRegistrationData + .getKeepMeInformed()); + keepMeInformedCheckBox.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == VK_ENTER) { + evt.consume(); + keepMeInformedCheckBox.setSelected(!keepMeInformedCheckBox + .isSelected()); + } + } + }); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 7; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 2; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(keepMeInformedCheckBox, gbc); + + // Institution name + JLabel institutionLabel = new JLabel(INSTITUTION_COMPANY_NAME); + institutionLabel.setFont(baseFont); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 8; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(institutionLabel, gbc); + + institutionOrCompanyTextField = new JTextField(); + institutionOrCompanyTextField.setFont(baseFont); + if (previousRegistrationData != null) + institutionOrCompanyTextField.setText(previousRegistrationData + .getInstitutionOrCompanyName()); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 8; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(institutionOrCompanyTextField, gbc); + + // Industry type + JLabel industryLabel = new JLabel(" Industry type:"); + industryLabel.setFont(baseFont); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 9; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(industryLabel, gbc); + + industryTypeTextField = new JComboBox<>(industryTypes); + industryTypeTextField.setFont(baseFont); + if (previousRegistrationData != null) + industryTypeTextField.setSelectedItem(previousRegistrationData + .getIndustry()); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 9; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(industryTypeTextField, gbc); + + // Field of investigation + JTextArea fieldLabel = new JTextArea(FIELD_OF_INVESTIGATION); + fieldLabel.setFont(baseFont); + fieldLabel.setEditable(false); + fieldLabel.setFocusable(false); + fieldLabel.setBackground(getBackground()); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 10; + gbc.fill = NONE; + gbc.anchor = LINE_START; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(fieldLabel, gbc); + + fieldTextField = new JTextField(); + fieldTextField.setFont(baseFont); + if (previousRegistrationData != null) + fieldTextField.setText(previousRegistrationData.getField()); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 10; + gbc.fill = HORIZONTAL; + gbc.anchor = FIRST_LINE_START; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(fieldTextField, gbc); + + // Purpose of using Taverna + JTextArea purposeLabel = new JTextArea(WHY_YOU_INTEND_TO_USE_TAVERNA); + purposeLabel.setFont(baseFont); + purposeLabel.setEditable(false); + purposeLabel.setFocusable(false); + purposeLabel.setBackground(getBackground()); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 11; + gbc.fill = NONE; + gbc.anchor = LINE_START; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(purposeLabel, gbc); + + purposeTextArea = new JTextArea(4, 30); + purposeTextArea.setFont(baseFont); + purposeTextArea.setLineWrap(true); + purposeTextArea.setAutoscrolls(true); + if (previousRegistrationData != null) + purposeTextArea.setText(previousRegistrationData + .getPurposeOfUsingTaverna()); + purposeTextArea.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == VK_TAB) { + if (evt.getModifiers() > 0) + purposeTextArea.transferFocusBackward(); + else + purposeTextArea.transferFocus(); + evt.consume(); + } + } + }); + JScrollPane purposeScrollPane = new JScrollPane(purposeTextArea); + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + gbc.gridy = 11; + gbc.fill = HORIZONTAL; + gbc.anchor = FIRST_LINE_START; + gbc.gridwidth = 1; + gbc.insets = new Insets(5, 10, 0, 10); + mainPanel.add(purposeScrollPane, gbc); + + // Terms and conditions + termsAndConditionsCheckBox = new JCheckBox( + I_AGREE_TO_THE_TERMS_AND_CONDITIONS); + termsAndConditionsCheckBox.setFont(baseFont); + termsAndConditionsCheckBox.setBorder(null); + termsAndConditionsCheckBox.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == VK_ENTER) { + evt.consume(); + termsAndConditionsCheckBox + .setSelected(!termsAndConditionsCheckBox + .isSelected()); + } + } + }); + // gbc.weightx = 0.0; + // gbc.weighty = 0.0; + // gbc.gridx = 0; + // gbc.gridy = 12; + // gbc.fill = NONE; + // gbc.anchor = WEST; + // gbc.gridwidth = 2; + // gbc.insets = new Insets(10, 10, 0, 0); + // mainPanel.add(termsAndConditionsCheckBox, gbc); + + // Terms and conditions link + JEditorPane termsAndConditionsURL = new JEditorPane(); + termsAndConditionsURL.setEditable(false); + termsAndConditionsURL.setBackground(getBackground()); + termsAndConditionsURL.setFocusable(false); + HTMLEditorKit kit = new HTMLEditorKit(); + termsAndConditionsURL.setEditorKit(kit); + StyleSheet styleSheet = kit.getStyleSheet(); + // styleSheet.addRule("body {font-family:"+baseFont.getFamily()+"; font-size:"+baseFont.getSize()+";}"); + // // base font looks bigger when rendered as HTML + styleSheet.addRule("body {font-family:" + baseFont.getFamily() + + "; font-size:9px;}"); + Document doc = kit.createDefaultDocument(); + termsAndConditionsURL.setDocument(doc); + termsAndConditionsURL.setText("<html><body><a href=\"" + + TERMS_AND_CONDITIONS_URL + "\">" + TERMS_AND_CONDITIONS_URL + + "</a></body></html>"); + termsAndConditionsURL.addHyperlinkListener(new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent he) { + if (he.getEventType() == ACTIVATED) + followHyperlinkToTandCs(); + } + }); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridx = 0; + gbc.gridy = 13; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.gridwidth = 2; + gbc.insets = new Insets(5, 10, 0, 10); + JPanel termsAndConditionsPanel = new JPanel(new FlowLayout(LEFT)); + termsAndConditionsPanel.add(termsAndConditionsCheckBox); + termsAndConditionsPanel.add(termsAndConditionsURL); + mainPanel.add(termsAndConditionsPanel, gbc); + + // Button panel + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + JButton registerButton = new JButton("Register"); + registerButton.setFont(baseFont); + registerButton.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == VK_ENTER) { + evt.consume(); + register(); + } + } + }); + registerButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + register(); + } + }); + JButton doNotRegisterButton = new JButton("Do not ask me again"); + doNotRegisterButton.setFont(baseFont); + doNotRegisterButton.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == VK_ENTER) { + evt.consume(); + doNotRegister(); + } + } + }); + doNotRegisterButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doNotRegister(); + } + }); + JButton remindMeLaterButton = new JButton("Remind me later"); // in 2 weeks + remindMeLaterButton.setFont(baseFont); + remindMeLaterButton.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == VK_ENTER) { + evt.consume(); + remindMeLater(); + } + } + }); + remindMeLaterButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + remindMeLater(); + } + }); + buttonPanel.add(registerButton); + buttonPanel.add(remindMeLaterButton); + buttonPanel.add(doNotRegisterButton); + addDivider(buttonPanel, TOP, true); + gbc.gridx = 0; + gbc.gridy = 14; + gbc.fill = HORIZONTAL; + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(5, 10, 0, 10); + gbc.gridwidth = 2; + mainPanel.add(buttonPanel, gbc); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(mainPanel, CENTER); + + pack(); + setResizable(false); + // Center the dialog on the screen (we do not have the parent) + Dimension dimension = getToolkit().getScreenSize(); + Rectangle abounds = getBounds(); + setLocation((dimension.width - abounds.width) / 2, + (dimension.height - abounds.height) / 2); + setSize(getPreferredSize()); + } + + protected void remindMeLater() { + try { + FileUtils.touch(remindMeLaterFile); + } catch (IOException ioex) { + logger.error( + "Failed to touch the 'Remind me later' file at user registration.", + ioex); + } + closeDialog(); + } + + protected void doNotRegister() { + try { + FileUtils.touch(doNotRegisterMeFile); + if (remindMeLaterFile.exists()) + remindMeLaterFile.delete(); + } catch (IOException ioex) { + logger.error( + "Failed to touch the 'Do not register me' file at user registration.", + ioex); + } + closeDialog(); + } + + private void register() { + if (!validateForm()) + return; + UserRegistrationData regData = new UserRegistrationData(); + regData.setTavernaVersion(appName); + regData.setFirstName(firstNameTextField.getText()); + regData.setLastName(lastNameTextField.getText()); + regData.setEmailAddress(emailTextField.getText()); + regData.setKeepMeInformed(keepMeInformedCheckBox.isSelected()); + regData.setInstitutionOrCompanyName(institutionOrCompanyTextField + .getText()); + regData.setIndustry(industryTypeTextField.getSelectedItem().toString()); + regData.setField(fieldTextField.getText()); + regData.setPurposeOfUsingTaverna(purposeTextArea.getText()); + + if (postUserRegistrationDataToServer(regData)) { + saveUserRegistrationData(regData, registrationDataFile); + if (remindMeLaterFile.exists()) + remindMeLaterFile.delete(); + closeDialog(); + } + } + + private boolean validateForm() { + String errorMessage = ""; + if (firstNameTextField.getText().isEmpty()) + errorMessage += "Please provide your first name.<br>"; + if (lastNameTextField.getText().isEmpty()) + errorMessage += "Please provide your last name.<br>"; + if (emailTextField.getText().isEmpty()) + errorMessage += "Please provide your email address.<br>"; + if (institutionOrCompanyTextField.getText().isEmpty()) + errorMessage += "Please provide your institution or company name."; + if (!errorMessage.isEmpty()) { + showMessageDialog(this, new JLabel("<html><body>" + + errorMessage + "</body></html>"), "Error in form", + ERROR_MESSAGE); + return false; + } + if (!termsAndConditionsCheckBox.isSelected()) { + showMessageDialog(this, new JLabel( + "You must agree to the terms and conditions."), + "Error in form", ERROR_MESSAGE); + return false; + } + return true; + } + + public UserRegistrationData loadUserRegistrationData(File propertiesFile) { + UserRegistrationData regData = new UserRegistrationData(); + Properties props = new Properties(); + + // Try to retrieve data from file + try { + props.load(new FileInputStream(propertiesFile)); + } catch (IOException e) { + logger.error("Failed to load old user registration data from " + + propertiesFile.getAbsolutePath(), e); + return null; + } + regData.setTavernaVersion(props + .getProperty(TAVERNA_VERSION_PROPERTY_NAME)); + regData.setFirstName(props.getProperty(FIRST_NAME_PROPERTY_NAME)); + regData.setLastName(props.getProperty(LAST_NAME_PROPERTY_NAME)); + regData.setEmailAddress(props.getProperty(EMAIL_ADDRESS_PROPERTY_NAME)); + regData.setKeepMeInformed(props.getProperty( + KEEP_ME_INFORMED_PROPERTY_NAME).equals(TRUE)); + regData.setInstitutionOrCompanyName(props + .getProperty(INSTITUTION_OR_COMPANY_PROPERTY_NAME)); + regData.setIndustry(props.getProperty(INDUSTRY_PROPERTY_NAME)); + regData.setField(props.getProperty(FIELD_PROPERTY_NAME)); + regData.setPurposeOfUsingTaverna(props + .getProperty(PURPOSE_PROPERTY_NAME)); + return regData; + } + + private void enc(StringBuilder buffer, String name, Object value) + throws UnsupportedEncodingException { + if (buffer.length() != 0) + buffer.append('&'); + buffer.append(URLEncoder.encode(name, "UTF-8")); + buffer.append('='); + buffer.append(URLEncoder.encode(value.toString(), "UTF-8")); + } + + /** + * Post registration data to our server. + */ + private boolean postUserRegistrationDataToServer( + UserRegistrationData regData) { + StringBuilder parameters = new StringBuilder(); + + /* + * The 'submit' parameter - to let the server-side script know we are + * submitting the user's registration form - all other requests will be + * silently ignored + */ + try { + // value does not matter + enc(parameters, TAVERNA_REGISTRATION_POST_PARAMETER_NAME, "submit"); + + enc(parameters, TAVERNA_VERSION_POST_PARAMETER_NAME, + regData.getTavernaVersion()); + enc(parameters, FIRST_NAME_POST_PARAMETER_NAME, + regData.getFirstName()); + enc(parameters, LAST_NAME_POST_PARAMETER_NAME, + regData.getLastName()); + enc(parameters, EMAIL_ADDRESS_POST_PARAMETER_NAME, + regData.getEmailAddress()); + enc(parameters, KEEP_ME_INFORMED_POST_PARAMETER_PROPERTY_NAME, + regData.getKeepMeInformed()); + enc(parameters, INSTITUTION_OR_COMPANY_POST_PARAMETER_NAME, + regData.getInstitutionOrCompanyName()); + enc(parameters, INDUSTRY_TYPE_POST_PARAMETER_NAME, + regData.getIndustry()); + enc(parameters, FIELD_POST_PARAMETER_NAME, regData.getField()); + enc(parameters, PURPOSE_POST_PARAMETER_NAME, + regData.getPurposeOfUsingTaverna()); + } catch (UnsupportedEncodingException ueex) { + logger.error(FAILED + "Could not url encode post parameters", ueex); + showMessageDialog(null, REGISTRATION_FAILED_MSG, + "Error encoding registration data", ERROR_MESSAGE); + return false; + } + String server = REGISTRATION_URL; + logger.info("Posting user registartion to " + server + + " with parameters: " + parameters); + String response = ""; + String failure; + try { + URL url = new URL(server); + URLConnection conn = url.openConnection(); + /* + * Set timeout to e.g. 7 seconds, otherwise we might hang too long + * if server is not responding and it will block Taverna + */ + conn.setConnectTimeout(7000); + // Set connection parameters + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setUseCaches(false); + // Make server believe we are HTML form data... + conn.setRequestProperty("Content-Type", + "application/x-www-form-urlencoded"); + // Write out the bytes of the content string to the stream. + try (DataOutputStream out = new DataOutputStream( + conn.getOutputStream())) { + out.writeBytes(parameters.toString()); + out.flush(); + } + // Read response from the input stream. + try (BufferedReader in = new BufferedReader(new InputStreamReader( + conn.getInputStream()))) { + String temp; + while ((temp = in.readLine()) != null) + response += temp + "\n"; + // Remove the last \n character + if (!response.isEmpty()) + response = response.substring(0, response.length() - 1); + } + if (response.equals("Registration successful!")) + return true; + logger.error(FAILED + "Response form server was: " + response); + failure = "Error saving registration data on the server"; + } catch (ConnectException ceex) { + /* + * the connection was refused remotely (e.g. no process is listening + * on the remote address/port). + */ + logger.error( + FAILED + + "Registration server is not listening of the specified url.", + ceex); + failure = "Registration server is not listening at the specified url"; + } catch (SocketTimeoutException stex) { + // timeout has occurred on a socket read or accept. + logger.error(FAILED + "Socket timeout occurred.", stex); + failure = "Registration server timeout"; + } catch (MalformedURLException muex) { + logger.error(FAILED + "Registartion server's url is malformed.", + muex); + failure = "Error with registration server's url"; + } catch (IOException ioex) { + logger.error( + FAILED + + "Failed to open url connection to registration server or writing/reading to/from it.", + ioex); + failure = "Error opening connection to the registration server"; + } + showMessageDialog(null, REGISTRATION_FAILED_MSG, failure, ERROR_MESSAGE); + return false; + } + + private void saveUserRegistrationData(UserRegistrationData regData, + File propertiesFile) { + Properties props = new Properties(); + props.setProperty(TAVERNA_VERSION_PROPERTY_NAME, + regData.getTavernaVersion()); + props.setProperty(FIRST_NAME_PROPERTY_NAME, regData.getFirstName()); + props.setProperty(LAST_NAME_PROPERTY_NAME, regData.getLastName()); + props.setProperty(EMAIL_ADDRESS_PROPERTY_NAME, + regData.getEmailAddress()); + props.setProperty(KEEP_ME_INFORMED_PROPERTY_NAME, + regData.getKeepMeInformed() ? TRUE : FALSE); + props.setProperty(INSTITUTION_OR_COMPANY_PROPERTY_NAME, + regData.getInstitutionOrCompanyName()); + props.setProperty(INDUSTRY_PROPERTY_NAME, regData.getIndustry()); + props.setProperty(FIELD_PROPERTY_NAME, + regData.getPurposeOfUsingTaverna()); + props.setProperty(PURPOSE_PROPERTY_NAME, + regData.getPurposeOfUsingTaverna()); + + // Write the properties file. + try { + props.store(new FileOutputStream(propertiesFile), null); + } catch (Exception e) { + logger.error("Failed to save user registration data locally on disk."); + } + } + + private void closeDialog() { + setVisible(false); + dispose(); + } + + /** + * Adds a light gray or etched border to the top or bottom of a JComponent. + * + * @author David Withers + * @param component + */ + protected void addDivider(JComponent component, final int position, + final boolean etched) { + component.setBorder(new Border() { + private final Color borderColor = new Color(.6f, .6f, .6f); + + @Override + public Insets getBorderInsets(Component c) { + if (position == TOP) + return new Insets(5, 0, 0, 0); + else + return new Insets(0, 0, 5, 0); + } + + @Override + public boolean isBorderOpaque() { + return false; + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, + int width, int height) { + if (position == TOP) { + if (etched) { + g.setColor(borderColor); + g.drawLine(x, y, x + width, y); + g.setColor(WHITE); + g.drawLine(x, y + 1, x + width, y + 1); + } else { + g.setColor(LIGHT_GRAY); + g.drawLine(x, y, x + width, y); + } + } else { + if (etched) { + g.setColor(borderColor); + g.drawLine(x, y + height - 2, x + width, y + height - 2); + g.setColor(WHITE); + g.drawLine(x, y + height - 1, x + width, y + height - 1); + } else { + g.setColor(LIGHT_GRAY); + g.drawLine(x, y + height - 1, x + width, y + height - 1); + } + } + } + }); + } + + private void followHyperlinkToTandCs() { + // Open a Web browser + try { + Desktop.getDesktop().browse(new URI(TERMS_AND_CONDITIONS_URL)); + } catch (Exception ex) { + logger.error("User registration: Failed to launch browser to show terms and conditions at " + + TERMS_AND_CONDITIONS_URL); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationHook.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationHook.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationHook.java new file mode 100644 index 0000000..257c3f3 --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/UserRegistrationHook.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (C) 2009-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl; + +import static java.awt.GraphicsEnvironment.isHeadless; + +import java.io.File; +import java.io.FileFilter; +import java.util.Date; + +import net.sf.taverna.t2.workbench.StartupSPI; +import uk.org.taverna.configuration.app.ApplicationConfiguration; + +public class UserRegistrationHook implements StartupSPI { + /** Delay between when we ask the user about registration, in milliseconds */ + private static final int TWO_WEEKS = 14 * 24 * 3600 * 1000; + public static final String REGISTRATION_DIRECTORY_NAME = "registration"; + public static final String REGISTRATION_DATA_FILE_NAME = "registration_data.properties"; + public static final String REMIND_ME_LATER_FILE_NAME = "remind_me_later"; + public static final String DO_NOT_REGISTER_ME_FILE_NAME = "do_not_register_me"; + + private ApplicationConfiguration applicationConfiguration; + + @Override + public int positionHint() { + return 50; + } + + @Override + public boolean startup() { + File registrationDirectory = getRegistrationDirectory(); + File registrationDataFile = new File(registrationDirectory, + REGISTRATION_DATA_FILE_NAME); + File doNotRegisterMeFile = new File(registrationDirectory, + DO_NOT_REGISTER_ME_FILE_NAME); + File remindMeLaterFile = new File(registrationDirectory, + REMIND_ME_LATER_FILE_NAME); + + // if we are running headlessly just return + if (isHeadless()) + return true; + // For Taverna snapshots - do not ask user to register + if (applicationConfiguration.getName().toLowerCase().contains("snapshot")) + return true; + + // If there is already user's registration data present - exit. + if (registrationDataFile.exists()) + return true; + + // If user did not want to register - exit. + if (doNotRegisterMeFile.exists()) + return true; + + /* + * If user said to remind them - check if more than 2 weeks passed since + * we asked previously. + */ + if (remindMeLaterFile.exists()) { + long lastModified = remindMeLaterFile.lastModified(); + long now = new Date().getTime(); + if (now - lastModified < TWO_WEEKS) + // 2 weeks have not passed since we last asked + return true; + + // Ask user again if they want to register + UserRegistrationForm form = new UserRegistrationForm( + applicationConfiguration.getName(), registrationDataFile, + doNotRegisterMeFile, remindMeLaterFile); + form.setVisible(true); + return true; + } + + /* + * Check if there are previous Taverna versions installed and find the + * latest one that contains user registration data, if any. Ask user if + * they want to upload that previous data. + */ + final File appHomeDirectory = applicationConfiguration.getApplicationHomeDir(); + File parentDirectory = appHomeDirectory.getParentFile(); + FileFilter fileFilter = new FileFilter() { + @Override + public boolean accept(File file) { + return !(file.getName().equals(appHomeDirectory.getName()) + // Exclude Taverna home directory for this app + && file.isDirectory() + && file.getName().toLowerCase().startsWith("taverna-") + // exclude snapshots + && !file.getName().toLowerCase().contains("snapshot") + // exclude command line tool + && !file.getName().toLowerCase().contains("cmd") + // exclude dataviewer + && !file.getName().toLowerCase().contains("dataviewer")); + } + }; + File[] tavernaDirectories = parentDirectory.listFiles(fileFilter); + // Find the latest previous registration data file, if any + File previousRegistrationDataFile = null; + for (File tavernaDirectory : tavernaDirectories) { + File regFile = new File(tavernaDirectory, REGISTRATION_DIRECTORY_NAME + + System.getProperty("file.separator") + REGISTRATION_DATA_FILE_NAME); + if (!regFile.exists()) + continue; + if (previousRegistrationDataFile == null) + previousRegistrationDataFile = regFile; + else if (previousRegistrationDataFile.lastModified() < regFile + .lastModified()) + previousRegistrationDataFile = regFile; + } + + UserRegistrationForm form; + if (previousRegistrationDataFile == null) + // No previous registration file - ask user to register + form = new UserRegistrationForm(applicationConfiguration.getName(), + registrationDataFile, doNotRegisterMeFile, + remindMeLaterFile); + else + /* + * Fill in user's old registration data in the form and ask them to + * register + */ + form = new UserRegistrationForm(applicationConfiguration.getName(), + previousRegistrationDataFile, registrationDataFile, + doNotRegisterMeFile, remindMeLaterFile); + form.setVisible(true); + return true; + } + + /** + * Gets the registration directory where info about registration will be + * saved to. + */ + public File getRegistrationDirectory() { + File home = applicationConfiguration.getApplicationHomeDir(); + + File registrationDirectory = new File(home, REGISTRATION_DIRECTORY_NAME); + if (!registrationDirectory.exists()) + registrationDirectory.mkdir(); + return registrationDirectory; + } + + public void setApplicationConfiguration( + ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/WorkbenchImpl.java ---------------------------------------------------------------------- diff --git a/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/WorkbenchImpl.java b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/WorkbenchImpl.java new file mode 100644 index 0000000..16f189e --- /dev/null +++ b/taverna-workbench-workbench-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/WorkbenchImpl.java @@ -0,0 +1,538 @@ +/******************************************************************************* + * Copyright (C) 2007-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl; + +import static java.awt.GridBagConstraints.BOTH; +import static java.awt.GridBagConstraints.CENTER; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.LINE_START; +import static java.lang.Thread.setDefaultUncaughtExceptionHandler; +import static java.util.prefs.Preferences.userNodeForPackage; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.SwingUtilities.invokeLater; +import static javax.swing.UIManager.getCrossPlatformLookAndFeelClassName; +import static javax.swing.UIManager.getLookAndFeel; +import static javax.swing.UIManager.getLookAndFeelDefaults; +import static javax.swing.UIManager.getSystemLookAndFeelClassName; +import static net.sf.taverna.t2.workbench.MainWindow.setMainWindow; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.errorMessageIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.infoMessageIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.questionMessageIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.warningMessageIcon; +import static org.apache.log4j.Logger.getLogger; + +import java.awt.CardLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.lang.Thread.UncaughtExceptionHandler; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.prefs.Preferences; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JMenuBar; +import javax.swing.JPanel; +import javax.swing.JToolBar; +import javax.swing.UIManager; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.ui.menu.MenuManager.MenuManagerEvent; +import net.sf.taverna.t2.ui.menu.MenuManager.UpdatedMenuManagerEvent; +import net.sf.taverna.t2.workbench.ShutdownSPI; +import net.sf.taverna.t2.workbench.StartupSPI; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.helper.Helper; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Workbench; +import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI; + +import org.apache.log4j.Logger; +import org.simplericity.macify.eawt.Application; +import org.simplericity.macify.eawt.ApplicationAdapter; +import org.simplericity.macify.eawt.ApplicationEvent; +import org.simplericity.macify.eawt.ApplicationListener; +import org.simplericity.macify.eawt.DefaultApplication; + +import uk.org.taverna.commons.plugin.PluginException; +import uk.org.taverna.commons.plugin.PluginManager; +import uk.org.taverna.configuration.app.ApplicationConfiguration; + +/** + * The main workbench frame. + * + * @author David Withers + * @author Stian Soiland-Reyes + */ +public class WorkbenchImpl extends JFrame implements Workbench { + private static final String NIMBUS = "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"; + private static final String LAUNCHER_LOGO_PNG = "/launcher_logo.png"; + private static final long serialVersionUID = 1L; + private static Logger logger = getLogger(WorkbenchImpl.class); + private static Preferences userPreferences = userNodeForPackage(WorkbenchImpl.class); + + private Application osxApp = new DefaultApplication(); + private ApplicationListener osxAppListener = new OSXAppListener(); + private MenuManager menuManager; + private FileManager fileManager; + @SuppressWarnings("unused") + private EditManager editManager; + private PluginManager pluginManager; + private SelectionManager selectionManager; + private WorkbenchConfiguration workbenchConfiguration; + private ApplicationConfiguration applicationConfiguration; + private WorkbenchPerspectives workbenchPerspectives; + private T2ConfigurationFrame t2ConfigurationFrame; + private JToolBar perspectiveToolBar; + private List<StartupSPI> startupHooks; + private List<ShutdownSPI> shutdownHooks; + private final List<PerspectiveSPI> perspectives; + private JMenuBar menuBar; + private JToolBar toolBar; + private MenuManagerObserver menuManagerObserver; + + public WorkbenchImpl(List<StartupSPI> startupHooks, + List<ShutdownSPI> shutdownHooks, List<PerspectiveSPI> perspectives) { + this.perspectives = perspectives; + this.startupHooks = startupHooks; + this.shutdownHooks = shutdownHooks; + setMainWindow(this); + } + + protected void initialize() { + setExceptionHandler(); + setLookAndFeel(); + + // Set icons for Error, Information, Question and Warning messages + UIManager.put("OptionPane.errorIcon", errorMessageIcon); + UIManager.put("OptionPane.informationIcon", infoMessageIcon); + UIManager.put("OptionPane.questionIcon", questionMessageIcon); + UIManager.put("OptionPane.warningIcon", warningMessageIcon); + + // Call the startup hooks + if (!callStartupHooks()) { + System.exit(0); + } + + makeGUI(); + fileManager.newDataflow(); + try { + pluginManager.loadPlugins(); + } catch (PluginException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + /* + * the DataflowEditsListener changes the WorkflowBundle ID for every + * workflow edit and changes the URI so port definitions can't find the + * port they refer to + */ + // TODO check if it's OK to not update the WorkflowBundle ID + //editManager.addObserver(new DataflowEditsListener()); + + closeTheSplashScreen(); + setVisible(true); + } + + private void closeTheSplashScreen() { +// SplashScreen splash = SplashScreen.getSplashScreen(); +// if (splash != null) { +// splash.setClosable(); +// splash.requestClose(); +// } + } + + private void showAboutDialog() { + // TODO implement this! + } + + private void makeGUI() { + setLayout(new GridBagLayout()); + + addWindowListener(new WindowClosingListener()); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + + Helper.setKeyCatcher(this); + + URL launcherLogo = getClass().getResource(LAUNCHER_LOGO_PNG); + if (launcherLogo != null) { + ImageIcon imageIcon = new ImageIcon(launcherLogo); + setIconImage(imageIcon.getImage()); + } + setTitle(applicationConfiguration.getTitle()); + + osxApp.setEnabledPreferencesMenu(true); + osxApp.setEnabledAboutMenu(true); + osxApp.addApplicationListener(osxAppListener); + + /* + * Set the size and position of the Workbench to the last saved values + * or use the default ones the first time it is launched + */ + loadSizeAndLocationPrefs(); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0.1; + gbc.fill = HORIZONTAL; + gbc.anchor = LINE_START; + + add(makeToolbarPanel(), gbc); + + gbc.anchor = CENTER; + gbc.fill = BOTH; + gbc.gridy = 1; + gbc.weightx = 0.1; + gbc.weighty = 0.1; + + add(makePerspectivePanel(), gbc); + + menuBar = menuManager.createMenuBar(); + setJMenuBar(menuBar); + } + + protected JPanel makeToolbarPanel() { + JPanel toolbarPanel = new JPanel(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.anchor = LINE_START; + + toolBar = menuManager.createToolBar(); + toolBar.setFloatable(false); + toolbarPanel.add(toolBar, gbc); + + perspectiveToolBar = new JToolBar("Perspectives"); + perspectiveToolBar.setFloatable(false); + gbc.gridy = 1; + gbc.weightx = 0.1; + gbc.fill = HORIZONTAL; + toolbarPanel.add(perspectiveToolBar, gbc); + + return toolbarPanel; + } + + private JPanel makePerspectivePanel() { + CardLayout perspectiveLayout = new CardLayout(); + JPanel perspectivePanel = new JPanel(perspectiveLayout); + workbenchPerspectives = new WorkbenchPerspectives(perspectiveToolBar, + perspectivePanel, perspectiveLayout, selectionManager); + workbenchPerspectives.setPerspectives(perspectives); + return perspectivePanel; + } + + @Override + public void makeNamedComponentVisible(String componentName) { + // basePane.makeNamedComponentVisible(componentName); + } + + protected void setExceptionHandler() { + setDefaultUncaughtExceptionHandler(new ExceptionHandler()); + } + + public void setStartupHooks(List<StartupSPI> startupHooks) { + this.startupHooks = startupHooks; + } + + /** + * Calls the startup methods on all the {@link StartupSPI}s. If any startup + * method returns <code>false</code> (meaning that the Workbench will not + * function at all) then this method returns <code>false</code>. + */ + private boolean callStartupHooks() { + for (StartupSPI startupSPI : startupHooks) + if (!startupSPI.startup()) + return false; + return true; + } + + @Override + public void exit() { + if (callShutdownHooks()) + System.exit(0); + } + + public void setShutdownHooks(List<ShutdownSPI> shutdownHooks) { + this.shutdownHooks = shutdownHooks; + } + + /** + * Calls all the shutdown on all the {@link ShutdownSPI}s. If a shutdown + * returns <code>false</code> (meaning that the shutdown process should be + * aborted) then this method returns with a value of <code>false</code> + * immediately. + * + * @return <code>true</code> if all the <code>ShutdownSPIs</code> return + * <code>true</code> and the workbench shutdown should proceed + */ + private boolean callShutdownHooks() { + for (ShutdownSPI shutdownSPI : shutdownHooks) + if (!shutdownSPI.shutdown()) + return false; + return true; + } + + /** + * Store current Workbench position and size. + */ + @Override + public void storeSizeAndLocationPrefs() throws IOException { + userPreferences.putInt("width", getWidth()); + userPreferences.putInt("height", getHeight()); + userPreferences.putInt("x", getX()); + userPreferences.putInt("y", getY()); + } + + /** + * Loads last saved Workbench position and size. + */ + private void loadSizeAndLocationPrefs() { + Dimension screen = getToolkit().getScreenSize(); + + int width = userPreferences.getInt("width", (int) (screen.getWidth() * 0.75)); + int height = userPreferences.getInt("height", (int) (screen.getHeight() * 0.75)); + int x = userPreferences.getInt("x", 0); + int y = userPreferences.getInt("y", 0); + + // Make sure our window is not too big + width = Math.min((int) screen.getWidth(), width); + height = Math.min((int) screen.getHeight(), height); + + // Move to upper left corner if we are too far off + if (x > (screen.getWidth() - 50) || x < 0) + x = 0; + if (y > (screen.getHeight() - 50) || y < 0) + y = 0; + + this.setSize(width, height); + this.setLocation(x, y); + } + + public static void setLookAndFeel() { + String defaultLaf = System.getProperty("swing.defaultlaf"); + try { + if (defaultLaf != null) { + UIManager.setLookAndFeel(defaultLaf); + return; + } + } catch (Exception e) { + logger.info("Can't set requested look and feel -Dswing.defaultlaf=" + + defaultLaf, e); + } + String os = System.getProperty("os.name"); + if (os.contains("Mac") || os.contains("Windows")) { + // For OSX and Windows use the system look and feel + String systemLF = getSystemLookAndFeelClassName(); + try { + UIManager.setLookAndFeel(systemLF); + getLookAndFeelDefaults().put("ClassLoader", + WorkbenchImpl.class.getClassLoader()); + logger.info("Using system L&F " + systemLF); + return; + } catch (Exception ex2) { + logger.error("Unable to load system look and feel " + systemLF, + ex2); + } + } + /* + * The system look and feel on *NIX + * (com.sun.java.swing.plaf.gtk.GTKLookAndFeel) looks like Windows 3.1.. + * try to use Nimbus (Java 6e10 and later) + */ + try { + UIManager.setLookAndFeel(NIMBUS); + logger.info("Using Nimbus look and feel"); + return; + } catch (Exception e) { + } + + // Metal should be better than GTK still + try { + String crossPlatform = getCrossPlatformLookAndFeelClassName(); + UIManager.setLookAndFeel(crossPlatform); + logger.info("Using cross platform Look and Feel " + crossPlatform); + return; + } catch (Exception e){ + } + + // Final fallback + try { + String systemLF = getSystemLookAndFeelClassName(); + UIManager.setLookAndFeel(systemLF); + logger.info("Using system platform Look and Feel " + systemLF); + } catch (Exception e){ + logger.info("Using default Look and Feel " + getLookAndFeel()); + } + } + + public void setMenuManager(MenuManager menuManager) { + if (this.menuManager != null && menuManagerObserver != null) + this.menuManager.removeObserver(menuManagerObserver); + this.menuManager = menuManager; + menuManagerObserver = new MenuManagerObserver(); + menuManager.addObserver(menuManagerObserver); + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void refreshPerspectives(Object service, Map<?,?> properties) { + workbenchPerspectives.refreshPerspectives(); + } + + public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + } + + public void setT2ConfigurationFrame(T2ConfigurationFrame t2ConfigurationFrame) { + this.t2ConfigurationFrame = t2ConfigurationFrame; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setPluginManager(PluginManager pluginManager) { + this.pluginManager = pluginManager; + } + + private final class MenuManagerObserver extends + SwingAwareObserver<MenuManagerEvent> { + @Override + public void notifySwing(Observable<MenuManagerEvent> sender, + MenuManagerEvent message) { + if (message instanceof UpdatedMenuManagerEvent + && WorkbenchImpl.this.isVisible()) + refreshMenus(); + } + } + + private void refreshMenus() { + if (menuBar != null) { + menuBar.revalidate(); + menuBar.repaint(); + } + if (toolBar != null) { + toolBar.revalidate(); + toolBar.repaint(); + } + } + + private final class ExceptionHandler implements UncaughtExceptionHandler { + @Override + public void uncaughtException(Thread t, Throwable e) { + logger.error("Uncaught exception in " + t, e); + if (e instanceof Exception + && !workbenchConfiguration.getWarnInternalErrors()) { + /* + * User preference disables warnings - but we'll show it anyway + * if it is an Error (which is more serious) + */ + return; + } + final String message; + final String title; + final int style; + if (t.getClass().getName().equals("java.awt.EventDispatchThread")) { + message = "The user action could not be completed due to an unexpected error:\n" + + e; + title = "Could not complete user action"; + style = ERROR_MESSAGE; + } else { + message = "An unexpected internal error occured in \n" + t + ":\n" + e; + title = "Unexpected internal error"; + style = WARNING_MESSAGE; + } + invokeLater(new Runnable() { + @Override + public void run() { + showMessageDialog(WorkbenchImpl.this, message, title, style); + } + }); + } + } + + private class WindowClosingListener extends WindowAdapter { + @Override + public void windowClosing(WindowEvent e) { + exit(); + } + } + + private class OSXAppListener extends ApplicationAdapter { + @Override + public void handleAbout(ApplicationEvent e) { + showAboutDialog(); + e.setHandled(true); + } + + @Override + public void handleQuit(ApplicationEvent e) { + e.setHandled(true); + exit(); + } + + @Override + public void handlePreferences(ApplicationEvent e) { + e.setHandled(true); + t2ConfigurationFrame.showFrame(); + } + + @Override + public void handleOpenFile(ApplicationEvent e) { + try { + if (e.getFilename() != null) { + fileManager.openDataflow(null, new File(e.getFilename())); + e.setHandled(true); + } + } catch (OpenException | IllegalStateException ex) { + logger.warn("Could not open file " + e.getFilename(), ex); + } + } + } +}