From 37dfaff1e33e10d618a6f4c795d3eda653ef9d01 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe <tlaplus.net@lemmster.de> Date: Mon, 21 Dec 2015 19:41:18 +0100 Subject: [PATCH] Toolbox launch fails during tests. The Toolbox initialization can be described as hackery with layers of workaround on top. This has finally proven unmaintainable which caused this larger refactoring. It now checks the instance location at the correct place instead of in the Activator. Formerly it was done in the Activator, because it would run before the Application which was a result of an upside-down extension point. With the extension point reversed, the Application is executed prior to the Activator. [Bug](Toolbox] --- .../META-INF/MANIFEST.MF | 3 +- .../images/splash_small.bmp | Bin 0 -> 122816 bytes .../plugin.xml | 3 +- .../schema/org.lamport.tla.toolbox.tool.exsd | 0 .../org/lamport/tla/toolbox/Application.java | 38 +++- .../toolbox/ApplicationWorkbenchAdvisor.java | 61 ++---- .../ApplicationWorkbenchWindowAdvisor.java | 9 +- .../tla/toolbox/StandaloneActivator.java | 68 ++++++- .../ToolboxLifecycleParticipantManger.java | 30 +-- .../ToolboxLifecycleParticipant.java | 27 +-- .../tla/toolbox/ui/intro/ToolboxIntoPart.java | 53 ----- .../toolbox/ui/intro/ToolboxIntroPart.java | 190 ++++++++++++++++++ .../META-INF/MANIFEST.MF | 3 +- .../ProverToolboxLifecycleParticipant.java | 7 +- .../META-INF/MANIFEST.MF | 3 +- .../tool/tlc/TLCLifecycleParticipant.java | 5 +- org.lamport.tla.toolbox/META-INF/MANIFEST.MF | 3 +- org.lamport.tla.toolbox/plugin.xml | 13 +- .../org/lamport/tla/toolbox/Activator.java | 24 +-- .../tool/ToolboxLifecycleException.java | 22 -- .../toolbox/ui/navigator/ToolboxExplorer.java | 49 +---- .../ToolboxExplorerResourceListener.java | 71 +++++++ .../tla/toolbox/ui/view/ProblemView.java | 83 -------- .../ui/view/ProblemViewResourceListener.java | 104 ++++++++++ .../toolbox/ui/view/ToolboxWelcomeView.java | 159 +-------------- .../util/pref/UnwantedPreferenceManager.java | 17 +- 26 files changed, 564 insertions(+), 481 deletions(-) create mode 100644 org.lamport.tla.toolbox.product.standalone/images/splash_small.bmp rename {org.lamport.tla.toolbox => org.lamport.tla.toolbox.product.standalone}/schema/org.lamport.tla.toolbox.tool.exsd (100%) rename {org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util => org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox}/ToolboxLifecycleParticipantManger.java (66%) rename {org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool => org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/lifecycle}/ToolboxLifecycleParticipant.java (50%) delete mode 100644 org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntoPart.java create mode 100644 org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java delete mode 100644 org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool/ToolboxLifecycleException.java create mode 100644 org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorerResourceListener.java create mode 100644 org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemViewResourceListener.java diff --git a/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF index 9665635d4..5ff9affd3 100644 --- a/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF @@ -13,7 +13,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ui.forms, org.eclipse.ui.ide;bundle-version="3.4.1", org.eclipse.swt, - org.lamport.tla.toolbox;bundle-version="1.0.0", org.eclipse.equinox.p2.ui.sdk.scheduler;bundle-version="1.0.0" Bundle-ActivationPolicy: lazy Bundle-Activator: org.lamport.tla.toolbox.StandaloneActivator +Export-Package: org.lamport.tla.toolbox.lifecycle, + org.lamport.tla.toolbox.ui.intro diff --git a/org.lamport.tla.toolbox.product.standalone/images/splash_small.bmp b/org.lamport.tla.toolbox.product.standalone/images/splash_small.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c4ea2eebc90bf0c5473b1038beda3ff0a5ac4a2f GIT binary patch literal 122816 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�=3N#WV zRk3_h&C*HL*wN(bCDUr>POZU#YUj>soj1E}{_J+_XlC1-=^Zn0qV^dJCQhA?6HQyt z-@mZ8b3tDh7S!9hu)k~3)QO9xOu&vN_b-?+eZll;xKK-NLrZN#Yh5D-YN)SmtElO& ztm(pzs=M2&XSP&N$BJ63r#IBjZK$7DUxyXVt*cv5RW+}w8iwi?Rn;$sqPoR3&5J71 zHdLi;#DO;DCN7JQpO+LjA1g|TnHT3b8-ij3=EVlgj}1gd^Wp+l#RaUxiedxSB?hj^ z3R;mFv=S@I2ws_$yf!;+ZFVXal$ElkxL{L7`L>F(tyoc2#g3MigDs7Fv7@HFbyb^c zE7oI0HI*A$D>hCoT{oo+7g|55c*UgR<&#QQKvD7Xi3N+HD1Y(f(j}9smrSn0ik42Q zT0FIO=9Jo5P*gK(YW<w44Rb-Le(u!zxpR9KEu64q0Ve8OG{1M@oX&YOT4&5`orx9A zXqmBK()0zBX5c_G=k@i^@9D&edb{RN=*5ZpduC6bI(zaIT&T6Kv8A@5t-c8jwbnK? z)U{XE^i<V$Rn>N*p~~8>>RM1bZ>^cuS~DF3O#`L#y1DiB^Bb_Ed3ALQs;d{&)PPa_ z;_Aefs;HW(=sF0hj;XEmZmsZc!-}drn{%UPCnPRNj$4o%j|C;h&X4n(9q%_ME?{1o zeM7oURhn%z5~@nKX-c<gO}A;mhFUXin)7TMa&e-@e9z_rujYJD?5H%bqda<IMf4<W zXktb5#HPZPEv*Mzn)YHt`|82zysmNsCaS4e-x$^19op6v){YBxhBSAEHbGHHV^>(q zq=Lnh3YSbOTr#m>@sx5<Nj|xHDOLnd=d-5P&W4~VHM9Ejdit|E`?I^isB?Nr-;DD9 z85JmKdRgzB=9%+*7eUi`+sv7msCDLy)|m??O~aAMXDpZmO6LoDIv4bIVL=e_iG2%j zp$WZnrc9kZW$GLpXi%i{mK7Cx0cG5}<@|;SBxq2M166XX=Xo|KBrhhE&S%F2FD+5@ zsua|#6f#6d!iJT&kdR>org~%%LBk51$e=>Npj^-pC*sqqP;dpM^X7ddrt^)p73<5T z?MnId$^;CFL8biq<sxPi^A=7jT1rMbpISe+M$@ZQz^F{vv`pBv6oQ0IOHolVmtKE# z>B33NM$<V)sg7EnH!rVnNG=yNt`M^z22~0e<asqGBrhS9&gaAiFV5A8uM{EHWMXWm zTum9TUPJt(miEIWrt=MTHCrlFy~;&Q%f&2+LZapslGYP*=aZSv8|Kz(`IHG0u&#vH zpg*>3G@WB4^7#$*sOh|MS*345xqwlHxFs>DQqUmJqk))o9uu@E+a$YEm{^mEv7K@? z<-#VF8h$P9M@UZRb=xbn0?UO>iF8A`sCl`B)r9PM<fn6OAA;7E@)`EWmJjuGURm2! zS=)_)h;5(a=!c@F^M<8W5mlgcE@nYY41&_RdmS<9JT`Dax^-oxkYR;5Q6>{(JLPIX zMKQmAL*~4ewnLan9Z&nbu3=}TK}5N*36XAqr1So)*^`QvLE0^oNok)?ZJ1k2N;;q2 zITzFq#cZ6nfqJF`+dglpsRxg@G_=;Cpq83C@OVoPIGuN4l2x_c?KLyoYNlgH)idhr z<~B6UtFOn7>gpHN)XcA`1*3+=)k&?Sq;tbOm+JWBC0GYrlH%tl#6rhgVgu%<Iy8aG zbK-Ikk?DtWW#vM~)y6R`UB{c7kXz~dS{mWwEgNbpFi^wJN{hsDA!8yPP%dU(E^gVM zF>6xMQqYhKXuM_Vq$)gU$&{K|ldES>0i&r6^J}$;F3%^fnB6gVddu_~Ei<s9=`AxC z5*Tlpu%M?4C+h89FtHyen$S0U%CtF?r{Y50ZQWQ=Pg{3eV{b$C#KxKlI8c98)g+v# zdO}UvyxNjQHN^`tQEl<Us*;7JMT<*|7J*Rd($b^}6~ZQ<rY|u~7eT{9m+Fj^HJJ&^ zG855JM#8f6_+<%!bK?W�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 QTOw};mGDD3oii{10I&LW*#H0l literal 0 HcmV?d00001 diff --git a/org.lamport.tla.toolbox.product.standalone/plugin.xml b/org.lamport.tla.toolbox.product.standalone/plugin.xml index 885563653..66f90b8f9 100644 --- a/org.lamport.tla.toolbox.product.standalone/plugin.xml +++ b/org.lamport.tla.toolbox.product.standalone/plugin.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.2"?> <plugin> + <extension-point id="org.lamport.tla.toolbox.tool" name="Toolbox Life Cycle Participant" schema="schema/org.lamport.tla.toolbox.tool.exsd"/> <!-- --> <!-- Application --> @@ -48,7 +49,7 @@ <extension point="org.eclipse.ui.intro"> <intro - class="org.lamport.tla.toolbox.ui.intro.ToolboxIntoPart" + class="org.lamport.tla.toolbox.ui.intro.ToolboxIntroPart" id="org.lamport.tla.toolbox.product.standalone.intro"> </intro> <introProductBinding diff --git a/org.lamport.tla.toolbox/schema/org.lamport.tla.toolbox.tool.exsd b/org.lamport.tla.toolbox.product.standalone/schema/org.lamport.tla.toolbox.tool.exsd similarity index 100% rename from org.lamport.tla.toolbox/schema/org.lamport.tla.toolbox.tool.exsd rename to org.lamport.tla.toolbox.product.standalone/schema/org.lamport.tla.toolbox.tool.exsd diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java index ae1c1f46d..25fa173b0 100644 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java @@ -1,8 +1,15 @@ // Copyright (c) Jan 30, 2012 Microsoft Corporation. All rights reserved. package org.lamport.tla.toolbox; +import java.io.File; +import java.io.IOException; + +import org.eclipse.core.runtime.Platform; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.service.datalocation.Location; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI; @@ -11,7 +18,6 @@ import org.eclipse.ui.PlatformUI; * This class controls all aspects of the application's execution * * @author Simon Zambrovski - * @version $Id$ */ public class Application implements IApplication { @@ -35,6 +41,36 @@ public class Application implements IApplication { + " started without arguments."); } } + + // The call to getStateLocation makes sure the instance location gets + // initialized before we call .lock on it. + StandaloneActivator.getDefault().getStateLocation(); + final Location instanceLocation = Platform.getInstanceLocation(); + // Only allow a single Toolbox instance per workspace to prevent data + // corruption in the workspace files. + try { + if (!instanceLocation.lock()) { + final File workspaceDirectory = new File(Platform.getInstanceLocation().getURL().getFile()); + if (workspaceDirectory.exists()) { + MessageDialog.openError(PlatformUI.createDisplay().getActiveShell(), "Toolbox files cannot be locked", + NLS.bind( + "Could not launch the Toolbox because the associated workspace is currently in use by another Toolbox. Opening two instances on the same workspace leads to data corruption.\n\n" + + "If this is incorrect and there is no other Toolbox running, an earlier Toolbox terminated without releasing the lock. Please manually delete the lock file ''{0}'' and try restarting the Toolbox.", + workspaceDirectory.getAbsolutePath() + .concat(File.separator + ".metadata" + File.separator + ".lock"))); + } + // We showed an error to the user, lets do a "clean" (0) exit to + // not raise a second window with a detailed technical error. + System.exit(0); + } + } catch (IOException e) { + StandaloneActivator.getDefault().logError("Toolbox files cannot be locked", e); + MessageDialog.openError(PlatformUI.createDisplay().getActiveShell(), "Toolbox files cannot be locked", + "Could not launch the Toolbox because acquiring the associated workspace lock failed. We are sorry, this is a bug. Please get in contact with us."); + // We showed an error to the user, lets do a "clean" (0) exit to + // not raise a second window with a detailed technical error. + System.exit(0); + } Display display = PlatformUI.createDisplay(); try { diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchAdvisor.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchAdvisor.java index 638220ae1..1e3536e0f 100644 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchAdvisor.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchAdvisor.java @@ -9,24 +9,18 @@ import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchAdvisor; import org.eclipse.ui.application.WorkbenchWindowAdvisor; import org.eclipse.ui.ide.IDE; -import org.lamport.tla.toolbox.tool.ToolboxHandle; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleException; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleParticipant; -import org.lamport.tla.toolbox.util.ToolboxLifecycleParticipantManger; -import org.lamport.tla.toolbox.util.UIHelper; +import org.lamport.tla.toolbox.ui.intro.ToolboxIntroPart; import org.osgi.framework.Bundle; /** * This workbench advisor creates the window advisor, and specifies * the perspective id for the initial window. * @author Simon Zambrovski - * @version $Id$ */ public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor { // TODO MOVE! - public static final String PERSPECTIVE_ID = "org.lamport.tla.toolbox.ui.perspective.initial"; public static final String IDE_PLUGIN = "org.eclipse.ui.ide"; public static final String PATH_OBJECT = "icons/full/obj16/"; public static final String PATH_WIZBAN = "icons/full/wizban/"; @@ -39,7 +33,6 @@ public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor * Image definition from org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages#IMG_DLGBAN_SAVEAS_DLG */ public static final String IMG_DLGBAN_SAVEAS_DLG = "IMG_DLGBAN_SAVEAS_DLG"; - private ToolboxLifecycleParticipant[] registeredTools; public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) { @@ -48,7 +41,7 @@ public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor public String getInitialWindowPerspectiveId() { - return PERSPECTIVE_ID; + return ToolboxIntroPart.PERSPECTIVE_ID; } /* @@ -80,41 +73,19 @@ public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor configurer.declareImage(symbolicName, desc, shared); } - public boolean preShutdown() - { - if (!ToolboxHandle.getInstanceStore().getBoolean(ToolboxHandle.I_RESTORE_LAST_SPEC)) - { - UIHelper.getActivePage().closeAllEditors(true); - UIHelper.switchPerspective(getInitialWindowPerspectiveId()); - } - - try - { - ToolboxLifecycleParticipantManger.terminate(registeredTools); - } catch (ToolboxLifecycleException e) - { - // TODO - e.printStackTrace(); - } - - return super.preShutdown(); - } + /* (non-Javadoc) + * @see org.eclipse.ui.application.WorkbenchAdvisor#preShutdown() + */ + public boolean preShutdown() { + ToolboxLifecycleParticipantManger.terminate(); + return super.preShutdown(); + } - public void postStartup() - { - super.postStartup(); - - try - { - registeredTools = ToolboxLifecycleParticipantManger.getRegisteredTools(); - ToolboxLifecycleParticipantManger.initialize(registeredTools); - } catch (ToolboxLifecycleException e) - { - // TODO - e.printStackTrace(); - } - - } - - + /* (non-Javadoc) + * @see org.eclipse.ui.application.WorkbenchAdvisor#postStartup() + */ + public void postStartup() { + super.postStartup(); + ToolboxLifecycleParticipantManger.initialize(); + } } diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java index f9b48fe59..61148738e 100644 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java @@ -15,8 +15,6 @@ import org.eclipse.ui.application.IActionBarConfigurer; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchWindowAdvisor; import org.eclipse.ui.internal.ide.EditorAreaDropAdapter; -import org.lamport.tla.toolbox.ui.navigator.ToolboxExplorer; -import org.lamport.tla.toolbox.ui.view.ProblemView; /** * Configuration of the main window @@ -85,14 +83,11 @@ public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor } } } + super.postWindowOpen(); // At this point in time we can be certain that the UI is fully // instantiated (views, editors, menus...). Thus, register // listeners that connect the UI to the workspace resources. - ProblemView.ResourceListener.init(); - ToolboxExplorer.ResourceListener.init(); - - super.postWindowOpen(); + ToolboxLifecycleParticipantManger.postWorkbenchWindowOpen(); } - } diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/StandaloneActivator.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/StandaloneActivator.java index 85fd9953b..2d1c305fb 100644 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/StandaloneActivator.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/StandaloneActivator.java @@ -2,16 +2,20 @@ package org.lamport.tla.toolbox; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.plugin.AbstractUIPlugin; + /** * @author Markus Alexander Kuppe */ -public class StandaloneActivator extends AbstractTLCActivator { +public class StandaloneActivator extends AbstractUIPlugin { + private static final int DEBUG_SEVERITY = -1; public static final String PLUGIN_ID = "org.lamport.tla.toolbox.product.standalone"; private static StandaloneActivator plugin; public StandaloneActivator() { - super(PLUGIN_ID); plugin = this; } @@ -23,4 +27,64 @@ public class StandaloneActivator extends AbstractTLCActivator { public static StandaloneActivator getDefault() { return plugin; } + + /** + * Writes a string and a cause into the error category of the log + * + * @param string + */ + public void logError(String message) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, message)); + } + + /** + * Writes a string and a cause into the error category of the log + * + * @param string + * @param e + */ + public void logError(String message, Throwable cause) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, message, cause)); + } + + /** + * Writes a string and a cause into the warning category of the log + * + * @param string + */ + public void logWarning(String message) { + getLog().log(new Status(IStatus.WARNING, PLUGIN_ID, message)); + } + + /** + * Writes a string and a cause into the warning category of the log + * + * @param string + */ + public void logWarning(String message, Throwable cause) { + getLog().log(new Status(IStatus.WARNING, PLUGIN_ID, message, cause)); + } + + /** + * Writes a string into some debugging place + */ + public void logDebug(String message) { + logDebug(message, null); + } + + /** + * Writes a string into some debugging place + */ + public void logDebug(String message, Throwable cause) { + getLog().log(new Status(IStatus.INFO, PLUGIN_ID, DEBUG_SEVERITY, message, cause)); + } + + /** + * Writes a string into the info category of the log + * + * @param string + */ + public void logInfo(String message) { + getLog().log(new Status(IStatus.INFO, PLUGIN_ID, message)); + } } diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/ToolboxLifecycleParticipantManger.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ToolboxLifecycleParticipantManger.java similarity index 66% rename from org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/ToolboxLifecycleParticipantManger.java rename to org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ToolboxLifecycleParticipantManger.java index cfcb4e20a..9ba934efa 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/ToolboxLifecycleParticipantManger.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ToolboxLifecycleParticipantManger.java @@ -1,17 +1,15 @@ -package org.lamport.tla.toolbox.util; +package org.lamport.tla.toolbox; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleException; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleParticipant; +import org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant; /** * Provides methods for accessing the extensions registered to the toolbox extension points * * @author Simon Zambrovski - * @version $Id$ */ public class ToolboxLifecycleParticipantManger { @@ -22,7 +20,7 @@ public class ToolboxLifecycleParticipantManger * Retrieves tools registered * @return */ - public static ToolboxLifecycleParticipant[] getRegisteredTools() throws ToolboxLifecycleException + private static ToolboxLifecycleParticipant[] getRegisteredTools() { IConfigurationElement[] decls = Platform.getExtensionRegistry().getConfigurationElementsFor(POINT); ToolboxLifecycleParticipant[] extensions = new ToolboxLifecycleParticipant[decls.length]; @@ -34,7 +32,8 @@ public class ToolboxLifecycleParticipantManger extensions[i] = (ToolboxLifecycleParticipant) decls[i].createExecutableExtension(CLASS_ATTR_NAME); } catch (CoreException e) { - throw new ToolboxLifecycleException("Error retrieving the registered tools", e); + StandaloneActivator.getDefault().logError(e.getMessage(), e); + return new ToolboxLifecycleParticipant[0]; } } return extensions; @@ -43,10 +42,10 @@ public class ToolboxLifecycleParticipantManger /** * Distributes the initialize message * @param participants - * @throws ToolboxLifecycleException */ - public static void initialize(ToolboxLifecycleParticipant[] participants) throws ToolboxLifecycleException + public static void initialize() { + final ToolboxLifecycleParticipant[] participants = getRegisteredTools(); Assert.isNotNull(participants); // Activator.getDefault().logDebug("Initializing the tools"); for (int i = 0; i < participants.length; i++) @@ -55,19 +54,26 @@ public class ToolboxLifecycleParticipantManger } } + public static void postWorkbenchWindowOpen() { + final ToolboxLifecycleParticipant[] participants = getRegisteredTools(); + Assert.isNotNull(participants); + for (int i = 0; i < participants.length; i++) { + participants[i].postWorkbenchWindowOpen(); + } + } + /** * Distributes the terminate message * @param participants - * @throws ToolboxLifecycleException */ - public static void terminate(ToolboxLifecycleParticipant[] participants) throws ToolboxLifecycleException + public static void terminate() { + final ToolboxLifecycleParticipant[] participants = getRegisteredTools(); Assert.isNotNull(participants); // Activator.getDefault().logDebug("Terminating the tools"); for (int i = 0; i < participants.length; i++) { participants[i].terminate(); } - } - + } } diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool/ToolboxLifecycleParticipant.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/lifecycle/ToolboxLifecycleParticipant.java similarity index 50% rename from org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool/ToolboxLifecycleParticipant.java rename to org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/lifecycle/ToolboxLifecycleParticipant.java index bd52518a8..abaf9af45 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool/ToolboxLifecycleParticipant.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/lifecycle/ToolboxLifecycleParticipant.java @@ -1,31 +1,34 @@ -package org.lamport.tla.toolbox.tool; +package org.lamport.tla.toolbox.lifecycle; + +import org.eclipse.ui.IWorkbenchWindow; /** * Describes a basic interface for the tool contribution * * @author Simon Zambrovski - * @version $Id$ */ public abstract class ToolboxLifecycleParticipant { /** * Is called during toolbox initialization * The implementation is empty, subclasses may override - * @throws ToolboxLifecycleException */ - public void initialize() throws ToolboxLifecycleException - { - + public void initialize() { + // subclasses may override + } + + /** + * Is called when the {@link IWorkbenchWindow} has been opened. + */ + public void postWorkbenchWindowOpen() { + // subclasses may override } /** * Is called during termination of the toolbox. * The implementation is empty, subclasses may override - * - * @throws ToolboxLifecycleException */ - public void terminate() throws ToolboxLifecycleException - { - - } + public void terminate(){ + // subclasses may override + } } diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntoPart.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntoPart.java deleted file mode 100644 index 14d498f71..000000000 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntoPart.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Microsoft Research. All rights reserved. - * - * The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Contributors: - * Markus Alexander Kuppe - initial API and implementation - ******************************************************************************/ -package org.lamport.tla.toolbox.ui.intro; - -import org.eclipse.swt.widgets.Composite; -import org.eclipse.ui.intro.IIntroPart; -import org.eclipse.ui.part.IntroPart; -import org.lamport.tla.toolbox.ui.view.ToolboxWelcomeView; - -public class ToolboxIntoPart extends IntroPart implements IIntroPart { - - private Composite container; - - /** - * @wbp.parser.entryPoint - */ - public void createPartControl(Composite container) { - this.container = container; - ToolboxWelcomeView.createControl(container); - } - - public void standbyStateChanged(boolean standby) { - // do nothing for now (don't expect users to - // send welcome to standy) - } - - public void setFocus() { - container.setFocus(); - } -} diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java new file mode 100644 index 000000000..4fea0972b --- /dev/null +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.ui.intro; + +import java.net.URL; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.resource.ColorDescriptor; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.help.IWorkbenchHelpSystem; +import org.eclipse.ui.intro.IIntroPart; +import org.eclipse.ui.part.IntroPart; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + +public class ToolboxIntroPart extends IntroPart implements IIntroPart { + + public static final String PERSPECTIVE_ID = "org.lamport.tla.toolbox.ui.perspective.initial"; + + private Composite container; + + /** + * @wbp.parser.entryPoint + */ + public void createPartControl(Composite container) { + this.container = container; + createControl(container); + } + + public static void createControl(Composite container) { + Composite outerContainer = new Composite(container, SWT.NONE); + + // The local resource manager takes care of disposing images, fonts and + // colors when + // the outerContainer Composite is disposed. + final LocalResourceManager localResourceManager = new LocalResourceManager(JFaceResources.getResources(), + outerContainer); + + final GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 2; + outerContainer.setLayout(gridLayout); + final Color backgroundColor = localResourceManager + .createColor(ColorDescriptor.createFrom(new RGB(255, 255, 228))); + outerContainer.setBackground(backgroundColor); + + /* Logo */ + final Label lblImage = new Label(outerContainer, SWT.NONE); + lblImage.setText("Invisible text"); + final Bundle bundle = FrameworkUtil.getBundle(ToolboxIntroPart.class); + final URL url = FileLocator.find(bundle, new Path("images/splash_small.bmp"), null); + final ImageDescriptor logoImage = ImageDescriptor.createFromURL(url); + lblImage.setImage(localResourceManager.createImage(logoImage)); + + /* Welcome header */ + final Label lblHeader = new Label(outerContainer, SWT.WRAP); + + // Double its font size + final FontDescriptor headerFontDescriptor = JFaceResources.getHeaderFontDescriptor(); + final FontData fontData = headerFontDescriptor.getFontData()[0]; + lblHeader.setFont(localResourceManager.createFont(headerFontDescriptor.setHeight(fontData.getHeight() * 2))); + + // Color value (taken from old style.css) + lblHeader.setForeground(localResourceManager.createColor(new RGB(0, 0, 192))); + + lblHeader.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, true, false, 1, 1)); + lblHeader.setText("Welcome to the TLA\u207A Toolbox"); + lblHeader.setBackground(backgroundColor); + + /* What is next section */ + + Label lblSeparator = new Label(outerContainer, SWT.NONE); + lblSeparator.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + + final StyledText styledWhatIsNext = new StyledText(outerContainer, SWT.WRAP | SWT.CENTER); + styledWhatIsNext.setBackground(backgroundColor); + final String whatIsnext = "There is no specification open. Click on Help if you're not sure what you should do next."; + styledWhatIsNext.setText(whatIsnext); + GridData gd_styledWhatIsNext = new GridData(GridData.FILL_HORIZONTAL); + gd_styledWhatIsNext.horizontalAlignment = SWT.LEFT; + gd_styledWhatIsNext.horizontalSpan = 2; + styledWhatIsNext.setLayoutData(gd_styledWhatIsNext); + + StyleRange winStyle = new StyleRange(); + winStyle.underline = true; + winStyle.underlineStyle = SWT.UNDERLINE_LINK; + + int[] winRange = { whatIsnext.indexOf("Help"), "Help".length() }; + StyleRange[] winStyles = { winStyle }; + styledWhatIsNext.setStyleRanges(winRange, winStyles); + + // link styled text to getting started guide + styledWhatIsNext.addListener(SWT.MouseDown, new Listener() { + public void handleEvent(Event event) { + IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem(); + helpSystem.displayHelpResource("/org.lamport.tla.toolbox.doc/html/contents.html"); + } + }); + + /* Getting started text */ + + final StyledText styledGettingStarted = new StyledText(outerContainer, SWT.WRAP | SWT.CENTER); + styledGettingStarted.setBackground(backgroundColor); + final String lblString = "If this is the first time you have used the Toolbox, please read the Getting Started guide."; + styledGettingStarted.setText(lblString); + GridData gd_styledGettingStarted = new GridData(GridData.FILL_HORIZONTAL); + gd_styledGettingStarted.horizontalAlignment = SWT.LEFT; + gd_styledGettingStarted.horizontalSpan = 2; + styledGettingStarted.setLayoutData(gd_styledGettingStarted); + new Label(outerContainer, SWT.NONE); + new Label(outerContainer, SWT.NONE); + + StyleRange style = new StyleRange(); + style.underline = true; + style.underlineStyle = SWT.UNDERLINE_LINK; + + int[] range = { lblString.indexOf("Getting Started"), "Getting Started".length() }; + StyleRange[] styles = { style }; + styledGettingStarted.setStyleRanges(range, styles); + + // link styled text to getting started guide + styledGettingStarted.addListener(SWT.MouseDown, new Listener() { + public void handleEvent(Event event) { + IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem(); + helpSystem.displayHelpResource("/org.lamport.tla.toolbox.doc/html/gettingstarted/gettingstarted.html"); + } + }); + + /* Toolbox version */ + final Label verticalFillUp = new Label(outerContainer, SWT.NONE); + verticalFillUp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, 2, 1)); + verticalFillUp.setBackground(backgroundColor); + + final Label horizontalLine = new Label(outerContainer, SWT.SEPARATOR | SWT.HORIZONTAL); + horizontalLine.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); + + final Label lblVersion = new Label(outerContainer, SWT.WRAP); + lblVersion.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); + lblVersion.setText("Version 1.5.2 of 21 December 2015"); + lblVersion.setBackground(backgroundColor); + } + + public void standbyStateChanged(boolean standby) { + // do nothing for now (don't expect users to + // send welcome to standy) + } + + public void setFocus() { + container.setFocus(); + } +} diff --git a/org.lamport.tla.toolbox.tool.prover/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.tool.prover/META-INF/MANIFEST.MF index 9765c78d2..1ac78412a 100644 --- a/org.lamport.tla.toolbox.tool.prover/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.tool.prover/META-INF/MANIFEST.MF @@ -11,7 +11,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.debug.core;bundle-version="3.5.0", org.eclipse.ui.editors;bundle-version="3.5.0", org.eclipse.jface.text;bundle-version="3.5.0", - org.eclipse.ui.console + org.eclipse.ui.console, + org.lamport.tla.toolbox.product.standalone Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy Import-Package: org.eclipse.ui.forms, diff --git a/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ProverToolboxLifecycleParticipant.java b/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ProverToolboxLifecycleParticipant.java index ad72443f0..0ec0c115a 100644 --- a/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ProverToolboxLifecycleParticipant.java +++ b/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ProverToolboxLifecycleParticipant.java @@ -1,7 +1,6 @@ package org.lamport.tla.toolbox.tool.prover; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleException; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleParticipant; +import org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant; import org.lamport.tla.toolbox.tool.prover.ui.util.ProverHelper; /** @@ -22,10 +21,8 @@ public class ProverToolboxLifecycleParticipant extends ToolboxLifecycleParticipa /** * Is called during termination of the toolbox. * This implementation cancels all running prover jobs. - * - * @throws ToolboxLifecycleException */ - public void terminate() throws ToolboxLifecycleException + public void terminate() { ProverHelper.cancelProverJobs(true); diff --git a/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF index 486d78209..ee1f9989d 100644 --- a/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF @@ -12,7 +12,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.jdt.launching;bundle-version="3.4.1", org.eclipse.core.expressions;bundle-version="3.4.100", org.eclipse.ui.editors;bundle-version="3.5.0", - org.eclipse.jface.text;bundle-version="3.5.0" + org.eclipse.jface.text;bundle-version="3.5.0", + org.lamport.tla.toolbox.product.standalone Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy Export-Package: org.lamport.tla.toolbox.tool.tlc.job, diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCLifecycleParticipant.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCLifecycleParticipant.java index 3c0cc37d7..03a4dfac8 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCLifecycleParticipant.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCLifecycleParticipant.java @@ -1,8 +1,7 @@ package org.lamport.tla.toolbox.tool.tlc; import org.eclipse.core.runtime.jobs.Job; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleException; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleParticipant; +import org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant; import org.lamport.tla.toolbox.tool.tlc.job.TLCJob; /** @@ -15,7 +14,7 @@ public class TLCLifecycleParticipant extends ToolboxLifecycleParticipant { /* (non-Javadoc) * @see org.lamport.tla.toolbox.tool.ToolboxLifecycleParticipant#terminate() */ - public void terminate() throws ToolboxLifecycleException { + public void terminate() { Job.getJobManager().cancel(TLCJob.AllJobsMatcher); } } diff --git a/org.lamport.tla.toolbox/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox/META-INF/MANIFEST.MF index e1235f004..0e2ce0338 100644 --- a/org.lamport.tla.toolbox/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox/META-INF/MANIFEST.MF @@ -17,7 +17,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.expressions;bundle-version="3.4.100", org.lamport.tlatools;bundle-version="1.0.0";visibility:=reexport, org.eclipse.team.ui, - org.eclipse.core.filesystem;bundle-version="1.5.0" + org.eclipse.core.filesystem;bundle-version="1.5.0", + org.lamport.tla.toolbox.product.standalone Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy Bundle-ClassPath: ., diff --git a/org.lamport.tla.toolbox/plugin.xml b/org.lamport.tla.toolbox/plugin.xml index 361f0a4c5..1ec66e70c 100644 --- a/org.lamport.tla.toolbox/plugin.xml +++ b/org.lamport.tla.toolbox/plugin.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.2"?> <plugin> - <extension-point id="org.lamport.tla.toolbox.tool" name="Toolbox Life Cycle Participant" schema="schema/org.lamport.tla.toolbox.tool.exsd"/> <extension-point id="org.lamport.tla.toolbox.spec" name="Specification Life Cycle Participant" schema="schema/org.lamport.tla.toolbox.spec.exsd"/> <!-- --> <!-- Perspectives --> @@ -1349,6 +1348,18 @@ class="org.lamport.tla.toolbox.util.pref.UnwantedPreferenceManager"> </participant> </extension> + <extension + point="org.lamport.tla.toolbox.tool"> + <participant + class="org.lamport.tla.toolbox.ui.navigator.ToolboxExplorerResourceListener"> + </participant> + </extension> + <extension + point="org.lamport.tla.toolbox.tool"> + <participant + class="org.lamport.tla.toolbox.ui.view.ProblemViewResourceListener"> + </participant> + </extension> <!-- --> <!-- Specification content --> diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java index 28453d4c3..59bd2e61b 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java @@ -1,6 +1,5 @@ package org.lamport.tla.toolbox; -import java.io.File; import java.util.concurrent.CountDownLatch; import org.eclipse.core.resources.IResourceChangeEvent; @@ -12,12 +11,8 @@ import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.osgi.util.NLS; -import org.eclipse.ui.PlatformUI; import org.lamport.tla.toolbox.spec.Spec; import org.lamport.tla.toolbox.spec.manager.WorkspaceSpecManager; import org.lamport.tla.toolbox.spec.nature.TLAParsingBuilder.OutOfBuildSpecModulesGatheringDeltaVisitor; @@ -53,22 +48,7 @@ public class Activator extends AbstractTLCActivator { super.start(context); plugin = this; - - // Only allow a single Toolbox instance per workspace to prevent data - // corruption in the workspace files. - if (!Platform.getInstanceLocation().lock()) { - final File workspaceDirectory = new File(Platform.getInstanceLocation().getURL().getFile()); - if (workspaceDirectory.exists()) { - MessageDialog.openError(PlatformUI.createDisplay().getActiveShell(), "Toolbox files cannot be locked", - NLS.bind( - "Could not launch the Toolbox because the associated workspace is currently in use by another Toolbox. Opening two instances on the same workspace leads to data corruption.\n\n" - + "If this is incorrect and there is no other Toolbox running, an earlier Toolbox terminated without releasing the lock. Please manually delete the lock file ''{0}'' and try restarting the Toolbox.", - workspaceDirectory.getAbsolutePath() - .concat(File.separator + ".metadata" + File.separator + ".lock"))); - } - System.exit(1); - } - + // register the listeners IWorkspace workspace = ResourcesPlugin.getWorkspace(); @@ -108,7 +88,7 @@ public class Activator extends AbstractTLCActivator getThread().wait(100); } } catch (InterruptedException e) { - e.printStackTrace(); + logError(e.getMessage(), e); } state = context.getBundle().getState(); } diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool/ToolboxLifecycleException.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool/ToolboxLifecycleException.java deleted file mode 100644 index aeeb727f6..000000000 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/tool/ToolboxLifecycleException.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.lamport.tla.toolbox.tool; - - -/** - * Exception thrown during toolbox initialization or termination - * @author Simon Zambrovski - * @version $Id$ - */ -public class ToolboxLifecycleException extends Exception -{ - private static final long serialVersionUID = 6237014663324928734L; - - public ToolboxLifecycleException(String message) - { - super(message); - } - - public ToolboxLifecycleException(String message, Throwable cause) - { - super(message, cause); - } -} diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorer.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorer.java index de814a994..73c1ece03 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorer.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorer.java @@ -28,10 +28,6 @@ package org.lamport.tla.toolbox.ui.navigator; import java.util.HashMap; import java.util.Map; -import org.eclipse.core.resources.IResourceChangeEvent; -import org.eclipse.core.resources.IResourceChangeListener; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IStructuredSelection; @@ -125,7 +121,7 @@ public class ToolboxExplorer extends CommonNavigator /** * Refreshes the instance of the viewer if any */ - private static void refresh() + static void refresh() { CommonViewer instance = getViewer(); if (instance != null) @@ -159,47 +155,4 @@ public class ToolboxExplorer extends CommonNavigator // contentService.update(); // } // } - - /* - * Use an inner class because instantiation of ProblemView itself should be - * left to the Eclipse foundation and not be triggered directly via new. - */ - public static class ResourceListener implements IResourceChangeListener { - - private static ResourceListener INSTANCE; - - public synchronized static void init() { - if (INSTANCE == null) { - INSTANCE = new ResourceListener(); - } - } - - private ResourceListener() { - // We might have missed events during Toolbox startup when there was - // a workspace but no UI yet. - resourceChanged(null); - - // update CNF viewers - final IWorkspace workspace = ResourcesPlugin.getWorkspace(); - workspace.addResourceChangeListener(this); - } - - public void resourceChanged(final IResourceChangeEvent event) { - UIHelper.runUIAsync(new Runnable() { - public void run() { - ToolboxExplorer.refresh(); - // Expand the current spec and all its children - final CommonViewer viewer = getViewer(); - // Event is only null when this Ctor calls us causing the - // initial expanded state of a spec to be fully expanded. - // Afterwards, the users expanded states is preserved. - if (event == null && viewer != null) { // viewer might already be disposed which happens when the Toolbox shuts down. - final Spec specLoaded = Activator.getSpecManager().getSpecLoaded(); - viewer.expandToLevel(specLoaded, - AbstractTreeViewer.ALL_LEVELS); - } - } - }); - } - } } diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorerResourceListener.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorerResourceListener.java new file mode 100644 index 000000000..cb5322012 --- /dev/null +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/navigator/ToolboxExplorerResourceListener.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Simon Zambrovski - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.ui.navigator; + +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.ui.navigator.CommonViewer; +import org.lamport.tla.toolbox.Activator; +import org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant; +import org.lamport.tla.toolbox.spec.Spec; +import org.lamport.tla.toolbox.util.UIHelper; + +public class ToolboxExplorerResourceListener extends ToolboxLifecycleParticipant implements IResourceChangeListener { + + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant#postWorkbenchWindowOpen() + */ + public void postWorkbenchWindowOpen() { + // We might have missed events during Toolbox startup when there was + // a workspace but no UI yet. + resourceChanged(null); + + // update CNF viewers + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + workspace.addResourceChangeListener(this); + } + + public void resourceChanged(final IResourceChangeEvent event) { + UIHelper.runUIAsync(new Runnable() { + public void run() { + ToolboxExplorer.refresh(); + // Expand the current spec and all its children + final CommonViewer viewer = ToolboxExplorer.getViewer(); + // Event is only null when this Ctor calls us causing the + // initial expanded state of a spec to be fully expanded. + // Afterwards, the users expanded states is preserved. + if (event == null && viewer != null) { // viewer might already be disposed which happens when the Toolbox shuts down. + final Spec specLoaded = Activator.getSpecManager().getSpecLoaded(); + viewer.expandToLevel(specLoaded, + AbstractTreeViewer.ALL_LEVELS); + } + } + }); + } +} \ No newline at end of file diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemView.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemView.java index 4b6320a48..034642a8c 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemView.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemView.java @@ -6,11 +6,6 @@ import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IMarkerDelta; -import org.eclipse.core.resources.IResourceChangeEvent; -import org.eclipse.core.resources.IResourceChangeListener; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; @@ -27,18 +22,12 @@ import org.lamport.tla.toolbox.util.AdapterFactory; import org.lamport.tla.toolbox.util.TLAMarkerHelper; import org.lamport.tla.toolbox.util.UIHelper; import org.lamport.tla.toolbox.util.compare.MarkerComparator; -import org.lamport.tla.toolbox.util.pref.IPreferenceConstants; -import org.lamport.tla.toolbox.util.pref.PreferenceStoreHelper; /** * Shows parse problems * @version $Id$ * @author Simon Zambrovski */ -/** - * @author makuppe - * - */ public class ProblemView extends ViewPart { public static final String ID = "toolbox.view.ProblemView"; @@ -166,76 +155,4 @@ public class ProblemView extends ViewPart { bar.setFocus(); } - - /* - * Use an inner class because instantiation of ProblemView itself should be - * left to the Eclipse foundation and not be triggered directly via new. - */ - public static class ResourceListener implements IResourceChangeListener { - - private static ResourceListener INSTANCE; - - public synchronized static void init() { - if (INSTANCE == null) { - INSTANCE = new ResourceListener(); - } - } - - private ResourceListener() { - // Check if there have been any markers set already - showOrHideProblemView(); - - // ...and listen for new markers - final IWorkspace workspace = ResourcesPlugin.getWorkspace(); - workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_BUILD); - } - - private void showOrHideProblemView() { - boolean showProblems = PreferenceStoreHelper.getInstancePreferenceStore().getBoolean( - IPreferenceConstants.I_PARSER_POPUP_ERRORS); - if (showProblems) { - if (TLAMarkerHelper.currentSpecHasProblems()) { - // This used to be in Activator. However, - // at startup there might not be an - // activePage which results in a - // NullPointerException. Thus, have the - // ProblemView check the parse status when - // UI startup complete. - ProblemView view = (ProblemView) UIHelper.getActivePage().findView(ProblemView.ID); - // show - if (view != null) { - // already shown, hide - UIHelper.hideView(ProblemView.ID); - } - - // not shown, show - UIHelper.openViewNoFocus(ProblemView.ID); - } else { - // hide - UIHelper.hideView(ProblemView.ID); - } - } - } - - private boolean hasMarkerDelta(IResourceChangeEvent event) { - IMarkerDelta[] deltas = event.findMarkerDeltas(TLAMarkerHelper.TOOLBOX_MARKERS_ALL_MARKER_ID, true); - if (deltas.length > 0) { - return true; - } - return false; - } - - public void resourceChanged(IResourceChangeEvent event) { - // no marker update - if (!hasMarkerDelta(event)) { - return; - } else { - UIHelper.runUIAsync(new Runnable() { - public void run() { - showOrHideProblemView(); - } - }); - } - } - } } diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemViewResourceListener.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemViewResourceListener.java new file mode 100644 index 000000000..e49491915 --- /dev/null +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ProblemViewResourceListener.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Simon Zambrovski - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.ui.view; + +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant; +import org.lamport.tla.toolbox.util.TLAMarkerHelper; +import org.lamport.tla.toolbox.util.UIHelper; +import org.lamport.tla.toolbox.util.pref.IPreferenceConstants; +import org.lamport.tla.toolbox.util.pref.PreferenceStoreHelper; + +/* + * Use an inner class because instantiation of ProblemView itself should be + * left to the Eclipse foundation and not be triggered directly via new. + */ +public class ProblemViewResourceListener extends ToolboxLifecycleParticipant implements IResourceChangeListener { + + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant#postWorkbenchWindowOpen() + */ + public void postWorkbenchWindowOpen() { + // Check if there have been any markers set already + showOrHideProblemView(); + + // ...and listen for new markers + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_BUILD); + } + + private void showOrHideProblemView() { + boolean showProblems = PreferenceStoreHelper.getInstancePreferenceStore().getBoolean( + IPreferenceConstants.I_PARSER_POPUP_ERRORS); + if (showProblems) { + if (TLAMarkerHelper.currentSpecHasProblems()) { + // This used to be in Activator. However, + // at startup there might not be an + // activePage which results in a + // NullPointerException. Thus, have the + // ProblemView check the parse status when + // UI startup complete. + ProblemView view = (ProblemView) UIHelper.getActivePage().findView(ProblemView.ID); + // show + if (view != null) { + // already shown, hide + UIHelper.hideView(ProblemView.ID); + } + + // not shown, show + UIHelper.openViewNoFocus(ProblemView.ID); + } else { + // hide + UIHelper.hideView(ProblemView.ID); + } + } + } + + private boolean hasMarkerDelta(IResourceChangeEvent event) { + IMarkerDelta[] deltas = event.findMarkerDeltas(TLAMarkerHelper.TOOLBOX_MARKERS_ALL_MARKER_ID, true); + if (deltas.length > 0) { + return true; + } + return false; + } + + public void resourceChanged(IResourceChangeEvent event) { + // no marker update + if (!hasMarkerDelta(event)) { + return; + } else { + UIHelper.runUIAsync(new Runnable() { + public void run() { + showOrHideProblemView(); + } + }); + } + } +} \ No newline at end of file diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ToolboxWelcomeView.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ToolboxWelcomeView.java index 447b8cb30..5637ae342 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ToolboxWelcomeView.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/ToolboxWelcomeView.java @@ -25,43 +25,17 @@ ******************************************************************************/ package org.lamport.tla.toolbox.ui.view; -import java.net.URL; - -import org.eclipse.core.runtime.FileLocator; -import org.eclipse.core.runtime.Path; -import org.eclipse.jface.resource.ColorDescriptor; -import org.eclipse.jface.resource.FontDescriptor; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.StyleRange; -import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.FontData; -import org.eclipse.swt.graphics.RGB; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.help.IWorkbenchHelpSystem; import org.eclipse.ui.part.ViewPart; +import org.lamport.tla.toolbox.ui.intro.ToolboxIntroPart; import org.lamport.tla.toolbox.util.UIHelper; -import org.osgi.framework.Bundle; -import org.osgi.framework.FrameworkUtil; /** * The toolbox view that is shown when no spec is loaded. * @author Daniel Ricketts - * @version $Id$ */ public class ToolboxWelcomeView extends ViewPart { - public ToolboxWelcomeView() { - } private Composite parentComposite; @@ -71,139 +45,10 @@ public class ToolboxWelcomeView extends ViewPart { parentComposite = parent; - createControl(parent); + ToolboxIntroPart.createControl(parent); UIHelper.setHelp(parent, "WelcomeView"); } - - public static void createControl(Composite container) { - Composite outerContainer = new Composite(container, SWT.NONE); - - // The local resource manager takes care of disposing images, fonts and colors when - // the outerContainer Composite is disposed. - final LocalResourceManager localResourceManager = new LocalResourceManager(JFaceResources.getResources(), - outerContainer); - - final GridLayout gridLayout = new GridLayout(); - gridLayout.numColumns = 2; - outerContainer.setLayout(gridLayout); - final Color backgroundColor = localResourceManager.createColor(ColorDescriptor.createFrom(new RGB(255, 255, 228))); - outerContainer.setBackground(backgroundColor); - - /* Logo */ - final Label lblImage = new Label(outerContainer, SWT.NONE); - lblImage.setText("Invisible text"); - final Bundle bundle = FrameworkUtil.getBundle(ToolboxWelcomeView.class); - final URL url = FileLocator.find(bundle, new Path("images/splash_small.bmp"), null); - final ImageDescriptor logoImage = ImageDescriptor.createFromURL(url); - lblImage.setImage(localResourceManager.createImage(logoImage)); - - /* Welcome header */ - final Label lblHeader = new Label(outerContainer, SWT.WRAP); - - // Double its font size - final FontDescriptor headerFontDescriptor = JFaceResources.getHeaderFontDescriptor(); - final FontData fontData = headerFontDescriptor.getFontData()[0]; - lblHeader.setFont(localResourceManager.createFont(headerFontDescriptor.setHeight(fontData.getHeight() * 2))); - - // Color value (taken from old style.css) - lblHeader.setForeground(localResourceManager.createColor(new RGB(0, 0, 192))); - - lblHeader.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, true, false, 1, 1)); - lblHeader.setText("Welcome to the TLA\u207A Toolbox"); - lblHeader.setBackground(backgroundColor); - - /* What is next section */ - - Label lblSeparator = new Label(outerContainer, SWT.NONE); - lblSeparator.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); - - final StyledText styledWhatIsNext = new StyledText(outerContainer, SWT.WRAP | SWT.CENTER); - styledWhatIsNext.setBackground(backgroundColor); - final String whatIsnext = "There is no specification open. Click on Help if you're not sure what you should do next."; - styledWhatIsNext.setText(whatIsnext); - GridData gd_styledWhatIsNext = new GridData( GridData.FILL_HORIZONTAL); - gd_styledWhatIsNext.horizontalAlignment = SWT.LEFT; - gd_styledWhatIsNext.horizontalSpan = 2; - styledWhatIsNext.setLayoutData(gd_styledWhatIsNext); - - StyleRange winStyle = new StyleRange(); - winStyle.underline = true; - winStyle.underlineStyle = SWT.UNDERLINE_LINK; - - int[] winRange = {whatIsnext.indexOf("Help"), "Help".length()}; - StyleRange[] winStyles = {winStyle}; - styledWhatIsNext.setStyleRanges(winRange, winStyles); - - // link styled text to getting started guide - styledWhatIsNext.addListener(SWT.MouseDown, new Listener() { - public void handleEvent(Event event) { - IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem(); - helpSystem.displayHelpResource("/org.lamport.tla.toolbox.doc/html/contents.html"); - } - }); - -// Composite composite = new Composite(outerContainer, SWT.NONE); -// composite.setLayout(new GridLayout(2, false)); -// composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true, 2, 1)); //SWT.FILL, SWT.CENTER, false, false, 2, 1)); - - /* Getting started text */ - - final StyledText styledGettingStarted = new StyledText(outerContainer, SWT.WRAP | SWT.CENTER); - styledGettingStarted.setBackground(backgroundColor); - final String lblString = "If this is the first time you have used the Toolbox, please read the Getting Started guide."; - styledGettingStarted.setText(lblString); - GridData gd_styledGettingStarted = new GridData( GridData.FILL_HORIZONTAL); - gd_styledGettingStarted.horizontalAlignment = SWT.LEFT; - gd_styledGettingStarted.horizontalSpan = 2; - styledGettingStarted.setLayoutData(gd_styledGettingStarted); - new Label(outerContainer, SWT.NONE); - new Label(outerContainer, SWT.NONE); - - StyleRange style = new StyleRange(); - style.underline = true; - style.underlineStyle = SWT.UNDERLINE_LINK; - - int[] range = {lblString.indexOf("Getting Started"), "Getting Started".length()}; - StyleRange[] styles = {style}; - styledGettingStarted.setStyleRanges(range, styles); - - // link styled text to getting started guide - styledGettingStarted.addListener(SWT.MouseDown, new Listener() { - public void handleEvent(Event event) { - IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem(); - helpSystem.displayHelpResource("/org.lamport.tla.toolbox.doc/html/gettingstarted/gettingstarted.html"); - } - }); - -// This shows a help button next to the styled text -// final Button helpButton = new Button(composite, SWT.PUSH); -// helpButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1)); -// helpButton.setSize(36, 36); -// helpButton.setText (""); -// helpButton.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_LCL_LINKTO_HELP)); -// helpButton.addListener (SWT.Selection, new Listener () { -// public void handleEvent (Event event) { -// IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem(); -//// helpSystem.displayHelp("org.lamport.tla.toolbox.GettingStarted"); -//// helpSystem.displayHelpResource("/org.lamport.tla.toolbox.doc/html/contents.html"); -// helpSystem.displayHelpResource("/org.lamport.tla.toolbox.doc/html/gettingstarted/gettingstarted.html"); -// } -// }); - - /* Toolbox version */ - final Label verticalFillUp = new Label(outerContainer, SWT.NONE); - verticalFillUp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, 2, 1)); - verticalFillUp.setBackground(backgroundColor); - - final Label horizontalLine = new Label(outerContainer, SWT.SEPARATOR | SWT.HORIZONTAL); - horizontalLine.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); - - final Label lblVersion = new Label(outerContainer, SWT.WRAP); - lblVersion.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); - lblVersion.setText("Version 1.5.2 of 21 December 2015"); - lblVersion.setBackground(backgroundColor); - } public void setFocus() { diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/pref/UnwantedPreferenceManager.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/pref/UnwantedPreferenceManager.java index e297e578e..3ed0b3c98 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/pref/UnwantedPreferenceManager.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/pref/UnwantedPreferenceManager.java @@ -3,8 +3,10 @@ package org.lamport.tla.toolbox.util.pref; import org.eclipse.jface.preference.IPreferenceNode; import org.eclipse.jface.preference.PreferenceManager; import org.lamport.tla.toolbox.Activator; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleException; -import org.lamport.tla.toolbox.tool.ToolboxLifecycleParticipant; +import org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant; +import org.lamport.tla.toolbox.tool.ToolboxHandle; +import org.lamport.tla.toolbox.ui.intro.ToolboxIntroPart; +import org.lamport.tla.toolbox.util.UIHelper; /** * This class removes unwanted preference pages that are declared @@ -25,7 +27,7 @@ public class UnwantedPreferenceManager extends ToolboxLifecycleParticipant // TODO Auto-generated constructor stub } - public void initialize() throws ToolboxLifecycleException + public void initialize() { if (Activator.getDefault().getWorkbench() == null) { @@ -95,4 +97,13 @@ public class UnwantedPreferenceManager extends ToolboxLifecycleParticipant } } + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.lifecycle.ToolboxLifecycleParticipant#terminate() + */ + public void terminate() { + if (!ToolboxHandle.getInstanceStore().getBoolean(ToolboxHandle.I_RESTORE_LAST_SPEC)) { + UIHelper.getActivePage().closeAllEditors(true); + UIHelper.switchPerspective(ToolboxIntroPart.PERSPECTIVE_ID); + } + } } -- GitLab