diff options
118 files changed, 10591 insertions, 561 deletions
diff --git a/.gitignore b/.gitignore index 49f591f98..1f4f04464 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,8 @@ tests/test-dwarf/*.ncb /target_explorer/plugins/org.eclipse.tm.te.ui.forms/bin /target_explorer/plugins/org.eclipse.tm.te.ui.swt/target /target_explorer/plugins/org.eclipse.tm.te.ui.swt/bin +/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/target +/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/bin /target_explorer/plugins/org.eclipse.tm.te.ui.views/target /target_explorer/plugins/org.eclipse.tm.te.ui.views/bin /target_explorer/features/org.eclipse.tm.te.feature/target @@ -15,7 +15,7 @@ <name>TCF Parent</name> <properties> - <tycho-version>0.12.0</tycho-version> + <tycho-version>0.13.0</tycho-version> <platform-version>3.7</platform-version> <platform-site>http://download.eclipse.org/eclipse/updates/${platform-version}</platform-site> <cdt-site>http://download.eclipse.org/tools/cdt/builds/hudson/cdt-nightly</cdt-site> @@ -62,6 +62,7 @@ <module>target_explorer/plugins/org.eclipse.tm.te.ui.controls</module> <module>target_explorer/plugins/org.eclipse.tm.te.ui.forms</module> <module>target_explorer/plugins/org.eclipse.tm.te.ui.swt</module> + <module>target_explorer/plugins/org.eclipse.tm.te.ui.terminals</module> <module>target_explorer/plugins/org.eclipse.tm.te.ui.views</module> <module>target_explorer/plugins/org.eclipse.tm.te.tcf.core</module> <module>target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem</module> diff --git a/target_explorer/features/org.eclipse.tm.te.feature/feature.xml b/target_explorer/features/org.eclipse.tm.te.feature/feature.xml index 2dc891b7a..4763ec7bf 100644 --- a/target_explorer/features/org.eclipse.tm.te.feature/feature.xml +++ b/target_explorer/features/org.eclipse.tm.te.feature/feature.xml @@ -97,6 +97,13 @@ unpack="false"/>
<plugin
+ id="org.eclipse.tm.te.ui.terminals"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
id="org.eclipse.tm.te.ui.views"
download-size="0"
install-size="0"
diff --git a/target_explorer/features/org.eclipse.tm.te.sdk.feature/feature.xml b/target_explorer/features/org.eclipse.tm.te.sdk.feature/feature.xml index 79ba18bc7..272c3f5c7 100644 --- a/target_explorer/features/org.eclipse.tm.te.sdk.feature/feature.xml +++ b/target_explorer/features/org.eclipse.tm.te.sdk.feature/feature.xml @@ -92,6 +92,13 @@ unpack="false"/>
<plugin
+ id="org.eclipse.tm.te.ui.terminals.source"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
id="org.eclipse.tm.te.ui.views.source"
download-size="0"
install-size="0"
diff --git a/target_explorer/plugins/org.eclipse.tm.te.rcp.application/src/org/eclipse/tm/te/rcp/application/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tm.te.rcp.application/src/org/eclipse/tm/te/rcp/application/nls/Messages.properties index 338e09e2a..fb56c2f1e 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.rcp.application/src/org/eclipse/tm/te/rcp/application/nls/Messages.properties +++ b/target_explorer/plugins/org.eclipse.tm.te.rcp.application/src/org/eclipse/tm/te/rcp/application/nls/Messages.properties @@ -1,5 +1,5 @@ # -# com.windriver.vtl +# org.eclipse.tm.te.rcp.application # Externalized Strings. # diff --git a/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorServices.exsd b/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorServices.exsd index 2a6643d53..608a3e0d3 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorServices.exsd +++ b/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorServices.exsd @@ -75,12 +75,12 @@ <attribute name="class" type="string">
<annotation>
<documentation>
- The class that implements <code>com.windriver.core.runtime.concurrent.interfaces.IWRExecutor</code> or extends <code>com.windriver.core.runtime.concurrent.executors.AbstractDelegatingExecutorService</code>.
+ The class that implements <code>org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutor</code> or extends <code>org.eclipse.tm.te.runtime.concurrent.executors.AbstractDelegatingExecutorService</code>.
<p>
The executor service implementation class must be specified either by the class attribute or the class child element!
</documentation>
<appinfo>
- <meta.attribute kind="java" basedOn=":com.windriver.core.runtime.concurrent.interfaces.IWRExecutor"/>
+ <meta.attribute kind="java" basedOn="org.eclipse.tm.te.runtime.concurrent.executors.AbstractDelegatingExecutorService:org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutor"/>
</appinfo>
</annotation>
</attribute>
@@ -108,12 +108,12 @@ The executor service implementation class must be specified either by the class <attribute name="class" type="string">
<annotation>
<documentation>
- The class that implements <code>com.windriver.core.runtime.concurrent.interfaces.IWRExecutor</code> or extends <code>com.windriver.core.runtime.concurrent.executors.AbstractDelegatingExecutorService</code>.
+ The class that implements <code>org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutor</code> or extends <code>org.eclipse.tm.te.runtime.concurrent.executors.AbstractDelegatingExecutorService</code>.
<p>
The executor service implementation class must be specified either by the class attribute or the class child element!
</documentation>
<appinfo>
- <meta.attribute kind="java" basedOn=":com.windriver.core.runtime.concurrent.interfaces.IWRExecutor"/>
+ <meta.attribute kind="java" basedOn="org.eclipse.tm.te.runtime.concurrent.executors.AbstractDelegatingExecutorService:org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutor"/>
</appinfo>
</annotation>
</attribute>
diff --git a/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorUtilDelegates.exsd b/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorUtilDelegates.exsd index 4ff64843d..d69521522 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorUtilDelegates.exsd +++ b/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/schema/executorUtilDelegates.exsd @@ -75,12 +75,12 @@ <attribute name="class" type="string">
<annotation>
<documentation>
- The class that implements <code>com.windriver.core.runtime.concurrent.interfaces.IWRExecutorUtilDelegate</code>.
+ The class that implements <code>org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutorUtilDelegate</code>.
<p>
The executor utility delegate implementation class must be specified either by the class attribute or the class child element!
</documentation>
<appinfo>
- <meta.attribute kind="java" basedOn=":com.windriver.core.runtime.concurrent.interfaces.IWRExecutorUtilDelegate"/>
+ <meta.attribute kind="java" basedOn=":org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutorUtilDelegate"/>
</appinfo>
</annotation>
</attribute>
@@ -108,12 +108,12 @@ The executor utility delegate implementation class must be specified either by t <attribute name="class" type="string">
<annotation>
<documentation>
- The class that implements <code>com.windriver.core.runtime.concurrent.interfaces.IWRExecutorUtilDelegate</code>.
+ The class that implements <code>org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutorUtilDelegate</code>.
<p>
The executor utility delegate implementation class must be specified either by the class attribute or the class child element!
</documentation>
<appinfo>
- <meta.attribute kind="java" basedOn=":com.windriver.core.runtime.concurrent.interfaces.IWRExecutorUtilDelegate"/>
+ <meta.attribute kind="java" basedOn=":org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutorUtilDelegate"/>
</appinfo>
</annotation>
</attribute>
diff --git a/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/src/org/eclipse/tm/te/runtime/concurrent/util/ExecutorsUtil.java b/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/src/org/eclipse/tm/te/runtime/concurrent/util/ExecutorsUtil.java index e7a932046..13a98e687 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/src/org/eclipse/tm/te/runtime/concurrent/util/ExecutorsUtil.java +++ b/target_explorer/plugins/org.eclipse.tm.te.runtime.concurrent/src/org/eclipse/tm/te/runtime/concurrent/util/ExecutorsUtil.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.Assert; import org.eclipse.tm.te.runtime.concurrent.Executors; @@ -111,7 +112,7 @@ public final class ExecutorsUtil { static { EXECUTOR = (ISingleThreadedExecutor) Executors.getSharedExecutor("org.eclipse.tm.te.runtime.concurrent.executors.singleThreaded"); //$NON-NLS-1$ Assert.isNotNull(EXECUTOR); - UI_EXECUTOR = (ISingleThreadedExecutor) Executors.getSharedExecutor("org.eclipse.tm.te.ui.executors.platform.display"); //$NON-NLS-1$ + UI_EXECUTOR = (ISingleThreadedExecutor) Executors.getSharedExecutor("org.eclipse.tm.te.ui.executors.SWTDisplay"); //$NON-NLS-1$ } /** @@ -162,19 +163,68 @@ public final class ExecutorsUtil { EXECUTOR.execute(runnable); } } else { - if (EXECUTOR != null) { - EXECUTOR.execute(runnable); + EXECUTOR.execute(runnable); + } + } + } + + + /** + * Schedule the given {@link Runnable} for invocation within the used + * executor thread and blocks the caller until the runnable got executed. + * <p> + * <b>Note:</b> The method is using {@link #wait()} to block the calling + * thread. Therefore the method cannot be called from within + * the executor thread itself. + * + * @param runnable + * The <code>java.lang.Runnable</code> to execute within the + * executor thread. + */ + public static void executeWait(final Runnable runnable) { + Assert.isTrue(!EXECUTOR.isExecutorThread()); + if (runnable == null) return; + + // Wrap the original runnable in another runnable + // to notify ourself + Runnable r = new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } finally { + synchronized(runnable) { + runnable.notifyAll(); + } } } + }; + + if (EXECUTOR instanceof ExecutorService) { + if (!((ExecutorService) EXECUTOR).isShutdown() + && !((ExecutorService) EXECUTOR).isTerminated()) { + EXECUTOR.execute(r); + } + } else { + EXECUTOR.execute(r); + } + + synchronized(runnable) { + try { + runnable.wait(); + } catch (InterruptedException e) { + /* ignored on purpose */ + } } } /** - * Schedule the given {@link Runnable} to run the current workbench display - * thread. + * Schedule the given {@link Runnable} to run the current platform display + * thread and blocks the caller until the runnable got executed. * * @param runnable - * The runnable to execute. + * The <code>java.lang.Runnable</code> to execute within the + * UI thread. */ public static void executeInUI(Runnable runnable) { if (runnable != null) { @@ -192,6 +242,56 @@ public final class ExecutorsUtil { } /** + * Schedule the given {@link Runnable} to run the current platform display + * thread and blocks the caller until the runnable got executed. + * + * @param runnable + * The <code>java.lang.Runnable</code> to execute within the + * UI thread. + */ + public static void executeInUIWait(final Runnable runnable) { + if (runnable == null) return; + + final AtomicBoolean invoked = new AtomicBoolean(false); + + // Wrap the original runnable in another runnable + // to set the invoked flag + Runnable r = new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } finally { + invoked.set(true); + } + } + }; + + if (UI_EXECUTOR instanceof ExecutorService) { + if (!((ExecutorService) UI_EXECUTOR).isShutdown() + && !((ExecutorService) UI_EXECUTOR).isTerminated()) { + UI_EXECUTOR.execute(r); + } + } else { + if (UI_EXECUTOR != null) { + UI_EXECUTOR.execute(r); + } else { + invoked.set(true); + } + } + + waitAndExecute(0, new IConditionTester() { + @Override + public boolean isConditionFulfilled() { + return invoked.get(); + } + @Override + public void cleanup() { + } + }); + } + + /** * Waits either for the given condition tester to signal that the condition, * the caller want's to wait for, has been completely fulfilled or till the * timeout runs out. If the specified condition tester is <code>null</code>, diff --git a/target_explorer/plugins/org.eclipse.tm.te.runtime/src/org/eclipse/tm/te/runtime/events/DisposedEvent.java b/target_explorer/plugins/org.eclipse.tm.te.runtime/src/org/eclipse/tm/te/runtime/events/DisposedEvent.java new file mode 100644 index 000000000..fc8eee27c --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.runtime/src/org/eclipse/tm/te/runtime/events/DisposedEvent.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.runtime.events; + +import java.util.EventObject; + +import org.eclipse.tm.te.runtime.activator.CoreBundleActivator; +import org.eclipse.tm.te.runtime.interfaces.tracing.ITraceIds; + +/** + * Event used to signal the disposal of an element. + */ +public class DisposedEvent extends EventObject { + private static final long serialVersionUID = -8900361742097122798L; + + private final Object data; + + /** + * Constructor. + * + * @param source The event source. Must not be <code>null</code>. + * <p> + * The event source is expected to be of type {@link CTabItem}. + * + * @param data The custom data object or <code>null</code>. + */ + public DisposedEvent(Object source, Object data) { + super(source); + this.data = data; + } + + /** + * Returns the custom data object associated with the disposed terminal console. + * + * @return The custom data object or <code>null</code>. + */ + public final Object getData() { + return data; + } + + /* (non-Javadoc) + * @see java.util.EventObject#toString() + */ + @Override + public String toString() { + StringBuffer toString = new StringBuffer(getClass().getName()); + + String prefix = ""; //$NON-NLS-1$ + // if debugging the event, formating them a little bit better readable. + if (CoreBundleActivator.getTraceHandler().isSlotEnabled(1, ITraceIds.TRACE_EVENTS)) + prefix = "\n\t\t"; //$NON-NLS-1$ + + toString.append(prefix + "{source="); //$NON-NLS-1$ + toString.append(source); + toString.append("}"); //$NON-NLS-1$ + + return toString.toString(); + } + +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF index 072f96b99..70e436d92 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF @@ -18,6 +18,9 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="3.7.0", org.eclipse.tm.te.tcf.locator;bundle-version="1.0.0", org.eclipse.core.filesystem;bundle-version="1.3.100", org.eclipse.ui.ide;bundle-version="3.7.0", + org.eclipse.compare;bundle-version="3.5.200", + org.eclipse.text;bundle-version="3.5.100", + org.eclipse.ui.workbench.texteditor;bundle-version="3.7.0", org.eclipse.core.expressions;bundle-version="3.4.300" Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/icons/obj16/synch_synch.gif b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/icons/obj16/synch_synch.gif Binary files differnew file mode 100644 index 000000000..7437a027a --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/icons/obj16/synch_synch.gif diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties index 7ddba5cac..aa6681902 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties @@ -28,3 +28,26 @@ FSTreeViewerFilter.systemFiles=Protected operating system files (File System (TC PropertiesAction.label=P&roperties PropertiesAction.tooltip=Show Properties of Selection + +navigatorContent.name = Target File System (TCF) +fsmenu.open.label = Open +fsmenu.refresh.label = Refresh +fsmenu.update.label = Update +fsmenu.commit.label = Commit +fsmenu.merge.label = Merge +fsmenu.revert.label = Revert +temenu.open.label = Open +temenu.refresh.label = Refresh +temenu.update.label = Update +temenu.commit.label = Commit +temenu.merge.label = Merge +temenu.revert.label = Revert +command.refresh.name = Refresh File +command.update.name = Update File +command.commit.name = Commit File +command.merge.name = Merge File +command.revert.name = Revert File +decorator.modified.label = Modified Cache +decorator.outdated.label = Outdated Cache +decorator.conflict.label = Conflicting Cache +preference.page.name = Target Explorer
\ No newline at end of file diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml index 6efc5e071..c5136680a 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml @@ -25,11 +25,16 @@ icon="icons/obj16/rootdrive.gif" id="org.eclipse.tm.te.tcf.filesystem.navigator.content" labelProvider="org.eclipse.tm.te.tcf.filesystem.controls.FSTreeLabelProvider" - name="Target File System (TCF)" + name="%navigatorContent.name" priority="normal"> <triggerPoints> - <instanceof value="org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel"/> - <test property="org.eclipse.tm.te.tcf.locator.hasRemoteService" value="FileSystem"/> + <adapt + type="org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel"> + <test + property="org.eclipse.tm.te.tcf.locator.hasRemoteService" + value="FileSystem"> + </test> + </adapt> </triggerPoints> <possibleChildren> <instanceof value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"/> @@ -91,13 +96,13 @@ <menuContribution locationURI="popup:org.eclipse.tm.te.ui.controls.menu.fs?after=additions"> <separator name="group.open" visible="true"/> <command - commandId="org.eclipse.tm.te.tcf.filesystem.commands.open" + commandId="org.eclipse.ui.navigator.Open" helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Open" id="open" - label="Open" + label="%fsmenu.open.label" style="push"> <visibleWhen checkEnabled="false"> - <with variable="activeMenuSelection"> + <with variable="selection"> <count value="1"/> <iterate> <instanceof value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"/> @@ -112,6 +117,166 @@ </with> </visibleWhen> </command> + <separator + name="group.state" + visible="true"> + </separator> + <command + commandId="org.eclipse.ui.file.refresh" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Refresh" + icon="platform:/plugin/org.eclipse.ui/icons/full/elcl16/refresh_nav.gif" + id="refresh" + label="%fsmenu.refresh.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.update" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Update" + id="update" + label="%fsmenu.update.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.commit" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Commit" + id="commit" + label="%fsmenu.commit.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.merge" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Merge" + id="merge" + label="%fsmenu.merge.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.revert" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Revert" + id="revert" + label="%fsmenu.revert.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> <separator name="group.properties" visible="true"/> <command commandId="org.eclipse.ui.file.properties" @@ -152,10 +317,10 @@ </menuContribution> <menuContribution locationURI="popup:org.eclipse.tm.te.ui.views.TargetExplorer#Popup?after=group.open"> <command - commandId="org.eclipse.tm.te.tcf.filesystem.commands.open" + commandId="org.eclipse.ui.navigator.Open" helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Open" id="open" - label="Open" + label="%temenu.open.label" style="push"> <visibleWhen checkEnabled="false"> <with variable="selection"> @@ -173,6 +338,166 @@ </with> </visibleWhen> </command> + <separator + name="group.state" + visible="true"> + </separator> + <command + commandId="org.eclipse.ui.file.refresh" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Refresh" + icon="platform:/plugin/org.eclipse.ui/icons/full/elcl16/refresh_nav.gif" + id="refresh" + label="%temenu.refresh.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.update" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Update" + id="update" + label="%temenu.update.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.commit" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Commit" + id="commit" + label="%temenu.commit.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.merge" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Merge" + id="merge" + label="%temenu.merge.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> + <command + commandId="org.eclipse.tm.te.tcf.filesystem.commands.revert" + helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Revert" + id="revert" + label="%temenu.revert.label" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> + </iterate> + </with> + <not> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn"> + </test> + </not> + </and> + </visibleWhen> + </command> </menuContribution> </extension> @@ -194,30 +519,40 @@ class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.FSTreeNodePropertyTester" id="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode" namespace="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode" - properties="isFile,isDirectory,isBinaryFile,isReadable,isWritable,isExecutable" + properties="isFile,isDirectory,isBinaryFile,isReadable,isWritable,isExecutable,getCacheState" type="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> </propertyTester> + <propertyTester + class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.CachePropertyTester" + id="org.eclipse.tm.te.tcf.filesystem.propertytester.cache" + namespace="org.eclipse.tm.te.tcf.filesystem.propertytester.cache" + properties="isAutoSavingOn" + type="java.lang.Object"> + </propertyTester> </extension> <!-- Target Explorer command contributions --> <extension point="org.eclipse.ui.commands"> <command - id="org.eclipse.tm.te.tcf.filesystem.commands.open" - name="Open"> + id="org.eclipse.tm.te.tcf.filesystem.commands.update" + name="%command.update.name"> + </command> + <command + id="org.eclipse.tm.te.tcf.filesystem.commands.commit" + name="%command.commit.name"> + </command> + <command + id="org.eclipse.tm.te.tcf.filesystem.commands.merge" + name="%command.merge.name"> + </command> + <command + id="org.eclipse.tm.te.tcf.filesystem.commands.revert" + name="%command.revert.name"> </command> </extension> <!-- Target Explorer command handler contributions --> <extension point="org.eclipse.ui.handlers"> - <handler - class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.OpenFileHandler" - commandId="org.eclipse.tm.te.tcf.filesystem.commands.open"> - <enabledWhen> - <with variable="activeMenuSelection"> - <count value="1"/> - </with> - </enabledWhen> - </handler> <!-- This handler contribution is for the double click behaviour in the Target Explore tree view --> @@ -226,25 +561,203 @@ commandId="org.eclipse.ui.navigator.Open"> <activeWhen> <and> - <with variable="activePartId"> - <equals value="org.eclipse.tm.te.ui.views.TargetExplorer"/> - </with> - <with variable="selection"> - <count value="1"/> + <with + variable="selection"> + <count + value="1"> + </count> <iterate> - <instanceof value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"/> - <test property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"/> + <instanceof + value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"> + </instanceof> + <test + forcePluginActivation="true" + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"> + </test> <test + forcePluginActivation="true" property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isReadable"> </test> <not> - <test property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isBinaryFile"/> + <test + forcePluginActivation="true" + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isBinaryFile"> + </test> </not> </iterate> </with> + <with + variable="activePartId"> + <or> + <equals + value="org.eclipse.tm.te.ui.views.TargetExplorer"> + </equals> + <equals + value="org.eclipse.tm.te.ui.view.Editor"> + </equals> + </or> + </with> </and> </activeWhen> </handler> + <handler + class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.RefreshHandler" + commandId="org.eclipse.ui.file.refresh"> + <activeWhen> + <with + variable="activePartId"> + <or> + <equals + value="org.eclipse.tm.te.ui.views.TargetExplorer"> + </equals> + <equals + value="org.eclipse.tm.te.ui.view.Editor"> + </equals> + </or> + </with> + </activeWhen> + </handler> + <handler + class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.UpdateHandler" + commandId="org.eclipse.tm.te.tcf.filesystem.commands.update"> + <enabledWhen> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState" + value="outdated"> + </test> + </iterate> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.CommitHandler" + commandId="org.eclipse.tm.te.tcf.filesystem.commands.commit"> + <enabledWhen> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState" + value="modified"> + </test> + </iterate> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.MergeHandler" + commandId="org.eclipse.tm.te.tcf.filesystem.commands.merge"> + <enabledWhen> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState" + value="conflict"> + </test> + </iterate> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.RevertHandler" + commandId="org.eclipse.tm.te.tcf.filesystem.commands.revert"> + <enabledWhen> + <with + variable="selection"> + <count + value="1"> + </count> + <iterate> + <or> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState" + value="modified"> + </test> + <test + property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState" + value="conflict"> + </test> + </or> + </iterate> + </with> + </enabledWhen> + </handler> + </extension> + + <extension point="org.eclipse.ui.decorators"> + <decorator + icon="icons/ovr/ovr_modified.png" + id="org.eclipse.tm.te.tcf.filesystem.decorators.modified" + label="%decorator.modified.label" + lightweight="true" + location="TOP_RIGHT" + state="true"> + <enablement> + <objectState + name="cache.state" + value="modified"> + </objectState> + </enablement> + </decorator> + <decorator + icon="icons/ovr/ovr_outdated.png" + id="org.eclipse.tm.te.tcf.filesystem.decorators.outdated" + label="%decorator.outdated.label" + lightweight="true" + location="TOP_RIGHT" + state="true"> + <enablement> + <objectState + name="cache.state" + value="outdated"> + </objectState> + </enablement> + </decorator> + <decorator + icon="icons/ovr/ovr_conflict.png" + id="org.eclipse.tm.te.tcf.filesystem.decorators.conflict" + label="%decorator.conflict.label" + lightweight="true" + location="TOP_RIGHT" + state="true"> + <enablement> + <objectState + name="cache.state" + value="conflict"> + </objectState> + </enablement> + </decorator> + </extension> + + <extension point="org.eclipse.ui.preferencePages"> + <page + class="org.eclipse.tm.te.tcf.filesystem.internal.preferences.TargetExplorerPreferencePage" + id="org.eclipse.tm.te.tcf.filesystem.preferencePage" + name="%preference.page.name"> + </page> + </extension> + + <extension point="org.eclipse.core.runtime.adapters"> + <factory + adaptableType="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode" + class="org.eclipse.tm.te.tcf.filesystem.internal.adapters.NodeStateFilterFactory"> + <adapter + type="org.eclipse.ui.IActionFilter"> + </adapter> + </factory> </extension> </plugin> diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java index 08b0c33bf..23fc2074c 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java @@ -7,6 +7,7 @@ * Contributors: * Wind River Systems - initial API and implementation * William Chen (Wind River) - [345387] Open the remote files with a proper editor + * William Chen (Wind River) - [345552] Edit the remote files with a proper editor *******************************************************************************/ package org.eclipse.tm.te.tcf.filesystem.activator; @@ -15,11 +16,19 @@ import java.net.URL; import java.net.URLConnection; import java.util.Hashtable; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.IExecutionListener; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.swt.graphics.Image; import org.eclipse.tm.te.tcf.filesystem.internal.ImageConsts; +import org.eclipse.tm.te.tcf.filesystem.internal.autosave.SaveAllListener; +import org.eclipse.tm.te.tcf.filesystem.internal.autosave.SaveListener; import org.eclipse.tm.te.tcf.filesystem.internal.url.TcfURLConnection; +import org.eclipse.tm.te.tcf.filesystem.model.FSModel; +import org.eclipse.ui.IWorkbenchCommandConstants; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; @@ -27,16 +36,19 @@ import org.osgi.service.url.AbstractURLStreamHandlerService; import org.osgi.service.url.URLConstants; import org.osgi.service.url.URLStreamHandlerService; - /** * The activator class controls the plug-in life cycle */ public class UIPlugin extends AbstractUIPlugin { - // The shared instance + // The shared instance of this plug-in. private static UIPlugin plugin; // The service registration for the "tcf" URL stream handler. private ServiceRegistration<?> regURLStreamHandlerService; - + // The listener which listens to command "SAVE" and synchronize the local file with the target. + private IExecutionListener saveListener; + // The listener which listens to command "SAVE ALL" and synchronize the local file with the target. + private IExecutionListener saveAllListener; + /** * The constructor */ @@ -81,6 +93,16 @@ public class UIPlugin extends AbstractUIPlugin { return new TcfURLConnection(u); } }, properties); + // Add the two execution listeners to command "SAVE" and "SAVE ALL". + ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class); + if (commandService != null) { + saveListener = new SaveListener(); + Command saveCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE); + saveCmd.addExecutionListener(saveListener); + saveAllListener = new SaveAllListener(); + Command saveAllCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE_ALL); + saveAllCmd.addExecutionListener(saveAllListener); + } } /* (non-Javadoc) @@ -92,6 +114,15 @@ public class UIPlugin extends AbstractUIPlugin { regURLStreamHandlerService.unregister(); regURLStreamHandlerService = null; } + // Remove the two execution listeners. + ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class); + if (commandService != null) { + Command saveCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE); + saveCmd.removeExecutionListener(saveListener); + Command saveAllCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE_ALL); + saveAllCmd.removeExecutionListener(saveAllListener); + } + FSModel.getInstance().dispose(); plugin = null; super.stop(context); } @@ -102,12 +133,14 @@ public class UIPlugin extends AbstractUIPlugin { @Override protected void initializeImageRegistry(ImageRegistry registry) { URL url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OBJ + "folder.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_FOLDER, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.FOLDER, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OBJ + "rootdrive.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_ROOT_DRIVE, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.ROOT_DRIVE, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OBJ + "rootdriveopen.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_ROOT_DRIVE_OPEN, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.ROOT_DRIVE_OPEN, ImageDescriptor.createFromURL(url)); + url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OBJ + "synch_synch.gif"); //$NON-NLS-1$ + registry.put(ImageConsts.COMPARE_EDITOR, ImageDescriptor.createFromURL(url)); } /** diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java index d35287e12..2184b2ada 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java @@ -15,8 +15,9 @@ import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Display; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IPeer; import org.eclipse.tm.tcf.protocol.IToken; @@ -27,6 +28,7 @@ import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException; import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle; import org.eclipse.tm.te.tcf.core.Tcf; import org.eclipse.tm.te.tcf.core.interfaces.IChannelManager; +import org.eclipse.tm.te.tcf.filesystem.internal.events.INodeStateListener; import org.eclipse.tm.te.tcf.filesystem.model.FSModel; import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode; import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel; @@ -38,7 +40,7 @@ import org.eclipse.ui.PlatformUI; /** * Target Explorer: File system tree content provider implementation. */ -public class FSTreeContentProvider implements ITreeContentProvider { +public class FSTreeContentProvider implements ITreeContentProvider, INodeStateListener { /** * Static reference to the return value representing no elements. */ @@ -46,19 +48,22 @@ public class FSTreeContentProvider implements ITreeContentProvider { /** * The file system model instance associated with this file system - * tree content provider instance. Each content provider has it's own - * file system mode instance. + * tree content provider instance. */ - /* default*/ final FSModel model = new FSModel(); + /* package */ final static FSModel model = FSModel.getInstance(); - /* default */ Viewer viewer = null; + /* package */ TreeViewer viewer = null; + + public FSTreeContentProvider(){ + model.addNodeStateListener(this); + } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - this.viewer = viewer; + this.viewer = (TreeViewer) viewer; } /* (non-Javadoc) @@ -66,7 +71,7 @@ public class FSTreeContentProvider implements ITreeContentProvider { */ @Override public void dispose() { - model.dispose(); + model.removeNodeStateListener(this); } /** @@ -205,6 +210,7 @@ public class FSTreeContentProvider implements ITreeContentProvider { node.parent = rootNode; node.peerNode = rootNode.peerNode; rootNode.getChildren().add(node); + model.addNode(node); } } @@ -391,6 +397,7 @@ public class FSTreeContentProvider implements ITreeContentProvider { node.parent = parentNode; node.peerNode = parentNode.peerNode; parentNode.getChildren().add(node); + model.addNode(node); } } } @@ -417,7 +424,7 @@ public class FSTreeContentProvider implements ITreeContentProvider { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { - if (viewer instanceof StructuredViewer) ((StructuredViewer)viewer).refresh(parentNode); + viewer.refresh(parentNode); } }); } @@ -546,4 +553,23 @@ public class FSTreeContentProvider implements ITreeContentProvider { return hasChildren; } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.tcf.filesystem.internal.events.INodeStateListener#stateChanged(org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode) + */ + @Override + public void stateChanged(final FSTreeNode node) { + // Make sure that this node is inside of this viewer. + Display display = PlatformUI.getWorkbench().getDisplay(); + if (display.getThread() == Thread.currentThread()) { + viewer.refresh(node); + } else { + display.asyncExec(new Runnable() { + @Override + public void run() { + viewer.refresh(node); + } + }); + } + } } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java index 7385574da..3722356f9 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java @@ -10,7 +10,16 @@ *******************************************************************************/ package org.eclipse.tm.te.tcf.filesystem.controls; +import java.util.Collections; + +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.expressions.EvaluationContext; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.ITreeContentProvider; @@ -23,15 +32,21 @@ import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages; import org.eclipse.tm.te.ui.interfaces.IUIConstants; import org.eclipse.tm.te.ui.trees.AbstractTreeControl; +import org.eclipse.ui.IDecoratorManager; +import org.eclipse.ui.ISources; +import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.navigator.ICommonActionConstants; import org.eclipse.ui.part.MultiPageSelectionProvider; /** * Target Explorer: File system browser control. */ -public class FSTreeControl extends AbstractTreeControl implements ISelectionChangedListener{ +public class FSTreeControl extends AbstractTreeControl implements ISelectionChangedListener, IDoubleClickListener { /** * Constructor. @@ -71,6 +86,7 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan column.setWidth(200); } tree.setHeaderVisible(hasColumns()); + viewer.addDoubleClickListener(this); } /** @@ -95,7 +111,11 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan */ @Override protected ILabelProvider doCreateTreeViewerLabelProvider(TreeViewer viewer) { - return new FSTreeLabelProvider(viewer); + FSTreeLabelProvider labelProvider = new FSTreeLabelProvider(viewer); + IWorkbench workbench = PlatformUI.getWorkbench(); + IDecoratorManager manager = workbench.getDecoratorManager(); + ILabelDecorator decorator = manager.getLabelDecorator(); + return new FSTreeDecoratingLabelProvider(labelProvider,decorator); } /* (non-Javadoc) @@ -130,8 +150,7 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan return IUIConstants.ID_CONTROL_MENUS_BASE + ".menu.fs"; //$NON-NLS-1$; } - /* - * (non-Javadoc) + /* (non-Javadoc) * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ @Override @@ -149,4 +168,38 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan } } + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent) + */ + @Override + public void doubleClick(DoubleClickEvent event) { + // If an handled and enabled command is registered for the ICommonActionConstants.OPEN + // retargetable action id, redirect the double click handling to the command handler. + // + // Note: The default tree node expansion must be re-implemented in the active handler! + ICommandService service = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class); + Command command = service != null ? service.getCommand(ICommonActionConstants.OPEN) : null; + if (command != null && command.isDefined() && command.isEnabled()) { + try { + ISelection selection = event.getSelection(); + EvaluationContext ctx = new EvaluationContext(null, selection); + ctx.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection); + ctx.addVariable(ISources.ACTIVE_MENU_SELECTION_NAME, selection); + ctx.addVariable(ISources.ACTIVE_WORKBENCH_WINDOW_NAME, PlatformUI.getWorkbench().getActiveWorkbenchWindow()); + IWorkbenchPart part = getParentPart(); + if (part != null) { + IWorkbenchPartSite site = part.getSite(); + ctx.addVariable(ISources.ACTIVE_PART_ID_NAME, site.getId()); + ctx.addVariable(ISources.ACTIVE_PART_NAME, part); + ctx.addVariable(ISources.ACTIVE_SITE_NAME, site); + ctx.addVariable(ISources.ACTIVE_SHELL_NAME, site.getShell()); + } + ExecutionEvent executionEvent = new ExecutionEvent(command, Collections.EMPTY_MAP, part, ctx); + command.executeWithChecks(executionEvent); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeDecoratingLabelProvider.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeDecoratingLabelProvider.java new file mode 100644 index 000000000..0b07e5f02 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeDecoratingLabelProvider.java @@ -0,0 +1,91 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.controls;
+
+import org.eclipse.jface.viewers.DecoratingLabelProvider;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelDecorator;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A subclass of DecoratingLabelProvider provides an FS Tree Viewer
+ * with a label provider which combines a nested label provider and an optional
+ * decorator. The decorator decorates the label text, image
+ * provided by the nested label provider.
+ *
+ */
+public class FSTreeDecoratingLabelProvider extends DecoratingLabelProvider implements ITableLabelProvider {
+
+ //The label provider for the execution context viewer.
+ private FSTreeLabelProvider fProvider;
+ //The label decorator decorating the above label provider.
+ private ILabelDecorator fDecorator;
+
+ /**
+ * Create a FSTreeDecoratingLabelProvider with an FSTreeLabelProvider and a decorator.
+ *
+ * @param provider The label provider to be decorated.
+ * @param decorator The label decorator.
+ */
+ public FSTreeDecoratingLabelProvider(FSTreeLabelProvider provider, ILabelDecorator decorator) {
+ super(provider, decorator);
+ fProvider = provider;
+ fDecorator = decorator;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
+ */
+ @Override
+ public Image getColumnImage(Object element, int columnIndex) {
+ Image image = fProvider.getColumnImage(element, columnIndex);
+ if (columnIndex == 0 && fDecorator != null) {
+ if (fDecorator instanceof LabelDecorator) {
+ LabelDecorator ld2 = (LabelDecorator) fDecorator;
+ Image decorated = ld2.decorateImage(image, element, getDecorationContext());
+ if (decorated != null) {
+ return decorated;
+ }
+ } else {
+ Image decorated = fDecorator.decorateImage(image, element);
+ if (decorated != null) {
+ return decorated;
+ }
+ }
+ }
+ return image;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
+ */
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ String text = fProvider.getColumnText(element, columnIndex);
+ if (columnIndex == 0 && fDecorator != null) {
+ if (fDecorator instanceof LabelDecorator) {
+ LabelDecorator ld2 = (LabelDecorator) fDecorator;
+ String decorated = ld2.decorateText(text, element, getDecorationContext());
+ if (decorated != null) {
+ return decorated;
+ }
+ } else {
+ String decorated = fDecorator.decorateText(text, element);
+ if (decorated != null) {
+ return decorated;
+ }
+ }
+ }
+ return text;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeLabelProvider.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeLabelProvider.java index 1b843dc4c..36e3df63c 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeLabelProvider.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeLabelProvider.java @@ -101,9 +101,9 @@ public class FSTreeLabelProvider extends LabelProvider implements ITableLabelPro if (element instanceof FSTreeNode) { FSTreeNode node = (FSTreeNode)element; if ("FSRootDirNode".equals(node.type)) {//$NON-NLS-1$ - return isExpanded ? UIPlugin.getImage(ImageConsts.IMAGE_ROOT_DRIVE_OPEN) : UIPlugin.getImage(ImageConsts.IMAGE_ROOT_DRIVE); + return isExpanded ? UIPlugin.getImage(ImageConsts.ROOT_DRIVE_OPEN) : UIPlugin.getImage(ImageConsts.ROOT_DRIVE); } else if ("FSDirNode".equals(node.type)) { //$NON-NLS-1$ - return isExpanded ? PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER) : UIPlugin.getImage(ImageConsts.IMAGE_FOLDER); + return isExpanded ? PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER) : UIPlugin.getImage(ImageConsts.FOLDER); } else if ("FSFileNode".equals(node.type)) { //$NON-NLS-1$ String key = node.name; Image image = UIPlugin.getImage(key); diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java index a5b6fb7e2..73a20ba45 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java @@ -33,15 +33,20 @@ public interface ImageConsts { /** * The key to access the base folder object image. */ - public static final String IMAGE_FOLDER = "Folder"; //$NON-NLS-1$ + public static final String FOLDER = "Folder"; //$NON-NLS-1$ /** * The key to access the base folder object image. */ - public static final String IMAGE_ROOT_DRIVE = "RootDrive"; //$NON-NLS-1$ + public static final String ROOT_DRIVE = "RootDrive"; //$NON-NLS-1$ /** * The key to access the base folder object image. */ - public static final String IMAGE_ROOT_DRIVE_OPEN = "RootDriveOpen"; //$NON-NLS-1$ + public static final String ROOT_DRIVE_OPEN = "RootDriveOpen"; //$NON-NLS-1$ + + /** + * The key to access the image of compare editor. + */ + public static final String COMPARE_EDITOR = "CompareEditor"; //$NON-NLS-1$ } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilter.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilter.java new file mode 100644 index 000000000..f2fdb240b --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilter.java @@ -0,0 +1,50 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.adapters;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.StateManager;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IActionFilter;
+
+/**
+ * This action filter wraps an FSTreeNode and test its attribute of "cache.state".
+ * It serves as the expression filter of decorations of Target Explorer.
+ */
+public class NodeStateFilter implements IActionFilter {
+ private FSTreeNode node;
+
+ /**
+ * Constructor.
+ *
+ * @param node
+ * The wrapped tree node. Must not be <code>null</code>.
+ */
+ public NodeStateFilter(FSTreeNode node) {
+ Assert.isNotNull(node);
+ this.node = node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ @Override
+ public boolean testAttribute(Object target, String name, String value) {
+ if (name.equals("cache.state") && node.isFile()) { //$NON-NLS-1$
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ if (value == null)
+ value = CacheState.consistent.name();
+ return value.equals(state.name());
+ }
+ return false;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilterFactory.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilterFactory.java new file mode 100644 index 000000000..0e3ca1d74 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilterFactory.java @@ -0,0 +1,62 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.adapters;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IActionFilter;
+
+/**
+ * The adapter factory of <code>FSTreeNode</code> over <code>IActionFilter</code>
+ */
+@SuppressWarnings("rawtypes")
+public class NodeStateFilterFactory implements IAdapterFactory {
+ // The ADAPTERS adapted by this factory.
+ private static Class[] ADAPTERS = {IActionFilter.class};
+ // The fFilters map caching fFilters for FS nodes.
+ private Map<FSTreeNode, NodeStateFilter> filters;
+
+ /**
+ * Constructor.
+ */
+ public NodeStateFilterFactory(){
+ this.filters = Collections.synchronizedMap(new HashMap<FSTreeNode, NodeStateFilter>());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Object adaptableObject, Class adapterType) {
+ if(adaptableObject instanceof FSTreeNode){
+ FSTreeNode node = (FSTreeNode) adaptableObject;
+ NodeStateFilter filter = filters.get(node);
+ if(filter == null){
+ filter = new NodeStateFilter(node);
+ filters.put(node, filter);
+ }
+ return filter;
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
+ */
+ @Override
+ public Class[] getAdapterList() {
+ return ADAPTERS;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveAllListener.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveAllListener.java new file mode 100644 index 000000000..5c44dc833 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveAllListener.java @@ -0,0 +1,193 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * William Chen (Wind River) - [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.autosave;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.StateManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IURIEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The execution listener of command "SAVE ALL", which synchronizes the local
+ * file with the one on the target server after it is saved.
+ */
+public class SaveAllListener implements IExecutionListener {
+ // Dirty nodes that should be saved and synchronized.
+ private List<FSTreeNode> fDirtyNodes;
+ // The file system fModel storing the existing FSTreeNodes.
+ private FSModel fModel;
+ /**
+ * Create the listener listening to command "SAVE ALL".
+ */
+ public SaveAllListener() {
+ this.fModel = FSModel.getInstance();
+ this.fDirtyNodes = new ArrayList<FSTreeNode>();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteSuccess(java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void postExecuteSuccess(String commandId, Object returnValue) {
+ if (!fDirtyNodes.isEmpty()) {
+ try {
+ List<FSTreeNode> modified = new ArrayList<FSTreeNode>();
+ List<FSTreeNode> conflicts = new ArrayList<FSTreeNode>();
+ for (FSTreeNode node : fDirtyNodes) {
+ // Refresh the dirty nodes and get their latest states.
+ StateManager.getInstance().refreshState(node);
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case consistent:
+ break;
+ case outdated:
+ break;
+ case modified:
+ // Reclassifying
+ modified.add(node);
+ break;
+ case conflict:
+ // Reclassifying
+ conflicts.add(node);
+ break;
+ }
+ }
+
+ if (CacheManager.getInstance().isAutoSaving()) {
+ // If auto saving is on.
+ if (!modified.isEmpty()) {
+ // Upload the modified nodes.
+ CacheManager.getInstance().upload(modified.toArray(new FSTreeNode[modified.size()]));
+ }
+ if (!conflicts.isEmpty()) {
+ // Merge the conflicting ones.
+ mergeConflicts(conflicts);
+ }
+ }
+ } catch (TCFException tcfe) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, tcfe.getLocalizedMessage());
+ }
+ }
+ }
+
+ /**
+ * Merge those conflicting nodes.
+ *
+ * @param conflicts The conflicting nodes.
+ */
+ private void mergeConflicts(List<FSTreeNode> conflicts) {
+ for (FSTreeNode node : conflicts) {
+ String title = Messages.SaveAllListener_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.SaveAllListener_SingularMessage, node.name);
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ Shell parent = window.getShell();
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message,
+ MessageDialog.QUESTION, new String[] { Messages.SaveAllListener_Merge,
+ Messages.SaveAllListener_SaveAnyway, Messages.SaveAllListener_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) { // Merge
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ MergeEditorInput mergeInput = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(mergeInput);
+ } else if (index == 1) { // Save anyway
+ CacheManager.getInstance().upload(conflicts.toArray(new FSTreeNode[conflicts.size()]));
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#preExecute(java.lang.String, org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public void preExecute(String commandId, ExecutionEvent event) {
+ fDirtyNodes.clear();
+ IWorkbenchPage page = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
+ IEditorPart[] editors = page.getDirtyEditors();
+ for (IEditorPart editor : editors) {
+ IEditorInput input = editor.getEditorInput();
+ FSTreeNode node = getEditedNode(input);
+ if (node != null) {
+ // If it is a modified node, add it to the dirty node list.
+ fDirtyNodes.add(node);
+ }
+ }
+ }
+
+ /**
+ * Get the corresponding FSTreeNode from the input.
+ * If the input has no corresponding FSTreeNode, return null;
+ * @param input The editor input.
+ * @return The corresponding FSTreeNode or null if it has not.
+ */
+ private FSTreeNode getEditedNode(IEditorInput input){
+ if (input instanceof IURIEditorInput) {
+ //Get the file that is being edited.
+ IURIEditorInput fileInput = (IURIEditorInput) input;
+ URI uri = fileInput.getURI();
+ try {
+ IFileStore store = EFS.getStore(uri);
+ File localFile = store.toLocalFile(0, new NullProgressMonitor());
+ if (localFile != null) {
+ // Get the file's mapped FSTreeNode.
+ FSTreeNode node = fModel.getTreeNode(localFile.toString());
+ return node;
+ }
+ }catch(CoreException e){}
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#notHandled(java.lang.String, org.eclipse.core.commands.NotHandledException)
+ */
+ @Override
+ public void notHandled(String commandId, NotHandledException exception) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteFailure(java.lang.String, org.eclipse.core.commands.ExecutionException)
+ */
+ @Override
+ public void postExecuteFailure(String commandId, ExecutionException exception) {
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveListener.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveListener.java new file mode 100644 index 000000000..a4ffef507 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveListener.java @@ -0,0 +1,142 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * William Chen (Wind River) - [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.autosave;
+
+import java.io.File;
+import java.net.URI;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.StateManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IURIEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The execution listener of command "SAVE", which synchronizes the local file
+ * with the one on the target server after it is saved.
+ */
+public class SaveListener implements IExecutionListener {
+ // Dirty node that should be committed or merged.
+ private FSTreeNode dirtyNode;
+ // The file system fModel.
+ private FSModel model;
+
+ /**
+ * Create a SaveListener listening to command "SAVE".
+ */
+ public SaveListener() {
+ this.model = FSModel.getInstance();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteSuccess(java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void postExecuteSuccess(String commandId, Object returnValue) {
+ if (dirtyNode != null) {
+ try{
+ // Refresh the fDirtyNode's state.
+ StateManager.getInstance().refreshState(dirtyNode);
+ if (CacheManager.getInstance().isAutoSaving()) {
+ CacheState state = StateManager.getInstance().getCacheState(dirtyNode);
+ switch (state) {
+ case conflict:
+ String title = Messages.SaveListener_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.SaveListener_StateChangedMessage, dirtyNode.name);
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ Shell parent = window.getShell();
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message, MessageDialog.QUESTION,
+ new String[] { Messages.SaveListener_Merge, Messages.SaveListener_SaveAnyway, Messages.SaveListener_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) {// Merge
+ LocalTypedElement local = new LocalTypedElement(dirtyNode);
+ RemoteTypedElement remote = new RemoteTypedElement(dirtyNode);
+ MergeEditorInput mergeInput = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(mergeInput);
+ } else if (index == 1) {// Save anyway.
+ CacheManager.getInstance().upload(dirtyNode);
+ }
+ break;
+ case modified:
+ // Save anyway
+ CacheManager.getInstance().upload(dirtyNode);
+ break;
+ case consistent:
+ break;
+ case outdated:
+ break;
+ }
+ }
+ }catch(TCFException tcfe){
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, tcfe.getLocalizedMessage());
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#preExecute(java.lang.String, org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public void preExecute(String commandId, ExecutionEvent event) {
+ dirtyNode = null;
+ IEditorInput input = HandlerUtil.getActiveEditorInput(event);
+ if (input instanceof IURIEditorInput) {
+ IURIEditorInput fileInput = (IURIEditorInput) input;
+ URI uri = fileInput.getURI();
+ try {
+ IFileStore store = EFS.getStore(uri);
+ File localFile = store.toLocalFile(0, new NullProgressMonitor());
+ if (localFile != null) {
+ dirtyNode = model.getTreeNode(localFile.toString());
+ }
+ }catch(CoreException e){
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#notHandled(java.lang.String, org.eclipse.core.commands.NotHandledException)
+ */
+ @Override
+ public void notHandled(String commandId, NotHandledException exception) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteFailure(java.lang.String, org.eclipse.core.commands.ExecutionException)
+ */
+ @Override
+ public void postExecuteFailure(String commandId, ExecutionException exception) {
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/EditableSharedDocumentAdapter.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/EditableSharedDocumentAdapter.java new file mode 100644 index 000000000..310302dfb --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/EditableSharedDocumentAdapter.java @@ -0,0 +1,256 @@ +/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.SharedDocumentAdapter;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.IElementStateListener;
+
+/**
+ * A shared document adapter that tracks whether the element is connected to a
+ * shared document and whether the contents have been flushed from a compare
+ * viewer. When contents are flushed, this adapter will connect to the document
+ * provider to ensure that the changes are not lost (see
+ * {@link #hasBufferedContents()}). In order to avoid a leak, the buffer must
+ * either be saved (see
+ * {@link #saveDocument(IEditorInput, boolean, IProgressMonitor)}) or released
+ * (see {@link #releaseBuffer()}).
+ * <p>
+ * This adapter must have a one-to-one correspondence to a typed element.
+ *
+ * @since 3.7 - Copied from
+ * org.eclipse.team.internal.ui.synchronize.EditableSharedDocumentAdapter
+ */
+public class EditableSharedDocumentAdapter extends SharedDocumentAdapter implements IElementStateListener {
+
+ private int connectionCount;
+ private final ISharedDocumentAdapterListener listener;
+ private IEditorInput bufferedKey;
+
+ /**
+ * Interface that provides this adapter with the state of the typed element
+ * and supports call backs to the element when the adapter state changes.
+ */
+ public interface ISharedDocumentAdapterListener {
+
+ /**
+ * Method that is invoked when the adapter connects to the document
+ * provider. This method is only invoked when the adapter first connects
+ * to the document.
+ */
+ void handleDocumentConnected();
+
+ /**
+ * Method that is invoked when the adapter disconnects from the document
+ * provider. This method is only invoked when the adapter no longer has
+ * any connection to the document provider.
+ */
+ void handleDocumentDisconnected();
+
+ /**
+ * Method invoked when changes in the document are flushed to the
+ * adapter.
+ */
+ void handleDocumentFlushed();
+
+ /**
+ * Method invoked when the file behind the shared document is deleted.
+ */
+ void handleDocumentDeleted();
+
+ /**
+ * Method invoked when the document dirty state changes from dirty to
+ * clean.
+ */
+ void handleDocumentSaved();
+ }
+
+ /**
+ * Create the shared document adapter for the given element.
+ *
+ * @param listener
+ * access to element internals
+ */
+ public EditableSharedDocumentAdapter(ISharedDocumentAdapterListener listener) {
+ super();
+ this.listener = listener;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.SharedDocumentAdapter#connect(org.eclipse.ui.texteditor.IDocumentProvider, org.eclipse.ui.IEditorInput)
+ */
+ @Override
+ public void connect(IDocumentProvider provider, IEditorInput documentKey)
+ throws CoreException {
+ super.connect(provider, documentKey);
+ connectionCount++;
+ if (connectionCount == 1) {
+ provider.addElementStateListener(this);
+ listener.handleDocumentConnected();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.SharedDocumentAdapter#disconnect(org.eclipse.ui.texteditor.IDocumentProvider, org.eclipse.ui.IEditorInput)
+ */
+ @Override
+ public void disconnect(IDocumentProvider provider, IEditorInput documentKey) {
+ try {
+ super.disconnect(provider, documentKey);
+ } finally {
+ if (connectionCount > 0)
+ connectionCount--;
+ if (connectionCount == 0) {
+ provider.removeElementStateListener(this);
+ listener.handleDocumentDisconnected();
+ }
+ }
+ }
+
+ /**
+ * Return whether the element is connected to a shared document.
+ *
+ * @return whether the element is connected to a shared document
+ */
+ public boolean isConnected() {
+ return connectionCount > 0;
+ }
+
+ /**
+ * Save the shared document of the element of this adapter.
+ *
+ * @param input
+ * the document key of the element.
+ * @param monitor
+ * a progress monitor
+ * @return whether the save succeeded or not
+ * @throws CoreException
+ */
+ public boolean saveDocument(IEditorInput input, IProgressMonitor monitor)
+ throws CoreException {
+ if (isConnected()) {
+ IDocumentProvider provider = SharedDocumentAdapter
+ .getDocumentProvider(input);
+ try {
+ saveDocument(provider, input, provider.getDocument(input),
+ true, monitor);
+ } finally {
+ // When we write the document, remove out hold on the buffer
+ releaseBuffer();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Release the buffer if this adapter has buffered the contents in response
+ * to a
+ * {@link #flushDocument(IDocumentProvider, IEditorInput, IDocument, boolean)}
+ * .
+ */
+ public void releaseBuffer() {
+ if (bufferedKey != null) {
+ IDocumentProvider provider = SharedDocumentAdapter
+ .getDocumentProvider(bufferedKey);
+ provider.disconnect(bufferedKey);
+ bufferedKey = null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ISharedDocumentAdapter#flushDocument(org.eclipse.ui.texteditor.IDocumentProvider, org.eclipse.ui.IEditorInput, org.eclipse.jface.text.IDocument, boolean)
+ */
+ @Override
+ public void flushDocument(IDocumentProvider provider,
+ IEditorInput documentKey, IDocument document, boolean overwrite)
+ throws CoreException {
+ if (!hasBufferedContents()) {
+ // On a flush, make an extra connection to the shared document so it
+ // will be kept even
+ // if it is no longer being viewed.
+ bufferedKey = documentKey;
+ provider.connect(bufferedKey);
+ }
+ this.listener.handleDocumentFlushed();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementContentAboutToBeReplaced(java.lang.Object)
+ */
+ @Override
+ public void elementContentAboutToBeReplaced(Object element) {
+ // Nothing to do
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementContentReplaced(java.lang.Object)
+ */
+ @Override
+ public void elementContentReplaced(Object element) {
+ // Nothing to do
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementDeleted(java.lang.Object)
+ */
+ @Override
+ public void elementDeleted(Object element) {
+ listener.handleDocumentDeleted();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementDirtyStateChanged(java.lang.Object, boolean)
+ */
+ @Override
+ public void elementDirtyStateChanged(Object element, boolean isDirty) {
+ if (!isDirty) {
+ this.listener.handleDocumentSaved();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementMoved(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public void elementMoved(Object originalElement, Object movedElement) {
+ // Nothing to do
+ }
+
+ /**
+ * Return whether the adapter has buffered contents. The adapter buffers
+ * contents by connecting to the document through the document provider.
+ * This means that the adapter must be disconnected either by saving or
+ * discarding the buffer.
+ *
+ * @return whether the adapter has buffered contents
+ */
+ public boolean hasBufferedContents() {
+ return bufferedKey != null;
+ }
+
+ /**
+ * Override getDocumentKey in the super class to provide an editor input
+ * when the element is a <code>LocalTypedElement</code>.
+ */
+ @Override
+ public IEditorInput getDocumentKey(Object element) {
+ if (element instanceof LocalTypedElement) {
+ LocalTypedElement localElement = (LocalTypedElement) element;
+ return localElement.getEditorInput();
+ }
+ return super.getDocumentKey(element);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalFileSaveable.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalFileSaveable.java new file mode 100644 index 000000000..be42037ed --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalFileSaveable.java @@ -0,0 +1,479 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.compare.IContentChangeListener;
+import org.eclipse.compare.IContentChangeNotifier;
+import org.eclipse.compare.ISharedDocumentAdapter;
+import org.eclipse.compare.SharedDocumentAdapter;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.ISaveablesLifecycleListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartConstants;
+import org.eclipse.ui.Saveable;
+import org.eclipse.ui.SaveablesLifecycleEvent;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+
+/**
+ * A <code>LocalFileSaveable</code> is used as the saveable object that provides
+ * the save behavior for the merge editor input(<code>MergeEditorInput</code>).
+ */
+public class LocalFileSaveable extends Saveable implements
+ IPropertyChangeListener, ISharedDocumentAdapterListener,
+ IContentChangeListener {
+ // The property listener list.
+ private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+
+ // The merge input that provides the left and the right compared elements.
+ private final MergeInput input;
+
+ // The input of the editor that provides the comparing view.
+ private final MergeEditorInput editorInput;
+
+ // If the current document of the file is being saved.
+ private boolean saving;
+
+ // The local file element.
+ private LocalTypedElement fileElement;
+
+ // The document provided by the local file.
+ private IDocument document;
+
+ // If the current document has been connected.
+ private boolean connected;
+
+ /**
+ * Create the file-based saveable comparison.
+ *
+ * @param input
+ * the compare input to be save
+ * @param editorInput
+ * the editor input containing the comparison
+ * @param fileElement
+ * the file element that handles the saving and change
+ * notification
+ */
+ public LocalFileSaveable(MergeInput input, MergeEditorInput editorInput, LocalTypedElement fileElement) {
+ Assert.isNotNull(input);
+
+ this.input = input;
+ this.editorInput = editorInput;
+ this.fileElement = fileElement;
+ this.fileElement.addContentChangeListener(this);
+ this.fileElement.setDocumentListener(this);
+ }
+
+ /**
+ * Dispose of the saveable.
+ */
+ public void dispose() {
+ fileElement.removeContentChangeListener(this);
+ fileElement.discardBuffer();
+ fileElement.setDocumentListener(null);
+ }
+
+ /**
+ * Performs the save.
+ *
+ * @param monitor The progress monitor.
+ *
+ * @throws CoreException If the save operation fails.
+ */
+ protected void performSave(IProgressMonitor monitor) throws CoreException {
+ try {
+ saving = true;
+ monitor.beginTask(null, 100);
+ // First, we need to flush the viewers so the changes get buffered
+ // in the input
+ flushViewers(monitor);
+ // Then we tell the input to commit its changes
+ // Only the left is ever saveable
+ if (fileElement.isDirty()) {
+ if (fileElement.isConnected()) {
+ fileElement.store2Document(monitor);
+ } else {
+ fileElement.store2Cache(monitor);
+ }
+ }
+ } finally {
+ // Make sure we fire a change for the compare input to update the
+ // viewers
+ fireInputChange();
+ setDirty(false);
+ saving = false;
+ monitor.done();
+ //Trigger upload action
+ FSTreeNode node = fileElement.getFSTreeNode();
+ CacheManager.getInstance().upload(node);
+ }
+ }
+
+ /**
+ * Flush the contents of any viewers into the compare input.
+ *
+ * @param monitor
+ * a progress monitor
+ * @throws CoreException
+ */
+ protected void flushViewers(IProgressMonitor monitor) throws CoreException {
+ editorInput.saveChanges(monitor);
+ }
+
+ /**
+ * Fire an input change for the compare input after it has been saved.
+ */
+ protected void fireInputChange() {
+ editorInput.getCompareResult().fireInputChanged();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#isDirty()
+ */
+ @Override
+ public boolean isDirty() {
+ return editorInput.isSaveNeeded();
+ }
+
+ /**
+ * Marks the editor input dirty.
+ *
+ * @param dirty <code>True</code> if the editor is dirty, <code>false</code> if not.
+ */
+ protected void setDirty(boolean dirty) {
+ if (isDirty() != dirty) {
+ editorInput.setDirty(dirty);
+ firePropertyChange(IWorkbenchPartConstants.PROP_DIRTY);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getName()
+ */
+ @Override
+ public String getName() {
+ // Return the name of the file element as held in the compare input
+ if (fileElement.equals(input.getLeft())) {
+ return input.getLeft().getName();
+ }
+ if (fileElement.equals(input.getRight())) {
+ return input.getRight().getName();
+ }
+
+ // Fallback call returning name of the main non-null element of the input
+ //
+ // see org.eclipse.team.internal.ui.mapping.AbstractCompareInput#getMainElement()
+ return input.getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getToolTipText()
+ */
+ @Override
+ public String getToolTipText() {
+ return editorInput.getToolTipText();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getImageDescriptor()
+ */
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ Image image = input.getImage();
+ if (image != null)
+ return ImageDescriptor.createFromImage(image);
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ @Override
+ public void propertyChange(PropertyChangeEvent e) {
+ String propertyName = e.getProperty();
+ if (CompareEditorInput.DIRTY_STATE.equals(propertyName)) {
+ boolean changed = false;
+ Object newValue = e.getNewValue();
+ if (newValue instanceof Boolean)
+ changed = ((Boolean) newValue).booleanValue();
+
+ ContentMergeViewer cmv = (ContentMergeViewer) e.getSource();
+
+ if (fileElement.equals(input.getLeft())) {
+ if (changed && cmv.internalIsLeftDirty())
+ setDirty(changed);
+ else if (!changed && !cmv.internalIsLeftDirty()) {
+ setDirty(changed);
+ }
+ }
+ if (fileElement.equals(input.getRight())) {
+ if (changed && cmv.internalIsRightDirty())
+ setDirty(changed);
+ else if (!changed && !cmv.internalIsRightDirty()) {
+ setDirty(changed);
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ if (document != null) {
+ return document.hashCode();
+ }
+ return input.hashCode();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (!(obj instanceof Saveable))
+ return false;
+
+ if (document != null) {
+ Object otherDocument = ((Saveable) obj).getAdapter(IDocument.class);
+
+ if (document == null && otherDocument == null)
+ return false;
+
+ return document != null && document.equals(otherDocument);
+ }
+
+ if (obj instanceof LocalFileSaveable) {
+ LocalFileSaveable saveable = (LocalFileSaveable) obj;
+ return saveable.input.equals(input);
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter == IDocument.class) {
+ if (document != null)
+ return document;
+ if (fileElement.isConnected()) {
+ ISharedDocumentAdapter sda = (ISharedDocumentAdapter) fileElement
+ .getAdapter(ISharedDocumentAdapter.class);
+ if (sda != null) {
+ IEditorInput input = sda.getDocumentKey(fileElement);
+ if (input != null) {
+ IDocumentProvider provider = SharedDocumentAdapter
+ .getDocumentProvider(input);
+ if (provider != null)
+ return provider.getDocument(input);
+ }
+ }
+ }
+ }
+ if (adapter == IEditorInput.class) {
+ return fileElement.getEditorInput();
+ }
+ return super.getAdapter(adapter);
+ }
+
+ /**
+ * Get an ISaveablesLifecycleListener from the specified workbench
+ * part.
+ * @param part The workbench part.
+ * @return The listener.
+ */
+ private ISaveablesLifecycleListener getSaveablesLifecycleListener(
+ IWorkbenchPart part) {
+ if (part instanceof ISaveablesLifecycleListener)
+ return (ISaveablesLifecycleListener) part;
+
+ Object adapted = part.getAdapter(ISaveablesLifecycleListener.class);
+ if (adapted instanceof ISaveablesLifecycleListener)
+ return (ISaveablesLifecycleListener) adapted;
+
+ adapted = Platform.getAdapterManager().loadAdapter(part,
+ ISaveablesLifecycleListener.class.getName());
+ if (adapted instanceof ISaveablesLifecycleListener)
+ return (ISaveablesLifecycleListener) adapted;
+
+ return (ISaveablesLifecycleListener) part.getSite().getService(
+ ISaveablesLifecycleListener.class);
+ }
+
+ /**
+ * When the document of the local file is connected, register this saveable.
+ */
+ private void registerSaveable() {
+ ICompareContainer container = editorInput.getContainer();
+ IWorkbenchPart part = container.getWorkbenchPart();
+ if (part != null) {
+ ISaveablesLifecycleListener lifecycleListener = getSaveablesLifecycleListener(part);
+ // Remove this saveable from the lifecycle listener
+ lifecycleListener.handleLifecycleEvent(new SaveablesLifecycleEvent(
+ part, SaveablesLifecycleEvent.POST_CLOSE,
+ new Saveable[] { this }, false));
+ // Now fix the hashing so it uses the fConnected fDocument
+ IDocument document = (IDocument) getAdapter(IDocument.class);
+ if (document != null) {
+ this.document = document;
+ }
+ // Finally, add this saveable back to the listener
+ lifecycleListener.handleLifecycleEvent(new SaveablesLifecycleEvent(
+ part, SaveablesLifecycleEvent.POST_OPEN,
+ new Saveable[] { this }, false));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentConnected()
+ */
+ @Override
+ public void handleDocumentConnected() {
+ if (connected)
+ return;
+ connected = true;
+ registerSaveable();
+ fileElement.setDocumentListener(null);
+ }
+
+ @Override
+ public void handleDocumentDeleted() {
+ // Ignore
+ }
+
+ @Override
+ public void handleDocumentDisconnected() {
+ // Ignore
+ }
+
+ @Override
+ public void handleDocumentFlushed() {
+ // Ignore
+ }
+
+ @Override
+ public void handleDocumentSaved() {
+ // Ignore
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.compare.IContentChangeListener#contentChanged(org.eclipse.compare.IContentChangeNotifier)
+ */
+ @Override
+ public void contentChanged(IContentChangeNotifier source) {
+ if (!saving) {
+ try {
+ performSave(new NullProgressMonitor());
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void doSave(IProgressMonitor monitor) throws CoreException {
+ if (isDirty()) {
+ performSave(monitor);
+ setDirty(false);
+ }
+ }
+
+ /**
+ * Revert any changes in the buffer back to the last saved state.
+ *
+ * @param monitor
+ * a progress monitor on <code>null</code> if progress feedback
+ * is not required
+ */
+ public void doRevert(IProgressMonitor monitor) {
+ if (!isDirty())
+ return;
+ fileElement.discardBuffer();
+ setDirty(false);
+ }
+
+ /**
+ * Add a property change listener. Adding a listener that is already
+ * registered has no effect.
+ *
+ * @param listener
+ * the listener
+ */
+ public void addPropertyListener(IPropertyListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Remove a property change listener. Removing a listener that is not
+ * registered has no effect.
+ *
+ * @param listener
+ * the listener
+ */
+ public void removePropertyListener(IPropertyListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Fire a property change event for this buffer.
+ *
+ * @param property
+ * the property that changed
+ */
+ protected void firePropertyChange(final int property) {
+ Object[] allListeners = listeners.getListeners();
+ for (int i = 0; i < allListeners.length; i++) {
+ final Object object = allListeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ ((IPropertyListener) object).propertyChanged(
+ LocalFileSaveable.this, property);
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // handled by platform
+ }
+ });
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalTypedElement.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalTypedElement.java new file mode 100644 index 000000000..fe6c66ec3 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalTypedElement.java @@ -0,0 +1,306 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.compare.IEditableContent;
+import org.eclipse.compare.ISharedDocumentAdapter;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+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.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.ide.FileStoreEditorInput;
+
+/**
+ * A <code>LocalTypedElement</code> extends <code>MergeTypedElement</code> and
+ * wraps an <code>FSTreeNode</code> so that it can be used as the left element
+ * of a <code>MergeEditorInput</code>. It implements the interface
+ * <code>IEditableContent</code> so that it is editable.
+ */
+public class LocalTypedElement extends MergeTypedElement implements
+ IEditableContent, IAdaptable, ISharedDocumentAdapterListener {
+ // If the current edited file is dirty.
+ private boolean dirty;
+ // The shared document adapter
+ private EditableSharedDocumentAdapter documentAdapter;
+ // The shared document listener.
+ private ISharedDocumentAdapterListener documentListener;
+
+ /**
+ * Creates a <code>LocalTypedElement</code> for the given resource.
+ *
+ * @param resource
+ * the resource
+ */
+ public LocalTypedElement(FSTreeNode node) {
+ super(node);
+ setContent(getContent());
+ dirty = false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter == ISharedDocumentAdapter.class) {
+ if (documentAdapter == null)
+ documentAdapter = new EditableSharedDocumentAdapter(this);
+ return documentAdapter;
+ }
+ return Platform.getAdapterManager().getAdapter(this, adapter);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.BufferedContent#setContent(byte[])
+ */
+ @Override
+ public void setContent(byte[] contents) {
+ dirty = true;
+ super.setContent(contents);
+ }
+
+ /**
+ * Set the document listener.
+ *
+ * @param documentListener
+ * the document listener.
+ */
+ public void setDocumentListener(
+ ISharedDocumentAdapterListener documentListener) {
+ this.documentListener = documentListener;
+ }
+
+ /**
+ * Return an input stream that opens that locally cached file to provide the
+ * content.
+ *
+ * @return a buffered input stream containing the contents of this file
+ * @exception CoreException
+ * if the contents of this storage could not be accessed
+ */
+ @Override
+ protected InputStream createStream() throws CoreException {
+ try {
+ IPath cachePath = CacheManager.getInstance().getCachePath(node);
+ File cacheFile = cachePath.toFile();
+ return new BufferedInputStream(new FileInputStream(cacheFile));
+ } catch (FileNotFoundException e) {
+ IStatus error = new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ throw new CoreException(error);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IEditableContent#isEditable()
+ */
+ @Override
+ public boolean isEditable() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IEditableContent#replace(org.eclipse.compare.ITypedElement, org.eclipse.compare.ITypedElement)
+ */
+ @Override
+ public ITypedElement replace(ITypedElement dest, ITypedElement src) {
+ return dest;
+ }
+
+ /**
+ * Save the shared document for this element. The save can only be performed
+ * if the element is connected to a shared document. If the element is not
+ * connected, <code>false</code> is returned.
+ *
+ * @param overwrite
+ * indicates whether overwrite should be performed while saving
+ * the given element if necessary
+ * @param monitor
+ * a progress monitor
+ * @throws CoreException
+ */
+ public boolean store2Document(IProgressMonitor monitor)
+ throws CoreException {
+ if (isConnected()) {
+ IEditorInput input = documentAdapter.getDocumentKey(this);
+ documentAdapter.saveDocument(input, monitor);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Judges whether the content has been changed.
+ *
+ * @return
+ */
+ public boolean isDirty() {
+ return dirty
+ || (documentAdapter != null && documentAdapter
+ .hasBufferedContents());
+ }
+
+ /**
+ * Set the dirty state.
+ *
+ * @param dirty
+ * The dirty state.
+ */
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
+ }
+
+ /**
+ * If the document adapter has been connected.
+ *
+ * @return true if it is not null and connected.
+ */
+ public boolean isConnected() {
+ return documentAdapter != null && documentAdapter.isConnected();
+ }
+
+ /**
+ * Return the path to the local file of this node. It is used to compute its
+ * hash code and as the title of the comparison editor.
+ */
+ @Override
+ public String toString() {
+ File cacheFile = CacheManager.getInstance().getCacheFile(node);
+ return cacheFile.toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentConnected()
+ */
+ @Override
+ public void handleDocumentConnected() {
+ if (documentListener != null)
+ documentListener.handleDocumentConnected();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentDeleted()
+ */
+ @Override
+ public void handleDocumentDeleted() {
+ if (documentListener != null)
+ documentListener.handleDocumentDeleted();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentDisconnected()
+ */
+ @Override
+ public void handleDocumentDisconnected() {
+ if (documentListener != null)
+ documentListener.handleDocumentDisconnected();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentFlushed()
+ */
+ @Override
+ public void handleDocumentFlushed() {
+ fireContentChanged();
+ if (documentListener != null)
+ documentListener.handleDocumentFlushed();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentSaved()
+ */
+ @Override
+ public void handleDocumentSaved() {
+ if (documentListener != null)
+ documentListener.handleDocumentSaved();
+ }
+
+ /**
+ * Get an editor input for this file.
+ *
+ * @return The editor input.
+ */
+ public IEditorInput getEditorInput() {
+ IPath path = CacheManager.getInstance().getCachePath(node);
+ IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
+ return new FileStoreEditorInput(fileStore);
+ }
+
+ /**
+ * Save to its local file when the document has not been connected yet.
+ *
+ * @param monitor
+ * The monitor that reports the progress.
+ */
+ public void store2Cache(IProgressMonitor monitor) throws CoreException {
+ File cacheFile = CacheManager.getInstance().getCacheFile(node);
+ monitor.beginTask(Messages.LocalTypedElement_SavingFile + cacheFile.getName(), 100);
+ InputStream is = getContents();
+ BufferedOutputStream bos = null;
+ try {
+ long total = cacheFile.length();
+ bos = new BufferedOutputStream(new FileOutputStream(cacheFile));
+ byte[] data = new byte[10 * 1024];
+ int length;
+ long current = 0;
+ int currProgress = 0;
+ while ((length = is.read(data)) > 0) {
+ bos.write(data, 0, length);
+ bos.flush();
+ current += length;
+ int progress = (int) (current * 100 / total);
+ if (currProgress != progress) {
+ monitor.worked(progress - currProgress);
+ currProgress = progress;
+ }
+ }
+ setDirty(false);
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e));
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException ex) {
+ }
+ if (bos != null) {
+ try {
+ bos.close();
+ } catch (Exception e) {
+ }
+ }
+ // Notify the local file element that the document has changed.
+ fireContentChanged();
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeEditorInput.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeEditorInput.java new file mode 100644 index 000000000..5358126ea --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeEditorInput.java @@ -0,0 +1,408 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.IPropertyChangeNotifier;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.osgi.util.TextProcessor;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.ImageConsts;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.ISaveablesSource;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartConstants;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.Saveable;
+/**
+ * A <code>MergeEditorInput</code> is the input of a compare editor used to
+ * compare the local file and the remote file in a Target Explorer file system.
+ */
+public class MergeEditorInput extends CompareEditorInput implements
+ ISaveablesSource, IPropertyListener, ICompareInputChangeListener {
+
+ // The left element is a local file which can be editable.
+ private LocalTypedElement left;
+ // The right element is the remote file which is read only.
+ private RemoteTypedElement right;
+
+ // The active page of the current workbench.
+ private final IWorkbenchPage page;
+
+ // The current input change listener list.
+ private final ListenerList inputChangeListeners = new ListenerList(ListenerList.IDENTITY);
+ // The current saveable for the left element, i.e., the local file.
+ private LocalFileSaveable saveable;
+
+ /**
+ * Creates a new MergeEditorInput.
+ *
+ * @param left
+ * @param right
+ * @param page
+ */
+ public MergeEditorInput(LocalTypedElement left, RemoteTypedElement right, IWorkbenchPage page) {
+ super(new CompareConfiguration());
+ this.page = page;
+ this.left = left;
+ this.right = right;
+ configureCompare();
+ }
+
+ /**
+ * Configure the compare configuration using the left
+ * and right elements. Set the left and right label.
+ * Set the editable status.
+ */
+ protected void configureCompare() {
+ CompareConfiguration cc = getCompareConfiguration();
+ cc.setLeftEditable(true);
+ cc.setRightEditable(false);
+
+ String name = TextProcessor.process(left.getName());
+ String label = "Local: " + name; //$NON-NLS-1$
+ cc.setLeftLabel(label);
+
+ name = TextProcessor.process(right.toString());
+ label = "Remote: " + name; //$NON-NLS-1$
+ cc.setRightLabel(label);
+ }
+
+ /**
+ * Returns <code>true</code> if the other object is of type
+ * <code>MergeEditorInput</code> and both of their corresponding fLeft and
+ * fRight objects are identical. The content is not considered.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj instanceof MergeEditorInput) {
+ MergeEditorInput other = (MergeEditorInput) obj;
+ return other.left.equals(left) && other.right.equals(right);
+ }
+ return false;
+ }
+
+ /**
+ * Prepare the compare input of this editor input. This method is not
+ * intended to be overridden of extended by subclasses (but is not final for
+ * backwards compatibility reasons). The implementation of this method in
+ * this class delegates the creation of the compare input to the
+ * {@link #prepareCompareInput(IProgressMonitor)} method which subclasses
+ * must implement.
+ *
+ * @see org.eclipse.compare.CompareEditorInput#prepareInput(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ protected Object prepareInput(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ right.cacheContents(monitor);
+ MergeInput input = new MergeInput(left, right);
+ setTitle(input.getName());
+ return input;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getToolTipText()
+ */
+ @Override
+ public String getToolTipText() {
+ return "Compare " + left + " and " + right; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getTitle()
+ */
+ @Override
+ public String getTitle() {
+ return "Compare " + left.getName() + " with Local Cache"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Override the super method to provide clear typed input.
+ */
+ @Override
+ public MergeInput getCompareResult() {
+ return (MergeInput) super.getCompareResult();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#handleDispose()
+ */
+ @Override
+ protected void handleDispose() {
+ super.handleDispose();
+ ICompareInput compareInput = getCompareResult();
+ if (compareInput != null)
+ compareInput.removeCompareInputChangeListener(this);
+ if(getCompareResult()!=null){
+ getSaveable().removePropertyListener(this);
+ getSaveable().dispose();
+ }
+ left.discardBuffer();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#contentsCreated()
+ */
+ @Override
+ protected void contentsCreated() {
+ super.contentsCreated();
+ if (getCompareResult() != null) {
+ getCompareResult().addCompareInputChangeListener(this);
+ getSaveable().addPropertyListener(this);
+ setDirty(getSaveable().isDirty());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPropertyListener#propertyChanged(java.lang.Object, int)
+ */
+ @Override
+ public void propertyChanged(Object source, int propId) {
+ if (propId == IWorkbenchPartConstants.PROP_DIRTY && getCompareResult()!=null) {
+ setDirty(getSaveable().isDirty());
+ }
+ }
+
+ /**
+ * Close the editor if it is not dirty. If it is still dirty, let the
+ * content merge viewer handle the compare input change.
+ *
+ * @param checkForUnsavedChanges
+ * whether to check for unsaved changes
+ * @return <code>true</code> if the editor was closed (note that the close
+ * may be asynchronous)
+ */
+ protected boolean closeEditor(boolean checkForUnsavedChanges) {
+ if (isSaveNeeded() && checkForUnsavedChanges) {
+ return false;
+ }
+ final IWorkbenchPage page = getPage();
+ if (page == null)
+ return false;
+
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Shell shell = page.getWorkbenchWindow().getShell();
+ if (shell == null)
+ return;
+
+ IEditorPart part = page.findEditor(MergeEditorInput.this);
+ getPage().closeEditor(part, false);
+ }
+ };
+ if (Display.getCurrent() != null) {
+ runnable.run();
+ } else {
+ Shell shell = page.getWorkbenchWindow().getShell();
+ if (shell == null)
+ return false;
+ Display display = shell.getDisplay();
+ display.asyncExec(runnable);
+ }
+ return true;
+ }
+
+ /**
+ * Get the current active page.
+ * @return the workbench page.
+ */
+ IWorkbenchPage getPage() {
+ if (page == null)
+ return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ return page;
+ }
+
+ /**
+ * Propagate the input change event to the compare input change listener list.
+ */
+ /* default */void propogateInputChange() {
+ if (!inputChangeListeners.isEmpty()) {
+ Object[] allListeners = inputChangeListeners.getListeners();
+ for (int i = 0; i < allListeners.length; i++) {
+ final ICompareInputChangeListener listener = (ICompareInputChangeListener) allListeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ listener.compareInputChanged(getCompareResult());
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Logged by the safe runner
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Get the fSaveable that provides the save behavior for this compare editor
+ * input. The {@link #createSaveable()} is called to create the fSaveable if
+ * it does not yet exist. This method cannot be called until after the input
+ * is prepared (i.e. until after the {@link #run(IProgressMonitor)} method
+ * is called which will in turn will invoke
+ * {@link #prepareCompareInput(IProgressMonitor)}.
+ *
+ * @return fSaveable that provides the save behavior for this compare editor
+ * input.
+ */
+ public LocalFileSaveable getSaveable() {
+ if (saveable == null) {
+ assert getCompareResult() != null;
+ saveable = new LocalFileSaveable(getCompareResult(), this, left);
+ }
+ return saveable;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
+ */
+ @Override
+ public Saveable[] getActiveSaveables() {
+ if (getCompareResult() == null)
+ return new Saveable[0];
+ return new Saveable[] { getSaveable() };
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.ISaveablesSource#getSaveables()
+ */
+ @Override
+ public Saveable[] getSaveables() {
+ return getActiveSaveables();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#addCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void addCompareInputChangeListener(ICompareInput input,
+ ICompareInputChangeListener listener) {
+ if (input == getCompareResult()) {
+ inputChangeListeners.add(listener);
+ } else {
+ super.addCompareInputChangeListener(input, listener);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void removeCompareInputChangeListener(ICompareInput input,
+ ICompareInputChangeListener listener) {
+ if (input == getCompareResult()) {
+ inputChangeListeners.remove(listener);
+ } else {
+ super.removeCompareInputChangeListener(input, listener);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getTitleImage()
+ */
+ @Override
+ public Image getTitleImage() {
+ return UIPlugin.getImage(ImageConsts.COMPARE_EDITOR);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getImageDescriptor()
+ */
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ return UIPlugin.getImageDescriptor(ImageConsts.COMPARE_EDITOR);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#findContentViewer(org.eclipse.jface.viewers.Viewer, org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public Viewer findContentViewer(Viewer oldViewer, ICompareInput input,
+ Composite parent) {
+ Viewer newViewer = super.findContentViewer(oldViewer, input, parent);
+ boolean isNewViewer = newViewer != oldViewer;
+ if (isNewViewer && newViewer instanceof IPropertyChangeNotifier && getCompareResult()!=null) {
+ // Register the model for change events if appropriate
+ final IPropertyChangeNotifier dsp = (IPropertyChangeNotifier) newViewer;
+ dsp.addPropertyChangeListener(getSaveable());
+ Control c = newViewer.getControl();
+ c.addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ dsp.removePropertyChangeListener(getSaveable());
+ }
+ });
+ }
+ return newViewer;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#canRunAsJob()
+ */
+ @Override
+ public boolean canRunAsJob() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#isDirty()
+ */
+ @Override
+ public boolean isDirty() {
+ if (saveable != null)
+ return saveable.isDirty();
+ return super.isDirty();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener#compareInputChanged(org.eclipse.compare.structuremergeviewer.ICompareInput)
+ */
+ @Override
+ public void compareInputChanged(ICompareInput source) {
+ if (source == getCompareResult()) {
+ boolean closed = false;
+ if (source.getKind() == Differencer.NO_CHANGE) {
+ closed = closeEditor(true);
+ }
+ if (!closed) {
+ // The editor was closed either because the compare
+ // input still has changes or because the editor input
+ // is dirty. In either case, fire the changes
+ // to the registered listeners
+ propogateInputChange();
+ }
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeInput.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeInput.java new file mode 100644 index 000000000..3d461e840 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeInput.java @@ -0,0 +1,158 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * An abstract compare input whose purpose is to support change notification
+ * through a {@link CompareInputChangeNotifier}.
+ */
+public class MergeInput implements ICompareInput {
+
+ // The left element.
+ private ITypedElement left;
+ // The right element.
+ private ITypedElement right;
+ // The compare input change listener list.
+ private final ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+
+ /**
+ * Create a <code>MergeInput</code>.
+ * @param left the left element.
+ * @param right the right element.
+ */
+ public MergeInput(ITypedElement left, ITypedElement right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#addCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void addCompareInputChangeListener(ICompareInputChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void removeCompareInputChangeListener(ICompareInputChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Fire a compare input change event. This method must be called from the UI
+ * thread.
+ */
+ void fireInputChanged() {
+ if (!listeners.isEmpty()) {
+ Object[] _listeners = listeners.getListeners();
+ for (int i = 0; i < _listeners.length; i++) {
+ final ICompareInputChangeListener listener = (ICompareInputChangeListener) _listeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ listener.compareInputChanged(MergeInput.this);
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Logged by the safe runner
+ }
+ });
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#copy(boolean)
+ */
+ @Override
+ public void copy(boolean leftToRight) {
+ Assert.isTrue(false, "Copy is not support by this type of compare input"); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getAncestor()
+ */
+ @Override
+ public ITypedElement getAncestor() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getImage()
+ */
+ @Override
+ public Image getImage() {
+ ITypedElement element = getMainElement();
+ return element == null ? null : element.getImage();
+ }
+
+ /**
+ * Return the main non-null element that identifies this input. By default,
+ * the fLeft is returned if non-null. If the fLeft is null, the fRight is
+ * returned. If both the fLeft and fRight are null the ancestor is returned.
+ *
+ * @return the main non-null element that identifies this input
+ */
+ private ITypedElement getMainElement() {
+ if (left != null)
+ return left;
+ if (right != null)
+ return right;
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getKind()
+ */
+ @Override
+ public int getKind() {
+ return Differencer.CHANGE;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getLeft()
+ */
+ @Override
+ public ITypedElement getLeft() {
+ return left;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getName()
+ */
+ @Override
+ public String getName() {
+ ITypedElement element = getMainElement();
+ return element == null ? null : element.getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getRight()
+ */
+ @Override
+ public ITypedElement getRight() {
+ return right;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeTypedElement.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeTypedElement.java new file mode 100644 index 000000000..8eaf73b3c --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeTypedElement.java @@ -0,0 +1,100 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.BufferedContent;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+
+/**
+ * A <code>MergeTypedElement</code> wraps an <code>FSTreeNode</code> so that it
+ * can be used as input for the differencing engine (<code>ITypedElement</code>).
+ */
+public abstract class MergeTypedElement extends BufferedContent implements ITypedElement {
+ // The File System tree node to be wrapped.
+ protected FSTreeNode node;
+
+ /**
+ * Create a MergeTypedElement for the given node.
+ *
+ * @param node
+ * The node.
+ */
+ public MergeTypedElement(FSTreeNode node) {
+ this.node = node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getImage()
+ */
+ @Override
+ public Image getImage() {
+ return CompareUI.getImage(getType());
+ }
+
+ /**
+ * Return the tree node wrapped.
+ *
+ * @return The tree node of the file
+ */
+ public FSTreeNode getFSTreeNode() {
+ return node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getType()
+ */
+ @Override
+ public String getType() {
+ if (node.isDirectory())
+ return ITypedElement.FOLDER_TYPE;
+ if (node != null) {
+ String s = node.name;
+ int dot = s.lastIndexOf('.');
+ if (dot != -1)
+ s = s.substring(dot + 1);
+ if (s != null)
+ return s;
+ }
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ITypedElement) {
+ return toString().equals(other.toString());
+ }
+ return super.equals(other);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getName()
+ */
+ @Override
+ public String getName() {
+ return node.name;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ *
+ * Returns the hash code of the name.
+ */
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/RemoteTypedElement.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/RemoteTypedElement.java new file mode 100644 index 000000000..75d543537 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/RemoteTypedElement.java @@ -0,0 +1,104 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+
+/**
+ * A <code>RemoteTypedElement</code> wraps an <code>FSTreeNode</code> so that it
+ * can be used as input for the differencing engine (<code>ITypedElement</code>)
+ * as the right element of the comparison editor.
+ *
+ * @since 3.7
+ */
+public class RemoteTypedElement extends MergeTypedElement {
+ /**
+ * Creates a <code>RemoteTypedElement</code> for the given node.
+ *
+ * @param node
+ * the tree node.
+ */
+ public RemoteTypedElement(FSTreeNode node) {
+ super(node);
+ }
+
+ /**
+ * Return an input stream that opens that remote file to provide the stream
+ * content.
+ *
+ * @return a buffered input stream containing the contents of this file
+ * @exception CoreException
+ * if the contents of this storage could not be accessed
+ */
+ @Override
+ protected InputStream createStream() throws CoreException {
+ try {
+ return node.getLocationURL().openStream();
+ } catch (IOException e) {
+ Status error = new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ throw new CoreException(error);
+ }
+ }
+
+ /**
+ * Download the remote file and save the content so that it is cached for
+ * getContents call.
+ *
+ * @param monitor
+ * The monitor used to display downloading progress.
+ * @throws InvocationTargetException
+ * throws when an exception occurs during downloading.
+ */
+ public void cacheContents(IProgressMonitor monitor)
+ throws InvocationTargetException {
+ monitor.beginTask(
+ NLS.bind(Messages.CacheManager_DowloadingFile, node.name), 100);
+ OutputStream output = null;
+ try {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ output = new BufferedOutputStream(baos);
+ monitor.beginTask(Messages.RemoteTypedElement_GettingRemoteContent
+ + node.name, 100);
+ CacheManager.getInstance().download2OutputStream(node, output, monitor);
+ if (!monitor.isCanceled()) {
+ setContent(baos.toByteArray());
+ }
+ } catch (IOException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ /**
+ * Return the external form of the URL to the remote file of this node. It
+ * is used to compute its hash code and as the title of the comparison
+ * editor.
+ */
+ @Override
+ public String toString() {
+ return node.getLocationURL().toString();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/events/INodeStateListener.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/events/INodeStateListener.java new file mode 100644 index 000000000..9790e841d --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/events/INodeStateListener.java @@ -0,0 +1,26 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.events;
+
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+/**
+ * An INodeStateListener is a listener interface. Classes that implement this
+ * interface serve as a listener processing the event that a node state has changed.
+ *
+ */
+public interface INodeStateListener {
+ /**
+ * Fired when the state of the specified FSTreeNode has changed.
+ *
+ * @param node The FSTreeNode whose state has changed.
+ */
+ void stateChanged(FSTreeNode node);
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFChannelException.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFChannelException.java new file mode 100644 index 000000000..e8e22d6b5 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFChannelException.java @@ -0,0 +1,40 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.exceptions;
+
+/**
+ * TCF channel exception.
+ */
+public class TCFChannelException extends TCFException {
+ private static final long serialVersionUID = 7414816212710485160L;
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFChannelException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFChannelException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFException.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFException.java new file mode 100644 index 000000000..40d5a3523 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFException.java @@ -0,0 +1,40 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.exceptions;
+
+/**
+ * TCF file system implementation base exception.
+ */
+public class TCFException extends Exception {
+ private static final long serialVersionUID = -220092425137980661L;
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFFileSystemException.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFFileSystemException.java new file mode 100644 index 000000000..7b5edac15 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFFileSystemException.java @@ -0,0 +1,41 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.exceptions;
+
+/**
+ * TCF remote file system exception.
+ */
+public class TCFFileSystemException extends TCFException {
+ private static final long serialVersionUID = -5203855887734608373L;
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFFileSystemException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFFileSystemException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java index 2332963d1..9d2869cc9 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java @@ -6,45 +6,68 @@ *
* Contributors:
* Wind River Systems - initial API and implementation
- * William Chen (Wind River)- [345387]Open the remote files with a proper editor
+ * William Chen (Wind River)- [345387] Open the remote files with a proper editor
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
*******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.DecimalFormat;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.internal.preferences.TargetExplorerPreferencePage;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.TcfURLConnection;
import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.PlatformUI;
/**
* The local file system cache used to manage the temporary files downloaded
* from a remote file system.
*/
public class CacheManager {
+
+ // Time stamp file used to persist the time stamps of each file.
+ private static final String TIMESTAMP_FILE = "timestamps.xml"; //$NON-NLS-1$
+
+ // The agent directory's prefixed name.
private static final String WS_AGENT_DIR_PREFIX = "agent_"; //$NON-NLS-1$
- // The singleton instance.
- private static CacheManager instance;
- // The formatter used to format the size displayed while downloading.
- private static DecimalFormat SIZE_FORMAT = new DecimalFormat("#,##0.##"); //$NON-NLS-1$
// The default chunk size of the buffer used during downloading files.
private static final int DEFAULT_CHUNK_SIZE = 5 * 1024;
+ // The formatter used to format the size displayed while downloading.
+ private static final DecimalFormat SIZE_FORMAT = new DecimalFormat("#,##0.##"); //$NON-NLS-1$
+
+ // The singleton instance.
+ private static CacheManager instance;
+
+ // The time stamp for each file.
+ private Map<URL, Long> timestamps;
+
/**
* Get the singleton cache manager.
*
@@ -58,32 +81,133 @@ public class CacheManager { }
/**
+ * Create a cache manager.
+ */
+ private CacheManager() {
+ loadTimestamps();
+ }
+
+ /**
+ * If the option of "autosaving" is set to on.
+ *
+ * @return true if it is auto saving or else false.
+ */
+ public boolean isAutoSaving(){
+ IPreferenceStore preferenceStore = UIPlugin.getDefault().getPreferenceStore();
+ boolean autoSaving = preferenceStore.getBoolean(TargetExplorerPreferencePage.PREF_AUTOSAVING);
+ return autoSaving;
+ }
+
+ /**
+ * Load the time stamps from the time stamps file in the cache's root directory.
+ */
+ private void loadTimestamps() {
+ timestamps = Collections.synchronizedMap(new HashMap<URL, Long>());
+ File location = getCacheRoot();
+ File tsFile = new File(location, TIMESTAMP_FILE);
+ if (tsFile.exists()) {
+ Properties properties = new Properties();
+ InputStream input = null;
+ try {
+ input = new BufferedInputStream(new FileInputStream(tsFile));
+ properties.loadFromXML(input);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ Enumeration<String> keys = (Enumeration<String>) properties.propertyNames();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ String value = properties.getProperty(key);
+ long timestamp = 0L;
+ try {
+ timestamp = Long.parseLong(value);
+ timestamps.put(new URL(key), Long.valueOf(timestamp));
+ } catch (Exception nfe) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Save the time stamps to the time stamps file.
+ */
+ private void saveTimestamps(){
+ Properties properties = new Properties();
+ for(URL key:timestamps.keySet()){
+ Long timestamp = timestamps.get(key);
+ properties.setProperty(key.toString(), timestamp.toString());
+ }
+ File location = getCacheRoot();
+ File fTimestamp = new File(location, TIMESTAMP_FILE);
+ OutputStream output = null;
+ try{
+ output = new BufferedOutputStream(new FileOutputStream(fTimestamp));
+ properties.storeToXML(output, null);
+ }catch(IOException e){
+ e.printStackTrace();
+ }finally{
+ if(output!=null){
+ try{
+ output.close();
+ }catch(Exception e){}
+ }
+ }
+ }
+
+ /**
+ * Set the time stamp of the FSTreeNode with the specified location.
+ * @param url The FSTreeNode's location URL.
+ * @param timestamp The new base time stamp to be set.
+ */
+ public void setBaseTimestamp(URL url, long timestamp){
+ timestamps.put(url, Long.valueOf(timestamp));
+ // Persist as well.
+ saveTimestamps();
+ }
+
+ /**
+ * Remove the time stamp entry with the specified URL.
+ * @param url The URL key.
+ */
+ public void removeBaseTimestamp(URL url){
+ timestamps.remove(url);
+ // Persist
+ saveTimestamps();
+ }
+
+ /**
+ * Get the time stamp of the FSTreeNode with the specified location.
+ *
+ * @param url The FSTreeNode's location URL.
+ * @return The FSTreeNode's base time stamp.
+ */
+ public long getBaseTimestamp(URL url){
+ Long timestamp = timestamps.get(url);
+ return timestamp == null ? 0L : timestamp.longValue();
+ }
+
+ /**
* Get the local path of a node's cached file.
* <p>
* The preferred location is within the plugin's state location, in
* example <code><state location>agent_<hashcode_of_peerId>/remote/path/to/the/file...</code>.
* <p>
- * If the plugin is loaded in a RCP workspace-less environment, the
- * fallback strategy is to use the users home directory.
+ * If the plug-in is loaded in a RCP workspace-less environment, the
+ * fall back strategy is to use the users home directory.
*
* @param node
* The file/folder node.
* @return The local path of the node's cached file.
*/
public IPath getCachePath(FSTreeNode node) {
- File location;
- try {
- location = UIPlugin.getDefault().getStateLocation().toFile();
- }
- catch (IllegalStateException e) {
- // An RCP workspace-less environment (-data @none)
- location = new File(System.getProperty("user.home"), ".tcf"); //$NON-NLS-1$ //$NON-NLS-2$
- location = new File(location, "fs"); //$NON-NLS-1$
- }
-
- // Create the location if it not exist
- if (!location.exists()) location.mkdir();
-
+ File location = getCacheRoot();
String agentId = node.peerNode.getPeer().getID();
// Use Math.abs to avoid negative hash value.
String agent = WS_AGENT_DIR_PREFIX + Math.abs(agentId.hashCode());
@@ -92,11 +216,49 @@ public class CacheManager { if (!agentDirFile.exists()) {
agentDirFile.mkdir();
}
-
return appendNodePath(agentDir, node);
}
/**
+ * Get the local file of the specified node.
+ *
+ * <p>
+ * The preferred location is within the plugin's state location, in
+ * example <code><state location>agent_<hashcode_of_peerId>/remote/path/to/the/file...</code>.
+ * <p>
+ * If the plug-in is loaded in a RCP workspace-less environment, the
+ * fall back strategy is to use the users home directory.
+ *
+ * @param node
+ * The file/folder node.
+ * @return The file object of the node's local cache.
+ */
+ public File getCacheFile(FSTreeNode node){
+ return getCachePath(node).toFile();
+ }
+
+ /**
+ * Get the cache file system's root directory on the local host's
+ * file system.
+ *
+ * @return The root folder's location of the cache file system.
+ */
+ private File getCacheRoot() {
+ File location;
+ try {
+ location = UIPlugin.getDefault().getStateLocation().toFile();
+ }catch (IllegalStateException e) {
+ // An RCP workspace-less environment (-data @none)
+ location = new File(System.getProperty("user.home"), ".tcf"); //$NON-NLS-1$ //$NON-NLS-2$
+ location = new File(location, "fs"); //$NON-NLS-1$
+ }
+
+ // Create the location if it not exist
+ if (!location.exists()) location.mkdir();
+ return location;
+ }
+
+ /**
* Append the path with the specified node's context path.
*
* @param path
@@ -106,7 +268,7 @@ public class CacheManager { * @return The path to the node.
*/
private IPath appendNodePath(IPath path, FSTreeNode node) {
- if (!node.isRoot()) {
+ if (!node.isRoot() && node.parent!=null) {
path = appendNodePath(path, node.parent);
return appendPathSegment(node, path, node.name);
}
@@ -124,7 +286,7 @@ public class CacheManager { * @param node The file/folder node.
* @param path The path to appended.
* @param name The segment's name.
- * @return
+ * @return The path with the segment "name" appended.
*/
private IPath appendPathSegment(FSTreeNode node, IPath path, String name) {
IPath newPath = path.append(name);
@@ -136,75 +298,116 @@ public class CacheManager { }
/**
- * Get the cache file's staleness. If the cache file does not exist or the
- * file is already out-dated, then return true.
- * <p>
- * When a local cache file is created, the modified time is set to that of
- * its remote corresponding file. So if its remote file is changed, the
- * modified time stamp should be different from the local cache file's
- * modified time stamp.
- * <p>
- * See the method "download" for more details.
- * <p>
- *
- * @param node
- * The file/folder node.
- * @return true if the file doesn't exist or it is stale.
- */
- public boolean isCacheStale(FSTreeNode node) {
- IPath path = getCachePath(node);
- File file = path.toFile();
- return !file.exists() || file.lastModified() != node.attr.mtime;
- }
-
- /**
* Download the data of the file from the remote file system.
- *
+ * Must be called within a UI thread.
* @param node
* The file node.
- * @param parent
- * The shell parent used to display messages.
*
* @return true if it is successful, false there're errors or it is
* canceled.
*/
- public boolean download(final FSTreeNode node, Shell parent) {
+ public boolean download(final FSTreeNode node) {
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask(NLS.bind(Messages.CacheManager_DowloadingFile, node.name), 100);
+ OutputStream output = null;
try {
- boolean canceled = downloadFile(node, monitor);
- if (canceled)
+ // Write the data to its local cache file.
+ File file = getCachePath(node).toFile();
+ if(file.exists() && !file.canWrite()){
+ // If the file exists and is read-only, delete it.
+ file.delete();
+ }
+ output = new BufferedOutputStream(new FileOutputStream(file));
+ download2OutputStream(node, output, monitor);
+ if (monitor.isCanceled())
throw new InterruptedException();
} catch (IOException e) {
throw new InvocationTargetException(e);
} finally {
+ if (output != null) {
+ try {
+ output.close();
+ } catch (Exception e) {
+ }
+ }
monitor.done();
}
}
};
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
TimeTriggeredProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(
parent, 250);
dialog.setCancelable(true);
File file = getCachePath(node).toFile();
try {
dialog.run(true, true, runnable);
- // If downloading is successful, set the last modified time to
- // that of its corresponding file.
- file.setLastModified(node.attr.mtime);
+ // If downloading is successful, update the attributes of the file and
+ // set the last modified time to that of its corresponding file.
+ StateManager.getInstance().updateState(node);
// If the node is read-only, make the cache file read-only.
if(!node.isWritable())
file.setReadOnly();
return true;
+ } catch(TCFException e) {
+ MessageDialog.openError(parent, Messages.StateManager_UpdateFailureTitle, e.getLocalizedMessage());
} catch (InvocationTargetException e) {
// Something's gone wrong. Roll back the downloading and display the
// error.
file.delete();
- displayError(node, parent, e);
+ removeBaseTimestamp(node.getLocationURL());
+ displayError(parent, e);
} catch (InterruptedException e) {
// It is canceled. Just roll back the downloading result.
file.delete();
+ removeBaseTimestamp(node.getLocationURL());
+ }
+ return false;
+ }
+
+ /**
+ * Upload the local files to the remote file system.
+ * Must be called within UI thread.
+ * @param nodes
+ * The files' location. Not null.
+ *
+ * @return true if it is successful, false there're errors or it is
+ * canceled.
+ */
+ public boolean upload(final FSTreeNode... nodes) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ try {
+ IRunnableWithProgress runnable = new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ String message;
+ if(nodes.length==1)
+ message = NLS.bind(Messages.CacheManager_UploadSingleFile, nodes[0].name);
+ else
+ message = NLS.bind(Messages.CacheManager_UploadNFiles, Long.valueOf(nodes.length));
+ monitor.beginTask(message, 100);
+ boolean canceled = uploadFiles(monitor, nodes);
+ if (canceled)
+ throw new InterruptedException();
+ } catch (Exception e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+ };
+ TimeTriggeredProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(parent, 250);
+ dialog.setCancelable(true);
+ dialog.run(true, true, runnable);
+ return true;
+ } catch (InvocationTargetException e) {
+ // Something's gone wrong. Roll back the downloading and display the
+ // error.
+ displayError(parent, e);
+ } catch (InterruptedException e) {
+ // It is canceled. Just roll back the downloading result.
}
return false;
}
@@ -219,40 +422,114 @@ public class CacheManager { * @param e
* The error exception.
*/
- private void displayError(FSTreeNode node, Shell parent, InvocationTargetException e) {
+ private void displayError(Shell parent, InvocationTargetException e) {
Throwable target = e.getTargetException();
Throwable cause = target.getCause() != null ? target.getCause() : target;
MessageDialog.openError(parent, Messages.CacheManager_DownloadingError, cause.getLocalizedMessage());
}
/**
- * Download the specified file using the monitor to report the progress.
+ * Upload the specified files using the monitor to report the progress.
+ *
+ * @param peers
+ * The local files' peer files.
+ * @param locals
+ * The local files to be uploaded.
+ * @param monitor
+ * The monitor used to report the progress.
+ * @return true if it is canceled or else false.
+ * @throws Exception
+ * an Exception thrown during downloading and storing data.
+ */
+ public boolean uploadFiles(IProgressMonitor monitor, FSTreeNode... nodes) throws IOException {
+ BufferedInputStream input = null;
+ BufferedOutputStream output = null;
+ // The buffer used to download the file.
+ byte[] data = new byte[DEFAULT_CHUNK_SIZE];
+ // Calculate the total size.
+ long totalSize = 0;
+ for (FSTreeNode node : nodes) {
+ File file = getCachePath(node).toFile();
+ totalSize += file.length();
+ }
+ // Calculate the chunk size of one percent.
+ int chunk_size = (int) totalSize / 100;
+ // The current reading percentage.
+ int percentRead = 0;
+ // The current length of read bytes.
+ long bytesRead = 0;
+ for (int i = 0; i < nodes.length && !monitor.isCanceled(); i++) {
+ File file = getCachePath(nodes[i]).toFile();
+ try {
+ URL url = nodes[i].getLocationURL();
+ TcfURLConnection connection = (TcfURLConnection) url.openConnection();
+ connection.setDoInput(false);
+ connection.setDoOutput(true);
+ input = new BufferedInputStream(new FileInputStream(file));
+ output = new BufferedOutputStream(connection.getOutputStream());
+
+ // Total size displayed on the progress dialog.
+ String fileLength = formatSize(file.length());
+ int length;
+ while ((length = input.read(data)) >= 0 && !monitor.isCanceled()) {
+ output.write(data, 0, length);
+ output.flush();
+ bytesRead += length;
+ if (chunk_size != 0) {
+ int percent = (int) bytesRead / chunk_size;
+ if (percent != percentRead) { // Update the progress.
+ monitor.worked(percent - percentRead);
+ percentRead = percent; // Remember the percentage.
+ // Report the progress.
+ monitor.subTask(NLS.bind(Messages.CacheManager_UploadingProgress, new Object[]{file.getName(), formatSize(bytesRead), fileLength}));
+ }
+ }
+ }
+ } finally {
+ if (output != null) {
+ try {
+ output.close();
+ } catch (Exception e) {
+ }
+ }
+ if (input != null) {
+ try {
+ input.close();
+ } catch (Exception e) {
+ }
+ }
+ if(!monitor.isCanceled()){
+ // Once upload is successful, synchronize the modified time.
+ try {
+ StateManager.getInstance().commitState(nodes[i]);
+ } catch (TCFException tcfe) {
+ throw new IOException(tcfe.getLocalizedMessage());
+ }
+ }
+ }
+ }
+ return monitor.isCanceled();
+ }
+
+ /**
+ * Download the specified file into an output stream using the monitor to report the progress.
*
* @param node
* The file to be downloaded.
+ * @param output
+ * The output stream.
* @param monitor
* The monitor used to report the progress.
- * @return true if it is canceled or else false.
* @throws IOException
* an IOException thrown during downloading and storing data.
*/
- protected boolean downloadFile(FSTreeNode node, IProgressMonitor monitor)
- throws IOException {
- BufferedInputStream input = null;
- BufferedOutputStream output = null;
- try {
- // Open the input stream of the node using the tcf stream protocol.
+ public void download2OutputStream(FSTreeNode node, OutputStream output, IProgressMonitor monitor) throws IOException {
+ InputStream input = null;
+ // Open the input stream of the node using the tcf stream protocol.
+ try{
URL url = node.getLocationURL();
InputStream in = url.openStream();
input = new BufferedInputStream(in);
- // Write the data to its local cache file.
- File file = getCachePath(node).toFile();
- if(file.exists() && !file.canWrite()){
- // If the file exists and is read-only, delete it.
- file.delete();
- }
- output = new BufferedOutputStream(new FileOutputStream(file));
-
// The buffer used to download the file.
byte[] data = new byte[DEFAULT_CHUNK_SIZE];
// Calculate the chunk size of one percent.
@@ -277,14 +554,7 @@ public class CacheManager { }
}
}
- return monitor.isCanceled();
- } finally {
- if (output != null) {
- try {
- output.close();
- } catch (Exception e) {
- }
- }
+ }finally{
if (input != null) {
try {
input.close();
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CachePropertyTester.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CachePropertyTester.java new file mode 100644 index 000000000..1e8d96ebc --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CachePropertyTester.java @@ -0,0 +1,37 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.core.expressions.PropertyTester;
+
+/**
+ * Provide a tester to test if the current auto saving mode is on or off.
+ *
+ */
+public class CachePropertyTester extends PropertyTester {
+ /**
+ * Create a cache property tester.
+ */
+ public CachePropertyTester() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.expressions.IPropertyTester#test(java.lang.Object, java.lang.String, java.lang.Object[], java.lang.Object)
+ */
+ @Override
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ if(property.equals("isAutoSavingOn")){ //$NON-NLS-1$
+ return CacheManager.getInstance().isAutoSaving();
+ }
+ return false;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CommitHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CommitHandler.java new file mode 100644 index 000000000..6ece7624c --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CommitHandler.java @@ -0,0 +1,90 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import java.io.File;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler that commits the content of a modified file to the target file system.
+ */
+public class CommitHandler extends AbstractHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ try {
+ // Refresh the node to get the latest state.
+ StateManager.getInstance().refreshState(node);
+ Shell parent = HandlerUtil.getActiveShell(event);
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if (file.exists()) {
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case conflict:
+ String title = Messages.CmmitHandler_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.CmmitHandler_StateChangedMessage, node.name);
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message, MessageDialog.QUESTION, new String[] { Messages.CmmitHandler_Merge, Messages.CmmitHandler_CommitAnyway,
+ Messages.CmmitHandler_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) {// Merge
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ IWorkbenchPage page = HandlerUtil.getActiveSite(event).getPage();
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ } else if (index == 1) { // Commit anyway
+ CacheManager.getInstance().upload(node);
+ }
+ break;
+ case modified:
+ // Commit it normally.
+ CacheManager.getInstance().upload(node);
+ break;
+ case consistent:
+ break;
+ case outdated:
+ break;
+ }
+ } else {
+ String message = NLS.bind(Messages.CmmitHandler_FileDeleted, file.getName());
+ MessageDialog.openError(parent, Messages.CmmitHandler_ErrorTitle, message);
+ }
+ } catch (TCFException e) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ }
+ return null;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java index 0ae5dcf57..a746b982e 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java @@ -9,9 +9,9 @@ *******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@@ -38,10 +38,10 @@ public class ContentTypeHelper { private Map<URL, IContentType> resolvables = new HashMap<URL, IContentType>();
// Already known unresolvable file nodes specified by their URLs.
private Map<URL, FSTreeNode> unresolvables = new HashMap<URL, FSTreeNode>();
-
+
/**
* Get the singleton instance of the content type helper.
- *
+ *
* @return The singleton instance of the content type helper.
*/
public static ContentTypeHelper getInstance() {
@@ -53,7 +53,7 @@ public class ContentTypeHelper { /**
* Judges if the node is a binary file.
- *
+ *
* @param node
* The file node.
* @return true if the node is a binary file or else false.
@@ -71,24 +71,19 @@ public class ContentTypeHelper { /**
* Get the content type of the specified file node.
- *
+ *
* @param node
* The file node.
* @return The content type of the file node.
*/
public IContentType getContentType(FSTreeNode node) {
- URL location = null;
- try {
- location = node.getLocationURL();
- } catch (MalformedURLException e1) {
- return null;
- }
+ URL location = node.getLocationURL();
if (unresolvables.get(location) != null)
// If it is already known unresolvable.
return null;
IContentType contentType = null;
contentType = resolvables.get(location);
- if (contentType != null)
+ if (contentType != null)
// If it is already known to have a certain content type.
return contentType;
// First check the content type by its name.
@@ -110,7 +105,7 @@ public class ContentTypeHelper { /**
* Find the content type of the file using its content stream.
- *
+ *
* @param node
* The file node.
* @return The content type of the file.
@@ -119,13 +114,12 @@ public class ContentTypeHelper { * @throws IOException
* If something goes wrong during the content type parsing.
*/
- private IContentType findContentTypeByStream(FSTreeNode node)
- throws CoreException, IOException {
+ private IContentType findContentTypeByStream(FSTreeNode node) throws CoreException, IOException {
InputStream is = null;
try {
- if (!CacheManager.getInstance().isCacheStale(node)) {
- // If the local cache file is update to date, then use the local
- // cache file.
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if (file.exists()) {
+ // If the local cache file exits.
IPath path = CacheManager.getInstance().getCachePath(node);
IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
is = fileStore.openInputStream(EFS.NONE, null);
@@ -134,8 +128,7 @@ public class ContentTypeHelper { URL url = node.getLocationURL();
is = url.openStream();
}
- return Platform.getContentTypeManager().findContentTypeFor(is,
- node.name);
+ return Platform.getContentTypeManager().findContentTypeFor(is, node.name);
} finally {
if (is != null) {
try {
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java index 0e23f0a1b..4e7ada41c 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java @@ -12,14 +12,18 @@ *******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+import java.io.File;
+
import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
/**
* The property tester of an FSTreeNode. The properties include "isFile"
* if it is a file node, "isDirectory" if it is a directory, "isBinaryFile"
* if it is a binary file, "isReadable" if it is readable, "isWritable" if
- * it is writable and "isExecutable" if it is executable.
+ * it is writable, "isExecutable" if it is executable and "getCacheState" to
+ * get a node's state.
*/
public class FSTreeNodePropertyTester extends PropertyTester {
@@ -42,6 +46,12 @@ public class FSTreeNodePropertyTester extends PropertyTester { return node.isWritable();
} else if (property.equals("isExecutable")){ //$NON-NLS-1$
return node.isExecutable();
+ } else if (property.equals("getCacheState")){ //$NON-NLS-1$
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if(!file.exists())
+ return false;
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ return state.name().equals(expectedValue);
}
return false;
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/MergeHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/MergeHandler.java new file mode 100644 index 000000000..08d3b5cb6 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/MergeHandler.java @@ -0,0 +1,46 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler used to merge a file which is conflicting with its remote file.
+ */
+public class MergeHandler extends AbstractHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ return null;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java index 00214c396..b92ee3f39 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java @@ -9,6 +9,9 @@ *******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+import java.io.File;
+
+import org.eclipse.compare.CompareUI;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
@@ -17,9 +20,15 @@ import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
@@ -36,13 +45,14 @@ public class OpenFileHandler extends AbstractHandler { */
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getActiveMenuSelectionChecked(event);
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
final FSTreeNode node = (FSTreeNode) selection.getFirstElement();
IWorkbenchPage page = HandlerUtil.getActiveSite(event).getPage();
if (ContentTypeHelper.getInstance().isBinaryFile(node)) {
// If the file is a binary file.
Shell parent = HandlerUtil.getActiveShell(event);
- MessageDialog.openWarning(parent, Messages.OpenFileHandler_Warning, Messages.OpenFileHandler_OpeningBinaryNotSupported);
+ MessageDialog.openWarning(parent, Messages.OpenFileHandler_Warning,
+ Messages.OpenFileHandler_OpeningBinaryNotSupported);
} else {
// Open the file node.
openFile(node, page);
@@ -61,13 +71,63 @@ public class OpenFileHandler extends AbstractHandler { * The workbench page in which the editor is opened.
*/
private void openFile(FSTreeNode node, IWorkbenchPage page) {
- if (CacheManager.getInstance().isCacheStale(node)) {
- // If the file node's local cache is already stale, download it.
- Shell parent = page.getWorkbenchWindow().getShell();
- boolean successful = CacheManager.getInstance().download(node, parent);
- if (successful) openEditor(page, node);
- } else {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if (!file.exists()) {
+ // If the file node's local cache does not exist yet, download it.
+ boolean successful = CacheManager.getInstance().download(node);
+ if (!successful) {
+ return;
+ }
+ }
+ if (!CacheManager.getInstance().isAutoSaving()) {
openEditor(page, node);
+ } else {
+ try {
+ StateManager.getInstance().refreshState(node);
+ } catch (TCFException e) {
+ Shell parent = page.getWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ return;
+ }
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case consistent:
+ openEditor(page, node);
+ break;
+ case modified: {
+ // If the file node's local cache has been modified, upload it
+ // before open it.
+ boolean successful = CacheManager.getInstance().upload(node);
+ if (successful)
+ openEditor(page, node);
+ }
+ break;
+ case outdated: {
+ // If the file node's local cache does not exist yet, download
+ // it.
+ boolean successful = CacheManager.getInstance().download(node);
+ if (successful)
+ openEditor(page, node);
+ }
+ break;
+ case conflict: {
+ String title = Messages.OpenFileHandler_ConflictingTitle;
+ String message = NLS.bind(Messages.OpenFileHandler_ConflictingMessage, node.name);
+ Shell parent = page.getWorkbenchWindow().getShell();
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message, MessageDialog.QUESTION, new String[] { Messages.OpenFileHandler_Merge, Messages.OpenFileHandler_OpenAnyway,
+ Messages.OpenFileHandler_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) {
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ } else if (index == 1) {
+ openEditor(page, node);
+ }
+ }
+ break;
+ }
}
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RefreshHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RefreshHandler.java new file mode 100644 index 000000000..bb7ec71f7 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RefreshHandler.java @@ -0,0 +1,45 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler to refresh the state of a file.
+ */
+public class RefreshHandler extends AbstractHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ try {
+ StateManager.getInstance().refreshState(node);
+ } catch (TCFException e) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ }
+ return null;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RevertHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RevertHandler.java new file mode 100644 index 000000000..83e27c185 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RevertHandler.java @@ -0,0 +1,38 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler to revert the content of the file from the remote file.
+ * This handler is enabled only when the file is modified or conflicting.
+ */
+public class RevertHandler extends AbstractHandler {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ CacheManager.getInstance().download(node);
+ return null;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/StateManager.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/StateManager.java new file mode 100644 index 000000000..e43b4c7fd --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/StateManager.java @@ -0,0 +1,259 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import java.io.File;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneSetStat;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneStat;
+import org.eclipse.tm.tcf.services.IFileSystem.FileAttrs;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.Rendezvous;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.TCFUtilities;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+
+/**
+ * This class provides several utility methods to get, update, commit
+ * or refresh a file node's state.
+ *
+ */
+public class StateManager {
+
+ // The singleton instance.
+ private static StateManager instance;
+
+ /**
+ * Get the singleton user manager.
+ *
+ * @return The singleton cache manager.
+ */
+ public static StateManager getInstance() {
+ if (instance == null) {
+ instance = new StateManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Create a StateManager fInstance.
+ */
+ private StateManager() {
+ }
+
+ /**
+ * Update the state of the specified node.
+ *
+ * @param node The tree node whose state is going to be updated.
+ * @throws TCFException
+ */
+ public void updateState(FSTreeNode node) throws TCFException {
+ updateFileStat(node, true);
+ }
+
+ /**
+ * Refresh the state of the specified node.
+ *
+ * @param node The tree node whose state is going to be refreshed.
+ * @throws TCFException
+ */
+ public void refreshState(FSTreeNode node) throws TCFException {
+ updateFileStat(node, false);
+ }
+
+ /**
+ * Update the file's state of the specified tree node. Synchronize the time stamp of
+ * the file with the base time stamp and that of the remote file if sync is true.
+ *
+ * @param node The tree node whose file state is going to be updated.
+ * @param sync If its base time stamp and its remote file's time stamp should be synchronized.
+ */
+ private void updateFileStat(final FSTreeNode node, final boolean sync) throws TCFException {
+ IChannel channel = null;
+ try {
+ channel = TCFUtilities.openChannel(node.peerNode.getPeer());
+ if (channel != null) {
+ updateFileAttr(node, sync, channel);
+ }
+ } finally {
+ if (channel != null)
+ channel.close();
+ }
+ }
+
+ private void updateFileAttr(final FSTreeNode node, final boolean sync, IChannel channel) throws TCFFileSystemException {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final Rendezvous rendezvous = new Rendezvous();
+ String path = node.getLocation(true);
+ service.stat(path, new DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) {
+ if (error == null) {
+ updateNodeAttr(node, attrs, sync);
+ } else {
+ String message = NLS.bind(Messages.StateManager_CannotGetFileStatMessage, new Object[]{node.name, error});
+ errors[0] = new TCFFileSystemException(message, error);
+ }
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.StateManager_CannotGetFileStateMessage2, new Object[]{node.name, e});
+ errors[0] = new TCFFileSystemException(message, e);
+ }
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }else{
+ String message = NLS.bind(Messages.StateManager_TCFNotProvideFSMessage, node.peerNode.getPeer().getID());
+ throw new TCFFileSystemException(message);
+ }
+ }
+
+ /**
+ * Update the file attribute of the specified tree node with the specified value
+ * and synchronize its base timestamp and its remote file's timestamp if "sync" is true.
+ *
+ * @param node The tree node whose file attribute is to updated.
+ * @param attr The new file attribute.
+ * @param sync If the timestamps should be synchronized.
+ */
+ void updateNodeAttr(FSTreeNode node, FileAttrs attr, boolean sync){
+ node.attr = attr;
+ if (sync) {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ assert file.exists();
+ file.setLastModified(attr.mtime);
+ CacheManager.getInstance().setBaseTimestamp(node.getLocationURL(), attr.mtime);
+ }
+ FSModel.getInstance().fireNodeStateChanged(node);
+ }
+
+ /**
+ * Commit the content of the local file to the target.
+ *
+ * @param node The tree node whose local file is going to committed.
+ * @throws TCFException
+ */
+ public void commitState(final FSTreeNode node) throws TCFException {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ assert file.exists();
+ long mtime = file.lastModified();
+ // Create the new file attribute based on the file's last modified time.
+ final IFileSystem.FileAttrs attrs = new IFileSystem.FileAttrs(node.attr.flags, node.attr.size, node.attr.uid, node.attr.gid, node.attr.permissions, node.attr.atime, mtime,
+ node.attr.attributes);
+ IChannel channel = null;
+ try {
+ channel = TCFUtilities.openChannel(node.peerNode.getPeer());
+ if (channel != null) {
+ commitFileAttr(node, attrs, channel);
+ }
+ } finally {
+ if (channel != null)
+ channel.close();
+ }
+ }
+
+ private void commitFileAttr(final FSTreeNode node, final IFileSystem.FileAttrs attrs, IChannel channel) throws TCFFileSystemException {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final Rendezvous rendezvous = new Rendezvous();
+ String path = node.getLocation(true);
+ service.setstat(path, attrs, new DoneSetStat() {
+ @Override
+ public void doneSetStat(IToken token, FileSystemException error) {
+ if (error == null) {
+ commitNodeAttr(node, attrs);
+ } else {
+ String message = NLS.bind(Messages.StateManager_CannotSetFileStateMessage, new Object[] { node.name, error });
+ errors[0] = new TCFFileSystemException(message, error);
+ }
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.StateManager_CannotSetFileStateMessage2, new Object[] { node.name, e });
+ errors[0] = new TCFFileSystemException(message, e);
+ }
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ } else {
+ String message = NLS.bind(Messages.StateManager_TCFNotProvideFSMessage2, node.peerNode.getPeer().getID());
+ throw new TCFFileSystemException(message);
+ }
+ }
+
+ /**
+ * Commit the file attribute of the specified tree node with the specified value.
+ *
+ * @param node The tree node whose file attribute is to committed.
+ * @param attr The new file attribute.
+ */
+ void commitNodeAttr(FSTreeNode node, FileAttrs attr){
+ node.attr = attr;
+ CacheManager.getInstance().setBaseTimestamp(node.getLocationURL(), attr.mtime);
+ FSModel.getInstance().fireNodeStateChanged(node);
+ }
+
+ /**
+ * Get the local file's state of the specified tree node. The local file must exist
+ * before calling this method to get its state.
+ *
+ * @param node The tree node whose local file state is going to retrieved.
+ * @return The tree node's latest cache state.
+ */
+ public CacheState getCacheState(FSTreeNode node) {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if(!file.exists())
+ return CacheState.consistent;
+ long ltime = file.lastModified();
+ long btime = CacheManager.getInstance().getBaseTimestamp(node.getLocationURL());
+ long mtime = 0;
+ if(node.attr!=null)
+ mtime = node.attr.mtime;
+ if(btime == ltime){
+ if(isUnchanged(mtime, btime))
+ return CacheState.consistent;
+ return CacheState.outdated;
+ }
+ if(isUnchanged(mtime, btime))
+ return CacheState.modified;
+ return CacheState.conflict;
+ }
+
+ /**
+ * Compare the modified time of the remote file and the base timestamp
+ * and see if they are equal to each other.
+ *
+ * @param mtime The modified time of the remote file.
+ * @param btime The base timestamp cached.
+ * @return true if they are equal in second precision.
+ */
+ private boolean isUnchanged(long mtime, long btime){
+ return Math.abs(mtime-btime)/1000 == 0;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UpdateHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UpdateHandler.java new file mode 100644 index 000000000..56f367a19 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UpdateHandler.java @@ -0,0 +1,89 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import java.io.File;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler to update the local file's content with the latest of its remote file.
+ *
+ */
+public class UpdateHandler extends AbstractHandler {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ try {
+ StateManager.getInstance().refreshState(node);
+ } catch (TCFException e) {
+ Shell parent = HandlerUtil.getActiveShell(event);
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ return null;
+ }
+ Shell parent = HandlerUtil.getActiveShell(event);
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if(file.exists()){
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case conflict:
+ String title = Messages.UpdateHandler_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.UpdateHandler_StateChangedMessage, node.name);
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message,
+ MessageDialog.QUESTION, new String[]{Messages.UpdateHandler_Merge,
+ Messages.UpdateHandler_UpdateAnyway, Messages.UpdateHandler_Cancel}, 0);
+ int index = msgDialog.open();
+ if (index == 0) {
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ IWorkbenchPage page = HandlerUtil.getActiveSite(event).getPage();
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ }else if(index == 1){
+ CacheManager.getInstance().download(node);
+ }
+ break;
+ case modified:
+ break;
+ case consistent:
+ break;
+ case outdated:
+ CacheManager.getInstance().download(node);
+ break;
+ }
+ }else{
+ CacheManager.getInstance().download(node);
+ }
+ return null;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UserManager.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UserManager.java new file mode 100644 index 000000000..826f1b20a --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UserManager.java @@ -0,0 +1,167 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneUser;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.UserAccount;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.Rendezvous;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.TCFUtilities;
+import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * A facility class to retrieve the user's information for a target file system.
+ */
+public class UserManager {
+ // The key to save and retrieve the user account in a peer model.
+ private static final String USER_ACCOUNT_KEY = "user.account"; //$NON-NLS-1$
+
+ // The singleton fInstance.
+ private static UserManager instance;
+
+ /**
+ * Get the singleton user manager.
+ *
+ * @return The singleton cache manager.
+ */
+ public static UserManager getInstance() {
+ if (instance == null) {
+ instance = new UserManager();
+ }
+ return instance;
+ }
+
+ private UserManager() {
+ }
+
+ /**
+ * Get the user account from the peer using the channel connected to the
+ * remote target.
+ *
+ * @param channel
+ * The channel connected to the remote target.
+ * @return The user account information or null if it fails.
+ */
+ private UserAccount getUserByChannel(final IChannel channel) throws TCFFileSystemException {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final Rendezvous rendezvous = new Rendezvous();
+ final UserAccount[] accounts = new UserAccount[1];
+ service.user(new DoneUser() {
+ @Override
+ public void doneUser(IToken token, FileSystemException error, int real_uid, int effective_uid, int real_gid, int effective_gid, String home) {
+ if (error == null) {
+ accounts[0] = new UserAccount(real_uid, real_gid, effective_uid, effective_gid, home);
+ }else {
+ String message = NLS.bind(Messages.UserManager_CannotGetUserAccountMessage, channel.getRemotePeer().getID());
+ errors[0] = new TCFFileSystemException(message, error);
+ }
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.UserManager_CannotGetUserAccountMessage2, channel.getRemotePeer().getID());
+ errors[0] = new TCFFileSystemException(message, e);
+ }
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ return accounts[0];
+ }
+ String message = NLS.bind(Messages.UserManager_TCFNotProvideFSMessage, channel.getRemotePeer().getID());
+ throw new TCFFileSystemException(message);
+ }
+
+ /**
+ * Get the information of the client user account.
+ *
+ * @return The client user account's information.
+ */
+ public UserAccount getUserAccount(IPeerModel peerNode) {
+ UserAccount account = getUserFromPeer(peerNode);
+ if (account == null) {
+ IChannel channel = null;
+ try{
+ channel = TCFUtilities.openChannel(peerNode.getPeer());
+ if (channel != null) {
+ account = getUserByChannel(channel);
+ if (account != null)
+ setUserToPeer(peerNode, account);
+ }
+ }catch(TCFException e){
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.UserManager_UserAccountTitle, e.getLocalizedMessage());
+ }finally{
+ if(channel!=null){
+ channel.close();
+ }
+ }
+ }
+ return account;
+ }
+
+ /**
+ * Get the user account stored in the specified peer model using a key named
+ * "user.account" defined by the constant USER_ACCOUNT_KEY.
+ *
+ * @param peer
+ * The peer model from which the user account is retrieved.
+ * @return The user account if it exists or null if not.
+ */
+ private UserAccount getUserFromPeer(final IPeerModel peer) {
+ if (Protocol.isDispatchThread()) {
+ return (UserAccount) peer.getProperty(USER_ACCOUNT_KEY);
+ }
+ final UserAccount[] accounts = new UserAccount[1];
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ accounts[0] = (UserAccount) peer.getProperty(USER_ACCOUNT_KEY);
+ }
+ });
+ return accounts[0];
+ }
+
+ /**
+ * Save the user account to the specified peer model using a key named
+ * "user.account" defined by the constant USER_ACCOUNT_KEY.
+ *
+ * @param peer
+ * The peer model to which the user account is saved.
+ */
+ private void setUserToPeer(final IPeerModel peer, final UserAccount account) {
+ assert peer != null && account != null;
+ if (Protocol.isDispatchThread()) {
+ peer.setProperty(USER_ACCOUNT_KEY, account);
+ } else {
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ peer.setProperty(USER_ACCOUNT_KEY, account);
+ }
+ });
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java index c6ba5634a..787822b58 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java @@ -103,6 +103,16 @@ public class Messages extends NLS { public static String CacheManager_DownloadingProgress; public static String CacheManager_KBs; public static String CacheManager_MBs; + public static String CacheManager_UploadingProgress; + public static String CacheManager_UploadNFiles; + public static String CacheManager_UploadSingleFile; + public static String CmmitHandler_Cancel; + public static String CmmitHandler_CommitAnyway; + public static String CmmitHandler_ErrorTitle; + public static String CmmitHandler_FileDeleted; + public static String CmmitHandler_Merge; + public static String CmmitHandler_StateChangedDialogTitle; + public static String CmmitHandler_StateChangedMessage; public static String InformationPage_Accessed; public static String InformationPage_Advanced; public static String InformationPage_Attributes; @@ -124,7 +134,13 @@ public class Messages extends NLS { public static String FSTreeControl_column_modified_label; public static String FSOpenFileDialog_title; + public static String LocalTypedElement_SavingFile; + public static String OpenFileHandler_Cancel; + public static String OpenFileHandler_ConflictingMessage; + public static String OpenFileHandler_ConflictingTitle; + public static String OpenFileHandler_Merge; + public static String OpenFileHandler_OpenAnyway; public static String OpenFileHandler_OpeningBinaryNotSupported; public static String OpenFileHandler_Warning; public static String PermissionsGroup_Executable; @@ -133,6 +149,26 @@ public class Messages extends NLS { public static String PermissionsGroup_Readable; public static String PermissionsGroup_UserPermissions; public static String PermissionsGroup_Writable; + public static String RemoteTypedElement_GettingRemoteContent; + public static String SaveAllListener_Cancel; + public static String SaveAllListener_Merge; + public static String SaveAllListener_SaveAnyway; + public static String SaveAllListener_SingularMessage; + public static String SaveAllListener_StateChangedDialogTitle; + public static String SaveListener_Cancel; + public static String SaveListener_Merge; + public static String SaveListener_SaveAnyway; + public static String SaveListener_StateChangedDialogTitle; + public static String SaveListener_StateChangedMessage; + public static String StateManager_CannotGetFileStateMessage2; + public static String StateManager_CannotGetFileStatMessage; + public static String StateManager_CannotSetFileStateMessage; + public static String StateManager_CannotSetFileStateMessage2; + public static String StateManager_CommitFailureTitle; + public static String StateManager_RefreshFailureTitle; + public static String StateManager_TCFNotProvideFSMessage; + public static String StateManager_TCFNotProvideFSMessage2; + public static String StateManager_UpdateFailureTitle; public static String TcfInputStream_CloseTimeout; public static String TcfInputStream_NoDataAvailable; public static String TcfInputStream_NoFileReturned; @@ -141,4 +177,23 @@ public class Messages extends NLS { public static String TcfInputStream_OpenTCFTimeout; public static String TcfInputStream_ReadTimeout; public static String TcfInputStream_StreamClosed; + public static String TcfOutputStream_StreamClosed; + public static String TcfOutputStream_WriteTimeout; + public static String TcfURLConnection_CloseFileTimeout; + public static String TcfURLConnection_NoFileHandleReturned; + public static String TcfURLConnection_NoFSServiceAvailable; + public static String TcfURLConnection_NoSuchTcfAgent; + public static String TcfURLConnection_OpenFileTimeout; + public static String TcfURLConnection_OpenTCFChannelTimeout; + public static String TCFUtilities_OpeningFailureMessage; + public static String TCFUtilities_OpeningFailureTitle; + public static String UpdateHandler_Cancel; + public static String UpdateHandler_Merge; + public static String UpdateHandler_StateChangedDialogTitle; + public static String UpdateHandler_StateChangedMessage; + public static String UpdateHandler_UpdateAnyway; + public static String UserManager_CannotGetUserAccountMessage; + public static String UserManager_CannotGetUserAccountMessage2; + public static String UserManager_TCFNotProvideFSMessage; + public static String UserManager_UserAccountTitle; } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties index b5d362d6c..08e539479 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties @@ -57,6 +57,22 @@ CacheManager_DownloadingError=Downloading Error CacheManager_DownloadingProgress=Downloading {0}/{1}. CacheManager_KBs=\ KBs CacheManager_MBs=\ MBs +CacheManager_UploadingProgress=Uploading file {0}: {1}/{2} +CacheManager_UploadNFiles=Uploading {0} files... +CacheManager_UploadSingleFile=Uploading file {0}... +CmmitHandler_Cancel=Cancel +CmmitHandler_CommitAnyway=Commit anyway +CmmitHandler_ErrorTitle=File Deleted +CmmitHandler_FileDeleted=The local file {0} that you are trying to commit has been deleted\! +CmmitHandler_Merge=Merge +CmmitHandler_StateChangedDialogTitle=State Changed +CmmitHandler_StateChangedMessage={0} on the target has changed and is conflicting with the local file. What do you want? +LocalTypedElement_SavingFile=Saving file: +OpenFileHandler_Cancel=Cancel +OpenFileHandler_ConflictingMessage=The local {0} is conflicting with the remote version. What do you want? +OpenFileHandler_ConflictingTitle=Conflicting content +OpenFileHandler_Merge=Merge +OpenFileHandler_OpenAnyway=Open anyway OpenFileHandler_OpeningBinaryNotSupported=Opening a binary file is not supported yet. OpenFileHandler_Warning=Warning @@ -66,6 +82,26 @@ PermissionsGroup_OtherPermissions=Other: PermissionsGroup_Readable=Readable PermissionsGroup_UserPermissions=User: PermissionsGroup_Writable=Writable +RemoteTypedElement_GettingRemoteContent=Getting content from the remote file: +SaveAllListener_Cancel=Cancel +SaveAllListener_Merge=Merge +SaveAllListener_SaveAnyway=Save anyway +SaveAllListener_SingularMessage=The file {0} on the target has changed and is conflicting with the local file. What do you want? +SaveAllListener_StateChangedDialogTitle=State Changed +SaveListener_Cancel=Cancel +SaveListener_Merge=Merge +SaveListener_SaveAnyway=Save anyway +SaveListener_StateChangedDialogTitle=State Changed +SaveListener_StateChangedMessage={0} on the target has changed and is conflicting with the local file. What do you want? +StateManager_CannotGetFileStateMessage2=Cannot get the file's stat of {0}\!. Caused by {1} +StateManager_CannotGetFileStatMessage=Cannot get the file's stat of {0}\!. Caused by {1} +StateManager_CannotSetFileStateMessage=Cannot set the file's stat of {0}\!. Caused by {1} +StateManager_CannotSetFileStateMessage2=Cannot set the file's stat of {0}\!. Caused by {1} +StateManager_CommitFailureTitle=Commit Failure +StateManager_RefreshFailureTitle=Refresh Failure +StateManager_TCFNotProvideFSMessage=This TCF agent, {0}, does not provide a file system service\! +StateManager_TCFNotProvideFSMessage2=This TCF agent, {0}, does not provide a file system service\! +StateManager_UpdateFailureTitle=Update Failure TcfInputStream_CloseTimeout=Closing has timed out\! TcfInputStream_NoDataAvailable=No data available TcfInputStream_NoFileReturned=No file handle returned\! @@ -74,3 +110,22 @@ TcfInputStream_OpenFileTimeout=Opening file has timed out\! TcfInputStream_OpenTCFTimeout=Opening TCF channel has timed out\! TcfInputStream_ReadTimeout=Reading has timed out\! TcfInputStream_StreamClosed=Stream is already closed\! +TcfOutputStream_StreamClosed=Stream is already closed\! +TcfOutputStream_WriteTimeout=Writing has timed out\! +TcfURLConnection_CloseFileTimeout=Closing has timed out\! +TcfURLConnection_NoFileHandleReturned=No file handle returned\! +TcfURLConnection_NoFSServiceAvailable=No remote File System Service available\! +TcfURLConnection_NoSuchTcfAgent=TCF agent is already disconnected\! +TcfURLConnection_OpenFileTimeout=Opening file has timed out\! +TcfURLConnection_OpenTCFChannelTimeout=Opening TCF channel has timed out\! +TCFUtilities_OpeningFailureMessage=We cannot open a TCF channel to the target: {0}. It is caused by {1}. +TCFUtilities_OpeningFailureTitle=Opening Channel +UpdateHandler_Cancel=Cancel +UpdateHandler_Merge=Merge +UpdateHandler_StateChangedDialogTitle=State Changed +UpdateHandler_StateChangedMessage=The local {0} has changed and is conflicting with the remote file. What do you want? +UpdateHandler_UpdateAnyway=Update anyway +UserManager_CannotGetUserAccountMessage=Cannot get the user account from the agent {0} +UserManager_CannotGetUserAccountMessage2=Cannot get the user account from the agent {0}. Caused by: networking too slow. +UserManager_TCFNotProvideFSMessage=This TCF agent, {0}, does not provide a file system service\! +UserManager_UserAccountTitle=User Account diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java index c8e247ca0..134750f39 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java @@ -12,6 +12,7 @@ package org.eclipse.tm.te.tcf.filesystem.internal.preferences; import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin; import org.eclipse.tm.te.tcf.filesystem.interfaces.preferences.IPreferenceKeys; @@ -39,5 +40,7 @@ public class PreferencesInitializer extends AbstractPreferenceInitializer { // [Hidden] Editor content contribution: default on prefs.putBoolean(IPreferenceKeys.PREF_FEATURE_ENABLE_EDITOR_CONTENT_CONTRIBUTION, true); } + IPreferenceStore preferenceStore = UIPlugin.getDefault().getPreferenceStore(); + preferenceStore.setDefault(TargetExplorerPreferencePage.PREF_AUTOSAVING, TargetExplorerPreferencePage.DEFAULT_AUTOSAVING); } } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/TargetExplorerPreferencePage.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/TargetExplorerPreferencePage.java new file mode 100644 index 000000000..a00d965f3 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/TargetExplorerPreferencePage.java @@ -0,0 +1,61 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.preferences;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+/**
+ * The preference page for configuring the preference options for Target
+ * Explorer File System Explorer.
+ *
+ */
+public class TargetExplorerPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+ // The preference key to access the option of auto saving
+ public static final String PREF_AUTOSAVING = "PrefAutoSaving"; //$NON-NLS-1$
+ // The default value of the option of auto saving.
+ public static final boolean DEFAULT_AUTOSAVING = true;
+
+ // The editor to edit the value of auto saving.
+ protected BooleanFieldEditor fAutoSaving;
+
+ /***
+ * Create a preference page for Target Explorer File System Explorer.
+ */
+ public TargetExplorerPreferencePage() {
+ super(GRID);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.FieldEditorPreferencePage#createFieldEditors()
+ */
+ @Override
+ protected void createFieldEditors() {
+ UIPlugin plugin = UIPlugin.getDefault();
+ IPreferenceStore preferenceStore = plugin.getPreferenceStore();
+ setPreferenceStore(preferenceStore);
+ fAutoSaving = new BooleanFieldEditor(PREF_AUTOSAVING, "Automatically upload files to targets upon saving.", //$NON-NLS-1$
+ getFieldEditorParent());
+ addField(fAutoSaving);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
+ */
+ @Override
+ public void init(IWorkbench workbench) {
+ // do nothing
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TCFUtilities.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TCFUtilities.java new file mode 100644 index 000000000..b27f3ed02 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TCFUtilities.java @@ -0,0 +1,67 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.url;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IChannel.IChannelListener;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFChannelException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+
+/**
+ * A utilities class that provides common TCF operations.
+ */
+public class TCFUtilities {
+
+ /**
+ * Open a channel connected to the target represented by the peer.
+ *
+ * @return The channel or null if the operation fails.
+ */
+ public static IChannel openChannel(final IPeer peer) throws TCFChannelException {
+ final Rendezvous rendezvous = new Rendezvous();
+ final TCFChannelException[] errors = new TCFChannelException[1];
+ final IChannel[] channels = new IChannel[1];
+ channels[0] = peer.openChannel();
+ channels[0].addChannelListener(new IChannelListener() {
+ @Override
+ public void onChannelOpened() {
+ rendezvous.arrive();
+ }
+
+ @Override
+ public void onChannelClosed(Throwable error) {
+ if (error != null) {
+ String message = NLS.bind(Messages.TCFUtilities_OpeningFailureMessage,
+ new Object[]{peer.getID(), error.getLocalizedMessage()});
+ errors[0] = new TCFChannelException(message, error);
+ rendezvous.arrive();
+ }
+ }
+
+ @Override
+ public void congestionLevel(int level) {
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.TCFUtilities_OpeningFailureMessage,
+ new Object[]{peer.getID(), e.getLocalizedMessage()});
+ errors[0] = new TCFChannelException(message, e);
+ }
+ if(errors[0] != null){
+ throw errors[0];
+ }
+ return channels[0];
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java index f446ef585..6e41bb15f 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java @@ -12,18 +12,10 @@ package org.eclipse.tm.te.tcf.filesystem.internal.url; import java.io.IOException; import java.io.InputStream; -import org.eclipse.core.runtime.Assert; -import org.eclipse.tm.tcf.protocol.IChannel; -import org.eclipse.tm.tcf.protocol.IChannel.IChannelListener; -import org.eclipse.tm.tcf.protocol.IPeer; import org.eclipse.tm.tcf.protocol.IToken; -import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.tm.tcf.services.IFileSystem; -import org.eclipse.tm.tcf.services.IFileSystem.DoneClose; -import org.eclipse.tm.tcf.services.IFileSystem.DoneOpen; import org.eclipse.tm.tcf.services.IFileSystem.DoneRead; import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException; -import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle; import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages; /** @@ -34,21 +26,6 @@ public class TcfInputStream extends InputStream { // Default chunk size while pumping the data. private static final int DEFAULT_CHUNK_SIZE = 5 * 1024; - // If the stream has already connected. - private boolean connected; - - // The TCF agent peer. - IPeer peer; - // The remote path to the resource. - String path; - - // The TCF channel used to open and read the resource. - IChannel channel; - // The file system service used to open and read the resource. - IFileSystem service; - // The file's handle - IFileHandle handle; - // Current reading position long position; // The byte array used to buffer data. @@ -66,14 +43,11 @@ public class TcfInputStream extends InputStream { // The chunk size of the reading buffer. int chunk_size = 0; - // Channel opening timeout. - long connectTimeout; - // File opening timeout. - long openTimeout; // File reading timeout. - long readTimeout; - // File closing timeout. - long closeTimeout; + int timeout; + + // The URL Connection + TcfURLConnection connection; /** * Create a TCF input stream connected the specified peer with specified @@ -84,8 +58,8 @@ public class TcfInputStream extends InputStream { * @param path * The path to the remote resource. */ - public TcfInputStream(IPeer peer, String path) { - this(peer, path, DEFAULT_CHUNK_SIZE); + public TcfInputStream(TcfURLConnection connection) { + this(connection, DEFAULT_CHUNK_SIZE); } /** @@ -99,112 +73,18 @@ public class TcfInputStream extends InputStream { * @param chunk_size * The buffer size. */ - public TcfInputStream(IPeer peer, final String path, int chunk_size) { - this.peer = peer; - this.path = path; + public TcfInputStream(TcfURLConnection connection, int chunk_size) { + this.connection = connection; this.chunk_size = chunk_size; } /** - * Set the timeout for opening a TCF channel. - * - * @param connectTimeout the timeout in milliseconds. - */ - void setConnectTimeout(long channelTimeout) { - this.connectTimeout = channelTimeout; - } - - /** - * Set the timeout for opening a file. - * - * @param openTimeout the timeout in milliseconds. - */ - void setOpenTimeout(long openTimeout) { - this.openTimeout = openTimeout; - } - - /** * Set the timeout for reading a file. * - * @param readTimeout the timeout in milliseconds. - */ - void setReadTimeout(long readTimeout) { - this.readTimeout = readTimeout; - } - - /** - * Set the timeout for closing a file. - * - * @param closeTimeout the timeout in milliseconds. + * @param timeout the timeout in milliseconds. */ - void setCloseTimeout(long closeTimeout) { - this.closeTimeout = closeTimeout; - } - - - /** - * Open and connect the input stream to the agent peer. - * - * @throws IOException - * Thrown when the connecting is failed. - */ - private void connect() throws IOException { - final Rendezvous rendezvous = new Rendezvous(); - // Open the channel - channel = peer.openChannel(); - channel.addChannelListener(new IChannelListener() { - @Override - public void onChannelOpened() { - Assert.isTrue(Protocol.isDispatchThread()); - service = channel.getRemoteService(IFileSystem.class); - rendezvous.arrive(); - } - - @Override - public void onChannelClosed(Throwable error) { - } - - @Override - public void congestionLevel(int level) { - } - }); - // Wait for the end of the opening. - try { - rendezvous.waiting(connectTimeout); - } catch (InterruptedException e) { - throw new IOException(Messages.TcfInputStream_OpenTCFTimeout); - } - if (service != null) { - rendezvous.reset(); - final FileSystemException[] errors = new FileSystemException[1]; - // Open the file. - service.open(path, IFileSystem.TCF_O_READ, null, new DoneOpen() { - @Override - public void doneOpen(IToken token, FileSystemException error, - IFileHandle hdl) { - errors[0] = error; - handle = hdl; - // Rendezvous - rendezvous.arrive(); - } - }); - try { - rendezvous.waiting(openTimeout); - } catch (InterruptedException e) { - throw new IOException(Messages.TcfInputStream_OpenFileTimeout); - } - if (errors[0] != null) { - IOException exception = new IOException(errors[0].toString()); - exception.initCause(errors[0]); - throw exception; - } - if (handle == null) { - throw new IOException(Messages.TcfInputStream_NoFileReturned); - } - } else { - throw new IOException(Messages.TcfInputStream_NoFSServiceAvailable); - } - connected = true; + void setTimeout(int readTimeout) { + this.timeout = readTimeout; } /* @@ -214,9 +94,6 @@ public class TcfInputStream extends InputStream { */ @Override public int read() throws IOException { - if (!connected) { - connect(); - } if (closed) throw new IOException(Messages.TcfInputStream_StreamClosed); if (ERROR != null) { @@ -253,10 +130,10 @@ public class TcfInputStream extends InputStream { */ private void readBlock() { final Rendezvous rendezvous = new Rendezvous(); - service.read(handle, position, chunk_size, new DoneRead() { + IFileSystem service = connection.handle.getService(); + service.read(connection.handle, position, chunk_size, new DoneRead() { @Override - public void doneRead(IToken token, FileSystemException error, - byte[] data, boolean eof) { + public void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof) { if (error != null) { ERROR = error; } @@ -274,7 +151,7 @@ public class TcfInputStream extends InputStream { }); // Waiting for reading. try { - rendezvous.waiting(readTimeout); + rendezvous.waiting(timeout); } catch (InterruptedException e) { ERROR = new IOException(Messages.TcfInputStream_ReadTimeout); } @@ -287,21 +164,8 @@ public class TcfInputStream extends InputStream { */ @Override public void close() throws IOException { - if (connected && !closed) { - final Rendezvous rendezvous = new Rendezvous(); - service.close(handle, new DoneClose() { - @Override - public void doneClose(IToken token, FileSystemException error) { - rendezvous.arrive(); - } - }); - try { - rendezvous.waiting(closeTimeout); - } catch (InterruptedException e) { - throw new IOException(Messages.TcfInputStream_CloseTimeout); - } - channel.close(); - super.close(); + if (!closed) { + connection.closeStream(this); closed = true; } } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfOutputStream.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfOutputStream.java new file mode 100644 index 000000000..b0175744e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfOutputStream.java @@ -0,0 +1,146 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * William Chen (Wind River) - [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.url;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneWrite;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+
+/**
+ * The TCF output stream returned by {@link TcfURLConnection#getOutputStream()}.
+ *
+ */
+public class TcfOutputStream extends OutputStream {
+ // Default chunk size while pumping the data.
+ private static final int DEFAULT_CHUNK_SIZE = 5 * 1024;
+
+ // Current writing position
+ long position;
+ // The byte array used to buffer data.
+ byte[] buffer;
+ // The offset being written in the buffer.
+ int offset;
+
+ // If the stream has been closed.
+ boolean closed;
+ // The current error during writing.
+ Exception ERROR;
+
+ // The chunk size of the writing buffer.
+ int chunk_size = 0;
+
+ // The URL Connection
+ TcfURLConnection connection;
+
+ // The timeout for writing data.
+ int timeout;
+ /**
+ * Create a TCF output stream connected the specified peer with specified
+ * path to the remote resource.
+ *
+ * @param peer
+ * The TCF agent peer.
+ * @param path
+ * The path to the remote resource.
+ */
+ public TcfOutputStream(TcfURLConnection connection) {
+ this(connection, DEFAULT_CHUNK_SIZE);
+ }
+
+ /**
+ * Create a TCF output stream connected the specified peer with specified
+ * path to the remote resource using the specified buffer size.
+ *
+ * @param peer
+ * The TCF agent peer.
+ * @param path
+ * The path to the remote resource.
+ * @param chunk_size
+ * The buffer size.
+ */
+ public TcfOutputStream(TcfURLConnection connection, int chunk_size) {
+ this.connection = connection;
+ this.chunk_size = chunk_size;
+ buffer = new byte[chunk_size];
+ offset = 0;
+ }
+
+ /**
+ * Set the timeout for writing a file.
+ *
+ * @param timeout The timeout for writing a file.
+ */
+ void setTimeout(int timeout){
+ this.timeout = timeout;
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#write(int)
+ */
+ @Override
+ public void write(int b) throws IOException {
+ if (closed)
+ throw new IOException(Messages.TcfOutputStream_StreamClosed);
+ if (ERROR != null) {
+ IOException exception = new IOException(ERROR.toString());
+ exception.initCause(ERROR);
+ throw exception;
+ }
+ if (offset < buffer.length) {
+ buffer[offset++] = (byte) b;
+ }
+ if (offset == buffer.length)
+ flush();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#flush()
+ */
+ @Override
+ public void flush() throws IOException {
+ if (offset > 0) {
+ final Rendezvous rendezvous = new Rendezvous();
+ IFileSystem service = connection.handle.getService();
+ service.write(connection.handle, position, buffer, 0, offset, new DoneWrite() {
+ @Override
+ public void doneWrite(IToken token, FileSystemException error) {
+ if (error != null) {
+ ERROR = error;
+ }
+ position += offset;
+ offset = 0;
+ rendezvous.arrive();
+ }
+ });
+ // Waiting for writing.
+ try {
+ rendezvous.waiting(timeout);
+ } catch (InterruptedException e) {
+ ERROR = new IOException(Messages.TcfOutputStream_WriteTimeout);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#close()
+ */
+ @Override
+ public void close() throws IOException {
+ if (!closed) {
+ connection.closeStream(this);
+ closed = true;
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java index 7cf06beda..7b24e513f 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java @@ -9,16 +9,29 @@ *******************************************************************************/ package org.eclipse.tm.te.tcf.filesystem.internal.url; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import java.net.URLConnection; +import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IPeer; -import org.eclipse.tm.tcf.protocol.Protocol; +import org.eclipse.tm.tcf.protocol.IToken; +import org.eclipse.tm.tcf.services.IFileSystem; +import org.eclipse.tm.tcf.services.IFileSystem.DoneClose; +import org.eclipse.tm.tcf.services.IFileSystem.DoneOpen; +import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException; +import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle; +import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFChannelException; +import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages; +import org.eclipse.tm.te.tcf.filesystem.model.FSModel; +import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode; + /** - * The URL connection returned by TCF url stream service used to handle - * "tcf" stream protocol. + * The URL connection returned by TCF stream service used to handler "tcf" + * stream protocol. */ public class TcfURLConnection extends URLConnection { // Default connecting timeout. @@ -27,29 +40,38 @@ public class TcfURLConnection extends URLConnection { private static final int DEFAULT_OPEN_TIMEOUT = 5000; // Default file reading timeout. private static final int DEFAULT_READ_TIMEOUT = 5000; - //Default file closing timeout. + // Default file closing timeout. private static final int DEFAULT_CLOSE_TIMEOUT = 5000; // The schema name of the stream protocol. public static final String PROTOCOL_SCHEMA = "tcf"; //$NON-NLS-1$ + // The input stream of this connection. private TcfInputStream inputStream; + // The output stream of this connection. + private TcfOutputStream outputStream; + // The TCF agent peer of the connection. private IPeer peer; // The path to the resource on the remote file system. private String path; // The timeout for opening a file. - private long openTimeout; + private int openTimeout; // The timeout for closing a file. - private long closeTimeout; + private int closeTimeout; + + // The TCF channel used to open and read the resource. + IChannel channel; + // The file's handle + IFileHandle handle; + /** - * Create a TCF URL Connection using the specified url. The - * format of this URL should be: - * tcf:///<TCF_AGENT_ID>/remote/path/to/the/resource... - * The stream protocol schema is designed in this way in order - * to retrieve the agent peer ID without knowing the structure - * of a TCF peer id. + * Create a TCF URL Connection using the specified url. The format of this + * URL should be: tcf:///<TCF_AGENT_ID>/remote/path/to/the/resource... The + * stream protocol schema is designed in this way in order to retrieve the + * agent peer ID without knowing the structure of a TCF peer id. * - * @param url The URL of the resource. + * @param url + * The URL of the resource. */ public TcfURLConnection(URL url) { super(url); @@ -57,16 +79,14 @@ public class TcfURLConnection extends URLConnection { // /<TCF_AGENT_ID>/remote/path/to/the/resource... path = url.getPath(); int slash = path.indexOf("/", 1); //$NON-NLS-1$ - String peerId; if (slash != -1){ - peerId = path.substring(1, slash); path = path.substring(slash); if (path.matches("/[A-Za-z]:.*")) path = path.substring(1); //$NON-NLS-1$ - }else{ - peerId = path.substring(1); } //Get the peer using the peer id. - peer = Protocol.getLocator().getPeers().get(peerId); + FSTreeNode node = FSModel.getInstance().getTreeNode(url); + if(node != null) + peer = node.peerNode.getPeer(); // Set default timeout. setConnectTimeout(DEFAULT_CONNECT_TIMEOUT); setOpenTimeout(DEFAULT_OPEN_TIMEOUT); @@ -86,11 +106,13 @@ public class TcfURLConnection extends URLConnection { /** * Set the timeout for closing a file. * - * @param closeTimeout the timeout in milliseconds. + * @param closeTimeout + * the timeout in milliseconds. */ - public void setCloseTimeout(long closeTimeout) { + public void setCloseTimeout(int closeTimeout) { this.closeTimeout = closeTimeout; } + /** * Get the timeout for opening a file. * @@ -103,29 +125,90 @@ public class TcfURLConnection extends URLConnection { /** * Set the timeout for opening a file. * - * @param openTimeout the timeout in milliseconds. + * @param openTimeout + * the timeout in milliseconds. */ - public void setOpenTimeout(long openTimeout) { + public void setOpenTimeout(int openTimeout) { this.openTimeout = openTimeout; } + + /** + * Open a file on the remote file system for read/write and store the file handle. + * + * @throws IOException Opening file fails. + */ + private void openFile() throws IOException { + if(peer == null) + throw new IOException(Messages.TcfURLConnection_NoSuchTcfAgent); + try { + // Open the channel + channel = TCFUtilities.openChannel(peer); + } catch (TCFChannelException e) { + throw new IOException(e.getLocalizedMessage()); + } + if (channel != null) { + IFileSystem service = channel.getRemoteService(IFileSystem.class); + if (service != null) { + final Rendezvous rendezvous = new Rendezvous(); + final FileSystemException[] errors = new FileSystemException[1]; + // Open the file. + int open_flag = 0; + if (doInput) + open_flag |= IFileSystem.TCF_O_READ; + if (doOutput) + open_flag |= IFileSystem.TCF_O_WRITE; + service.open(path, open_flag, null, new DoneOpen() { + @Override + public void doneOpen(IToken token, FileSystemException error, IFileHandle hdl) { + errors[0] = error; + handle = hdl; + // Rendezvous + rendezvous.arrive(); + } + }); + try { + rendezvous.waiting(openTimeout); + } catch (InterruptedException e) { + throw new IOException(Messages.TcfURLConnection_OpenFileTimeout); + } + if (errors[0] != null) { + IOException exception = new IOException(errors[0].toString()); + exception.initCause(errors[0]); + throw exception; + } + if (handle == null) { + throw new IOException(Messages.TcfURLConnection_NoFileHandleReturned); + } + } else { + throw new IOException(Messages.TcfURLConnection_NoFSServiceAvailable); + } + } + } + /* * (non-Javadoc) + * * @see java.net.URLConnection#connect() */ @Override public void connect() throws IOException { if (!connected) { - inputStream = new TcfInputStream(peer, path); - inputStream.setConnectTimeout(getConnectTimeout()); - inputStream.setOpenTimeout(getOpenTimeout()); - inputStream.setReadTimeout(getReadTimeout()); - inputStream.setCloseTimeout(getCloseTimeout()); + openFile(); + if (doInput) { + inputStream = new TcfInputStream(this); + inputStream.setTimeout(getReadTimeout()); + } + if (doOutput) { + outputStream = new TcfOutputStream(this); + outputStream.setTimeout(getReadTimeout()); + } connected = true; } } /* * (non-Javadoc) + * * @see java.net.URLConnection#getInputStream() */ @Override @@ -134,4 +217,72 @@ public class TcfURLConnection extends URLConnection { connect(); return inputStream; } + + /* + * (non-Javadoc) + * @see java.net.URLConnection#getOutputStream() + */ + @Override + public OutputStream getOutputStream() throws IOException { + if (!connected) + connect(); + return outputStream; + } + + /** + * Close the stream, release its file handler and close + * the TCF channel used if possible. + * + * @param stream The stream either the input stream or the output stream. + * @throws IOException If closing file handle times out. + */ + public synchronized void closeStream(Closeable stream) throws IOException { + boolean shouldClose = shouldCloseFileHandle(stream); + if (shouldClose) { + final Rendezvous rendezvous = new Rendezvous(); + IFileSystem service = handle.getService(); + service.close(handle, new DoneClose() { + @Override + public void doneClose(IToken token, FileSystemException error) { + rendezvous.arrive(); + } + }); + try { + rendezvous.waiting(closeTimeout); + } catch (InterruptedException e) { + throw new IOException(Messages.TcfURLConnection_CloseFileTimeout); + } + channel.close(); + } + } + + /** + * Decide if the file handle and the TCF channel should be closed if + * the specified stream is closed. If the stream is the last stream + * that depends on the file handle and the TCF channel, then it should + * be closed. + * + * @param stream The stream to be closed. + * @return true if the file handle and the TCF channel should be closed. + */ + private boolean shouldCloseFileHandle(Closeable stream) { + boolean shouldClose = false; + if (stream == inputStream) { + if (doOutput) { + if (outputStream.closed) { + shouldClose = true; + } + } else { + shouldClose = true; + } + } else if (stream == outputStream) { + if (doInput) { + if (inputStream.closed) + shouldClose = true; + } else { + shouldClose = true; + } + } + return shouldClose; + } } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/CacheState.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/CacheState.java new file mode 100644 index 000000000..a452855e4 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/CacheState.java @@ -0,0 +1,22 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.model;
+
+/**
+ * The enumeration that defines the states of a file's local cache, including "consistent", "modified",
+ * "outdated" and "conflict".
+ */
+public enum CacheState {
+ consistent, // Neither of the local file and the remote file has been changed since checking out.
+ modified, // The local file has changed while the remote file has not since checking out.
+ outdated, // The remote file has changed while the local file has not since checking out.
+ conflict // Both the local file and the remote file have changed since checking out.
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java index a92f26c88..b0c977f26 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java @@ -9,11 +9,18 @@ *******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.model;
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.tm.te.tcf.filesystem.internal.events.INodeStateListener;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
/**
* The file system model implementation.
@@ -22,13 +29,37 @@ import org.eclipse.core.runtime.PlatformObject; * model root node per peer id.
*/
public final class FSModel extends PlatformObject {
+ // Shared instance
+ private static final FSModel instance = new FSModel();
+ /**
+ * Get the shared instance of File System model.
+ * @return The File System Model.
+ */
+ public static FSModel getInstance(){
+ return instance;
+ }
/**
* The file system model root node cache. The keys
* are the peer id's.
*/
- private final Map<String, FSTreeNode> roots = new HashMap<String, FSTreeNode>();
+ private final Map<String, FSTreeNode> roots;
+ // The table mapping the local file to the fileNodes.
+ private Map<String, FSTreeNode> fileNodes;
+ // The table mapping the URL to the fileNodes.
+ private Map<URL, FSTreeNode> urlNodes;
+ // Node state listeners.
+ private List<INodeStateListener> listeners;
/**
+ * Create a File System Model.
+ */
+ private FSModel() {
+ roots = Collections.synchronizedMap(new HashMap<String, FSTreeNode>());
+ fileNodes = Collections.synchronizedMap(new HashMap<String, FSTreeNode>());
+ urlNodes = Collections.synchronizedMap(new HashMap<URL, FSTreeNode>());
+ listeners = Collections.synchronizedList(new ArrayList<INodeStateListener>());
+ }
+ /**
* Returns the file system model root node for the peer identified
* by the given peer id.
*
@@ -59,5 +90,74 @@ public final class FSModel extends PlatformObject { */
public void dispose() {
roots.clear();
+ fileNodes.clear();
+ urlNodes.clear();
+ }
+
+ /**
+ * Called to add an FSTreeNode to the two maps, i.e., the location-node map and
+ * the cache-location map.
+ * @param node The FSTreeNode to be added.
+ */
+ public void addNode(FSTreeNode node){
+ File cacheFile = CacheManager.getInstance().getCacheFile(node);
+ fileNodes.put(cacheFile.getAbsolutePath(), node);
+ urlNodes.put(node.getLocationURL(), node);
+ }
+
+ /**
+ * Get the FSTreeNode given its local file's path.
+ *
+ * @param path The local file's path.
+ * @return The FSTreeNode
+ */
+ public FSTreeNode getTreeNode(String path) {
+ return fileNodes.get(path);
+ }
+
+ /**
+ * Get the FSTreeNode given its URL location
+ * @param location the FSTreeNode's location
+ * @return the FSTreeNode
+ */
+ public FSTreeNode getTreeNode(URL location){
+ return urlNodes.get(location);
+ }
+
+ /**
+ * Add an INodeStateListener to the File System model if it is not
+ * in the listener list yet.
+ *
+ * @param listener The INodeStateListener to be added.
+ */
+ public void addNodeStateListener(INodeStateListener listener){
+ if(!listeners.contains(listener)){
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove the INodeStateListener from the File System model if it
+ * exists in the listener list.
+ *
+ * @param listener The INodeStateListener to be removed.
+ */
+ public void removeNodeStateListener(INodeStateListener listener){
+ if(listeners.contains(listener)){
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Fire a node state changed event with the specified node.
+ *
+ * @param node The node whose state has changed.
+ */
+ public void fireNodeStateChanged(FSTreeNode node){
+ synchronized(listeners){
+ for(INodeStateListener listener:listeners){
+ listener.stateChanged(node);
+ }
+ }
}
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java index 203a87963..cbb2f9a1d 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java @@ -15,33 +15,27 @@ package org.eclipse.tm.te.tcf.filesystem.model; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.PlatformObject; -import org.eclipse.tm.tcf.protocol.IChannel; -import org.eclipse.tm.tcf.protocol.IChannel.IChannelListener; -import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.tm.tcf.services.IFileSystem; -import org.eclipse.tm.tcf.services.IFileSystem.DoneUser; -import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException; import org.eclipse.tm.te.tcf.filesystem.interfaces.IWindowsFileAttributes; import org.eclipse.tm.te.tcf.filesystem.internal.UserAccount; -import org.eclipse.tm.te.tcf.filesystem.internal.url.Rendezvous; +import org.eclipse.tm.te.tcf.filesystem.internal.handlers.UserManager; import org.eclipse.tm.te.tcf.filesystem.internal.url.TcfURLConnection; import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel; /** * Target Explorer: Representation of a file system tree node. * <p> - * <b>Note:</b> Node construction and child list access is limited to - * the TCF event dispatch thread. + * <b>Note:</b> Node construction and child list access is limited to the TCF + * event dispatch thread. */ public final class FSTreeNode extends PlatformObject { - // The key to save and retrieve the user account in a peer model. - private static final String USER_ACCOUNT_KEY = "user.account"; //$NON-NLS-1$ private final UUID uniqueId = UUID.randomUUID(); @@ -73,7 +67,7 @@ public final class FSTreeNode extends PlatformObject { /** * The tree node children. */ - private List<FSTreeNode> children = new ArrayList<FSTreeNode>(); + private List<FSTreeNode> children; /** * Flag to mark once the children of the node got queried @@ -90,10 +84,13 @@ public final class FSTreeNode extends PlatformObject { */ public FSTreeNode() { super(); + children = Collections.synchronizedList(new ArrayList<FSTreeNode>()); Assert.isTrue(Protocol.isDispatchThread()); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.lang.Object#hashCode() */ @Override @@ -104,8 +101,9 @@ public final class FSTreeNode extends PlatformObject { /** * Returns the children list storage object. * <p> - * <b>Note:</b> This method must be called from within the TCF event dispatch thread only! - * + * <b>Note:</b> This method must be called from within the TCF event + * dispatch thread only! + * * @return The children list storage object. */ public final List<FSTreeNode> getChildren() { @@ -113,18 +111,24 @@ public final class FSTreeNode extends PlatformObject { return children; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.lang.Object#equals(java.lang.Object) */ @Override public final boolean equals(Object obj) { + if(this == obj) + return true; if (obj instanceof FSTreeNode) { - return uniqueId.equals(((FSTreeNode)obj).uniqueId); + return uniqueId.equals(((FSTreeNode) obj).uniqueId); } return super.equals(obj); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.lang.Object#toString() */ @Override @@ -137,6 +141,7 @@ public final class FSTreeNode extends PlatformObject { /** * Return if the node is a Windows file/folder node. + * * @return true if it is a Windows node, or else false. */ public boolean isWindowsNode() { @@ -145,6 +150,7 @@ public final class FSTreeNode extends PlatformObject { /** * Return if the node is a file. + * * @return true if it is a file, or else false. */ public boolean isFile() { @@ -153,6 +159,7 @@ public final class FSTreeNode extends PlatformObject { /** * Return if the node is a directory. + * * @return true if it is a directory, or else false. */ public boolean isDirectory() { @@ -161,7 +168,9 @@ public final class FSTreeNode extends PlatformObject { /** * Return if the attribute specified by the mask bit is turned on. - * @param bit The attribute's mask bit. + * + * @param bit + * The attribute's mask bit. * @return true if it is on, or else false. */ public boolean isWin32AttrOn(int bit) { @@ -174,6 +183,7 @@ public final class FSTreeNode extends PlatformObject { /** * Return if this file/folder is hidden. + * * @return true if it is hidden, or else false. */ public boolean isHidden() { @@ -182,6 +192,7 @@ public final class FSTreeNode extends PlatformObject { /** * Return if this file/folder is read-only. + * * @return true if it is read-only, or else false. */ public boolean isReadOnly() { @@ -191,8 +202,9 @@ public final class FSTreeNode extends PlatformObject { /** * Get the location of a file/folder node using the format of the file * system's platform. - * - * @param node The file/folder node. + * + * @param node + * The file/folder node. * @return The location of the file/folder. */ public String getLocation() { @@ -207,11 +219,12 @@ public final class FSTreeNode extends PlatformObject { /** * Get the location of a file/folder. - * - * @param cross If the format is cross-platform. + * + * @param cross + * If the format is cross-platform. * @return The path to the file/folder. */ - private String getLocation(boolean cross) { + public String getLocation(boolean cross) { if (parent == null) return null; String pLoc = parent.getLocation(cross); @@ -223,23 +236,28 @@ public final class FSTreeNode extends PlatformObject { } /** - * Get the URL of the file or folder. The URL's format - * is created in the following way: - * tcf:///<TCF_AGENT_ID>/remote/path/to/the/resource... - * See {@link TcfURLConnection#TcfURLConnection(URL)} - * + * Get the URL of the file or folder. The URL's format is created in the + * following way: tcf:///<TCF_AGENT_ID>/remote/path/to/the/resource... See + * {@link TcfURLConnection#TcfURLConnection(URL)} + * * @return The URL of the file/folder. * @throws MalformedURLException */ - public URL getLocationURL() throws MalformedURLException { - String id = peerNode.getPeer().getID(); - String path = getLocation(true); - String url = TcfURLConnection.PROTOCOL_SCHEMA+":///" + id + (isWindowsNode() ? "/" + path : path); //$NON-NLS-1$ //$NON-NLS-2$ - return new URL(url); + public URL getLocationURL() { + try { + String id = peerNode.getPeer().getID(); + String path = getLocation(true); + String location = TcfURLConnection.PROTOCOL_SCHEMA + ":/" + id + (isWindowsNode() ? "/" + path : path); //$NON-NLS-1$ //$NON-NLS-2$ + return new URL(location); + } catch (MalformedURLException e) { + assert false; + return null; + } } /** * If this node is a root node. + * * @return true if this node is a root node. */ public boolean isRoot() { @@ -248,11 +266,11 @@ public final class FSTreeNode extends PlatformObject { /** * If this file is readable. - * + * * @return true if it is readable. */ public boolean isReadable() { - UserAccount account = getUserAccount(); + UserAccount account = UserManager.getInstance().getUserAccount(peerNode); if (account != null && attr != null) { if (attr.uid == account.getEUID()) { return (attr.permissions & IFileSystem.S_IRUSR) != 0; @@ -267,11 +285,11 @@ public final class FSTreeNode extends PlatformObject { /** * If this file is writable. - * + * * @return true if it is writable. */ public boolean isWritable() { - UserAccount account = getUserAccount(); + UserAccount account = UserManager.getInstance().getUserAccount(peerNode); if (account != null && attr != null) { if (attr.uid == account.getEUID()) { return (attr.permissions & IFileSystem.S_IWUSR) != 0; @@ -286,11 +304,11 @@ public final class FSTreeNode extends PlatformObject { /** * If this file is executable. - * + * * @return true if it is executable. */ public boolean isExecutable() { - UserAccount account = getUserAccount(); + UserAccount account = UserManager.getInstance().getUserAccount(peerNode); if (account != null && attr != null) { if (attr.uid == account.getEUID()) { return (attr.permissions & IFileSystem.S_IXUSR) != 0; @@ -302,108 +320,4 @@ public final class FSTreeNode extends PlatformObject { } return false; } - - /** - * Get the information of the client user account. - * - * @return The client user account's information. - */ - private UserAccount getUserAccount() { - UserAccount account = getUserAccount(peerNode); - if (account == null) { - final Rendezvous rendezvous = new Rendezvous(); - IChannel channel = peerNode.getPeer().openChannel(); - channel.addChannelListener(new IChannelListener() { - @Override - public void onChannelOpened() { - rendezvous.arrive(); - } - - @Override - public void onChannelClosed(Throwable error) { - } - - @Override - public void congestionLevel(int level) { - } - }); - try { - rendezvous.waiting(5000L); - } catch (InterruptedException e) { - return null; - } - rendezvous.reset(); - IFileSystem service = channel.getRemoteService(IFileSystem.class); - final UserAccount[] accounts = new UserAccount[1]; - service.user(new DoneUser() { - @Override - public void doneUser(IToken token, FileSystemException error, - int real_uid, int effective_uid, int real_gid, - int effective_gid, String home) { - if (error == null) { - accounts[0] = new UserAccount(real_uid, real_gid, - effective_uid, effective_gid, home); - } - rendezvous.arrive(); - } - }); - try { - rendezvous.waiting(5000L); - } catch (InterruptedException e) { - return null; - } - if (accounts[0] == null) - return null; - account = accounts[0]; - setUserAccount(peerNode, account); - } - return account; - } - - /** - * Get the user account stored in the specified peer model using a key named - * "user.account" defined by the constant USER_ACCOUNT_KEY. - * - * @param peer - * The peer model from which the user account is retrieved. - * @return The user account if it exists or null if not. - */ - private UserAccount getUserAccount(final IPeerModel peer) { - UserAccount account; - if (Protocol.isDispatchThread()) { - account = (UserAccount) peer.getProperty(USER_ACCOUNT_KEY); - } else { - final UserAccount[] accounts = new UserAccount[1]; - Protocol.invokeAndWait(new Runnable() { - @Override - public void run() { - accounts[0] = (UserAccount) peer - .getProperty(USER_ACCOUNT_KEY); - } - }); - account = accounts[0]; - } - return account; - } - - /** - * Save the user account to the specified peer model using a key named - * "user.account" defined by the constant USER_ACCOUNT_KEY. - * - * @param peer - * The peer model to which the user account is saved. - */ - private void setUserAccount(final IPeerModel peer, final UserAccount account) { - assert peer != null && account != null; - if (Protocol.isDispatchThread()) { - peer.setProperty(USER_ACCOUNT_KEY, account); - } else { - Protocol.invokeAndWait(new Runnable() { - @Override - public void run() { - peer.setProperty(USER_ACCOUNT_KEY, account); - } - }); - } - } } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/activator/UIPlugin.java index f252b3718..c95969bfe 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/activator/UIPlugin.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/activator/UIPlugin.java @@ -76,18 +76,18 @@ public class UIPlugin extends AbstractUIPlugin { @Override protected void initializeImageRegistry(ImageRegistry registry) { URL url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OBJ + "target.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_TARGET, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.TARGET, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OVR + "gold_ovr.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_GOLD_OVR, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.GOLD_OVR, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OVR + "green_ovr.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_GREEN_OVR, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.GREEN_OVR, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OVR + "grey_ovr.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_GREY_OVR, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.GREY_OVR, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OVR + "red_ovr.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_RED_OVR, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.RED_OVR, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OVR + "redX_ovr.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_RED_X_OVR, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.RED_X_OVR, ImageDescriptor.createFromURL(url)); } /** diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/ImageConsts.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/ImageConsts.java index 477fb8574..4bd90b1ce 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/ImageConsts.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/ImageConsts.java @@ -51,30 +51,30 @@ public interface ImageConsts { /** * The key to access the base target object image. */ - public static final String IMAGE_TARGET = "TargetObject"; //$NON-NLS-1$ + public static final String TARGET = "TargetObject"; //$NON-NLS-1$ /** * The key to access the target object gold overlay image. */ - public static final String IMAGE_GOLD_OVR = "GoldOverlay"; //$NON-NLS-1$ + public static final String GOLD_OVR = "GoldOverlay"; //$NON-NLS-1$ /** * The key to access the target object green overlay image. */ - public static final String IMAGE_GREEN_OVR = "GreenOverlay"; //$NON-NLS-1$ + public static final String GREEN_OVR = "GreenOverlay"; //$NON-NLS-1$ /** * The key to access the target object grey overlay image. */ - public static final String IMAGE_GREY_OVR = "GreyOverlay"; //$NON-NLS-1$ + public static final String GREY_OVR = "GreyOverlay"; //$NON-NLS-1$ /** * The key to access the target object red overlay image. */ - public static final String IMAGE_RED_OVR = "RedOverlay"; //$NON-NLS-1$ + public static final String RED_OVR = "RedOverlay"; //$NON-NLS-1$ /** * The key to access the target object red X overlay image. */ - public static final String IMAGE_RED_X_OVR = "RedXOverlay"; //$NON-NLS-1$ + public static final String RED_X_OVR = "RedXOverlay"; //$NON-NLS-1$ } diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/LabelProviderDelegate.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/LabelProviderDelegate.java index 4c8cf3f4d..01e2de5a0 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/LabelProviderDelegate.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/LabelProviderDelegate.java @@ -64,7 +64,7 @@ public class LabelProviderDelegate extends LabelProvider implements ILabelDecora @Override public Image getImage(Object element) { if (element instanceof IPeerModel) { - return UIPlugin.getImage(ImageConsts.IMAGE_TARGET); + return UIPlugin.getImage(ImageConsts.TARGET); } return super.getImage(element); diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/images/PeerImageDescriptor.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/images/PeerImageDescriptor.java index b2d610eb9..2137fdfc0 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/images/PeerImageDescriptor.java +++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.ui/src/org/eclipse/tm/te/tcf/ui/internal/navigator/images/PeerImageDescriptor.java @@ -85,19 +85,19 @@ public class PeerImageDescriptor extends AbstractImageDescriptor { drawCentered(baseImage, width, height); if (state == IPeerModelProperties.STATE_UNKNOWN) { /* unknown */ - drawBottomRight(ImageConsts.IMAGE_GREY_OVR); + drawBottomRight(ImageConsts.GREY_OVR); } else if (state == IPeerModelProperties.STATE_REACHABLE) { /* not connected, but reachable */ - drawBottomRight(ImageConsts.IMAGE_GOLD_OVR); + drawBottomRight(ImageConsts.GOLD_OVR); } else if (state == IPeerModelProperties.STATE_CONNECTED) { /* connected */ - drawBottomRight(ImageConsts.IMAGE_GREEN_OVR); + drawBottomRight(ImageConsts.GREEN_OVR); } else if (state == IPeerModelProperties.STATE_NOT_REACHABLE) { /* not connected, not reachable */ - drawBottomRight(ImageConsts.IMAGE_RED_OVR); + drawBottomRight(ImageConsts.RED_OVR); } else if (state == IPeerModelProperties.STATE_ERROR) { /* not connected, error */ - drawBottomRight(ImageConsts.IMAGE_RED_X_OVR); + drawBottomRight(ImageConsts.RED_X_OVR); } } diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.controls/build.properties b/target_explorer/plugins/org.eclipse.tm.te.ui.controls/build.properties index aa1a00826..c5906f02c 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.controls/build.properties +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.controls/build.properties @@ -3,3 +3,4 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ plugin.properties +jars.extra.classpath = platform:/plugin/org.eclipse.core.resources diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.forms/pom.xml b/target_explorer/plugins/org.eclipse.tm.te.ui.forms/pom.xml index 5d2830829..4a3a44eb7 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.forms/pom.xml +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.forms/pom.xml @@ -12,6 +12,6 @@ </parent> <version>1.0.0.qualifier</version> - <artifactId>org.eclipse.tm.te.ui.swt</artifactId> + <artifactId>org.eclipse.tm.te.ui.forms</artifactId> <packaging>eclipse-plugin</packaging> </project> diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.swt/pom.xml b/target_explorer/plugins/org.eclipse.tm.te.ui.swt/pom.xml index 4a3a44eb7..5d2830829 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.swt/pom.xml +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.swt/pom.xml @@ -12,6 +12,6 @@ </parent> <version>1.0.0.qualifier</version> - <artifactId>org.eclipse.tm.te.ui.forms</artifactId> + <artifactId>org.eclipse.tm.te.ui.swt</artifactId> <packaging>eclipse-plugin</packaging> </project> diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractCheckBoxCellPaintListener.java b/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractCheckBoxCellPaintListener.java index e5f5ecb6e..23f6902aa 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractCheckBoxCellPaintListener.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractCheckBoxCellPaintListener.java @@ -46,7 +46,7 @@ public abstract class AbstractCheckBoxCellPaintListener extends AbstractCellPain } /* (non-Javadoc) - * @see com.windriver.ui.swt.listener.AbstractCellPaintListener#getPaintOrigin(org.eclipse.swt.widgets.Event, org.eclipse.swt.graphics.Image) + * @see org.eclipse.tm.te.ui.swt.listener.AbstractCellPaintListener#getPaintOrigin(org.eclipse.swt.widgets.Event, org.eclipse.swt.graphics.Image) */ @Override protected Point getPaintOrigin(Event event, Image image) { @@ -60,7 +60,7 @@ public abstract class AbstractCheckBoxCellPaintListener extends AbstractCellPain } /* (non-Javadoc) - * @see com.windriver.ui.swt.listener.AbstractCellPaintListener#getImageToDraw(org.eclipse.swt.widgets.Item, int) + * @see org.eclipse.tm.te.ui.swt.listener.AbstractCellPaintListener#getImageToDraw(org.eclipse.swt.widgets.Item, int) */ @Override protected Image getImageToDraw(Item item, int columnIndex) { diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractDecorationCellPaintListener.java b/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractDecorationCellPaintListener.java index 94f4cd916..84fac2862 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractDecorationCellPaintListener.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.swt/src/org/eclipse/tm/te/ui/swt/listener/AbstractDecorationCellPaintListener.java @@ -40,7 +40,7 @@ public abstract class AbstractDecorationCellPaintListener extends AbstractCellPa } /* (non-Javadoc) - * @see com.windriver.ui.swt.listener.AbstractCellPaintListener#getPaintOrigin(org.eclipse.swt.widgets.Event, org.eclipse.swt.graphics.Image) + * @see org.eclipse.tm.te.ui.swt.listener.AbstractCellPaintListener#getPaintOrigin(org.eclipse.swt.widgets.Event, org.eclipse.swt.graphics.Image) */ @Override protected Point getPaintOrigin(Event event, Image image) { @@ -48,7 +48,7 @@ public abstract class AbstractDecorationCellPaintListener extends AbstractCellPa } /* (non-Javadoc) - * @see com.windriver.ui.swt.listener.AbstractCellPaintListener#getImageToDraw(org.eclipse.swt.widgets.Item, int) + * @see org.eclipse.tm.te.ui.swt.listener.AbstractCellPaintListener#getImageToDraw(org.eclipse.swt.widgets.Item, int) */ @Override protected Image getImageToDraw(Item item, int columnIndex) { diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.classpath b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.classpath new file mode 100644 index 000000000..8a8f1668c --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.options b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.options new file mode 100644 index 000000000..2ad378b66 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.options @@ -0,0 +1 @@ +org.eclipse.tm.te.ui.terminals/debugmode = 0 diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.project b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.project new file mode 100644 index 000000000..ad1ea71d6 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.tm.te.ui.terminals</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.settings/org.eclipse.jdt.core.prefs b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..ea56f8105 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,82 @@ +#Tue Oct 04 14:39:47 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=enabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/META-INF/MANIFEST.MF b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/META-INF/MANIFEST.MF new file mode 100644 index 000000000..dd35637dc --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/META-INF/MANIFEST.MF @@ -0,0 +1,22 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.tm.te.ui.terminals;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.eclipse.tm.te.ui.terminals.activator.UIPlugin +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.core.runtime;bundle-version="3.7.0", + org.eclipse.core.expressions;bundle-version="3.4.300", + org.eclipse.core.resources;bundle-version="3.7.100";resolution:=optional, + org.eclipse.ui;bundle-version="3.7.0", + org.eclipse.ui.ide;bundle-version="3.7.0", + org.eclipse.tm.terminal;bundle-version="3.1.1", + org.eclipse.tm.terminal.telnet;bundle-version="2.1.0", + org.eclipse.tm.terminal.view;bundle-version="2.2.0", + org.eclipse.tm.te.runtime;bundle-version="1.0.0", + org.eclipse.tm.te.ui;bundle-version="1.0.0", + org.eclipse.tm.te.ui.swt;bundle-version="1.0.0", + org.eclipse.cdt.core;bundle-version="5.3.0" +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-ActivationPolicy: lazy +Bundle-Localization: plugin diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/build.properties b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/build.properties new file mode 100644 index 000000000..73a5119ed --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/build.properties @@ -0,0 +1,6 @@ +source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ plugin.xml
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/plugin.properties b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/plugin.properties new file mode 100644 index 000000000..faf90fdb0 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/plugin.properties @@ -0,0 +1,23 @@ +################################################################################## +# Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. +# This program and the accompanying materials are made available under the terms +# of the Eclipse Public License v1.0 which accompanies this distribution, and is +# available at http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Wind River Systems - initial API and implementation +################################################################################## + +pluginName = Target Explorer, Terminals +providerName = Eclipse.org + +# ----- Terminals View ----- + +TerminalsView.name=Terminals + +# ----- Terminal Connectors ----- + +TerminalConnector.process=Process +TerminalConnector.streams=Streams Connector (hidden) + +# ----- Commands and Menu contributions ----- diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/plugin.xml b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/plugin.xml new file mode 100644 index 000000000..31dc3d21b --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/plugin.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+
+<!-- View contributions -->
+ <extension point="org.eclipse.ui.views">
+ <view
+ category="org.eclipse.tm.te.ui.views.category"
+ class="org.eclipse.tm.te.ui.terminals.view.TerminalsView"
+ icon="platform:/plugin/org.eclipse.ui.console/icons/full/eview16/console_view.gif"
+ id="org.eclipse.tm.te.ui.terminals.TerminalsView"
+ name="%TerminalsView.name">
+ </view>
+ </extension>
+
+<!-- Perspective extension contributions -->
+ <extension point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension targetID="*">
+ <view
+ id="org.eclipse.tm.te.ui.terminals.TerminalsView"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.ui.views.TaskList"
+ visible="false">
+ </view>
+ </perspectiveExtension>
+ </extension>
+
+<!-- Terminal connector contributions -->
+ <extension point="org.eclipse.tm.terminal.terminalConnectors">
+ <connector
+ name="%TerminalConnector.process"
+ id="org.eclipse.tm.te.ui.terminals.ProcessConnector"
+ class="org.eclipse.tm.te.ui.terminals.process.ProcessConnector"/>
+
+ <connector
+ name="%TerminalConnector.streams"
+ id="org.eclipse.tm.te.ui.terminals.StreamsConnector"
+ hidden="true"
+ class="org.eclipse.tm.te.ui.terminals.streams.StreamsConnector"/>
+ </extension>
+
+<!-- Preferences contributions -->
+ <extension point="org.eclipse.core.runtime.preferences">
+ <initializer class="org.eclipse.tm.te.ui.terminals.internal.PreferenceInitializer"/>
+ </extension>
+
+</plugin>
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/pom.xml b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/pom.xml new file mode 100644 index 000000000..26a4b4bbd --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/pom.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.tcf</groupId> + <artifactId>tcf-parent</artifactId> + <version>0.5.0-SNAPSHOT</version> + <relativePath>../../../pom.xml</relativePath> + </parent> + + <version>1.0.0.qualifier</version> + <artifactId>org.eclipse.tm.te.ui.terminals</artifactId> + <packaging>eclipse-plugin</packaging> +</project> diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/actions/AbstractAction.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/actions/AbstractAction.java new file mode 100644 index 000000000..54d2ecee4 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/actions/AbstractAction.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.actions; + +import java.util.Collections; + +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.expressions.EvaluationContext; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.nls.Messages; +import org.eclipse.tm.te.ui.terminals.tabs.TabFolderManager; +import org.eclipse.tm.te.ui.terminals.tabs.TabFolderToolbarHandler; +import org.eclipse.ui.ISources; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; + +/** + * Abstract terminal action wrapper implementation. + */ +@SuppressWarnings("restriction") +public abstract class AbstractAction extends AbstractTerminalAction { + // Reference to the parent toolbar handler + private final TabFolderToolbarHandler parent; + + /** + * Constructor. + * + * @param parent + * The parent toolbar handler instance. Must not be + * <code>null</code>. + * @param id + * The terminal action id. Must not be <code>null</code>. + */ + public AbstractAction(TabFolderToolbarHandler parent, String id) { + super(id); + + Assert.isNotNull(parent); + this.parent = parent; + } + + /** + * Returns the parent toolbar handler. + * + * @return The parent toolbar handler. + */ + protected final TabFolderToolbarHandler getParent() { + return parent; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getParent().getActiveTerminalViewControl(); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#run() + */ + @Override + public void run() { + // Get the active tab item from the tab folder manager + TabFolderManager manager = (TabFolderManager)getParent().getAdapter(TabFolderManager.class); + if (manager != null) { + // If we have the active tab item, we can get the active terminal control + CTabItem activeTabItem = manager.getActiveTabItem(); + if (activeTabItem != null) { + // And execute the command + executeCommand(activeTabItem.getData("customData")); //$NON-NLS-1$ + } + } + } + + /** + * Executes the command for the given data node as current and active menu selection. + * <p> + * <b>Node:</b> If the provided data node is <code>null</code>, the method will trigger + * the command with an empty selection. + * + * @param data The terminal custom data node or <code>null</code>. + */ + protected void executeCommand(Object data) { + // Get the command service from the workbench + ICommandService service = (ICommandService)PlatformUI.getWorkbench().getAdapter(ICommandService.class); + if (service != null && getCommandId() != null) { + // Get the command + final Command command = service.getCommand(getCommandId()); + if (command != null && command.isDefined()) { + // Construct a selection element + IStructuredSelection selection = data != null ? new StructuredSelection(data) : new StructuredSelection(); + // Construct the application context + EvaluationContext context = new EvaluationContext(null, selection); + // Apply the selection to the "activeMenuSelection" and "selection" variable too + context.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection); + context.addVariable(ISources.ACTIVE_MENU_SELECTION_NAME, selection); + // Construct the execution event + ExecutionEvent execEvent = new ExecutionEvent(command, Collections.EMPTY_MAP, this, context); + // And execute the event + try { + command.executeWithChecks(execEvent); + } catch (Exception e) { + IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), + NLS.bind(Messages.AbstractAction_error_commandExecutionFailed, getCommandId(), e.getLocalizedMessage()), + e); + UIPlugin.getDefault().getLog().log(status); + } + } + } + } + + /** + * Returns the command id of the command to execute. + * + * @return The command id. Must be never <code>null</code>. + */ + protected abstract String getCommandId(); + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#updateAction(boolean) + */ + @Override + public void updateAction(boolean aboutToShow) { + // Ignore the flag given from outside. We have to decide ourself + // what the enabled state of the action is + boolean enabled = getTarget() != null; + + // If a target terminal control is available, we need to find the corresponding + // VLM target object which we need to trigger the handler + if (enabled) { + // The action will be enabled if we can determine the VLM target object + enabled = false; + // Get the active tab item from the tab folder manager + TabFolderManager manager = (TabFolderManager)getParent().getAdapter(TabFolderManager.class); + if (manager != null) { + // If we have the active tab item, we can get the active terminal control + CTabItem activeTabItem = manager.getActiveTabItem(); + if (activeTabItem != null) { + enabled = checkEnableAction(activeTabItem.getData("customData")); //$NON-NLS-1$ + } + } + } + + setEnabled(enabled); + } + + /** + * Checks if the action should be enabled based on the given terminal data object. + * + * @param data The terminal data node or <code>null</code>. + * @return <code>True</code> to enable the action, <code>false</code> otherwise. + */ + protected boolean checkEnableAction(Object data) { + return data != null; + } + + /** + * Returns if the action is a separator. Returning <code>true</code> here + * means that an additional separator toolbar element is added right or + * above of the action. + * + * @return <code>True</code> if the action is separating the parent contribution manager, <code>false</code> otherwise. + */ + public boolean isSeparator() { + return false; + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/actions/TabScrollLockAction.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/actions/TabScrollLockAction.java new file mode 100644 index 000000000..73d9fd237 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/actions/TabScrollLockAction.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.tm.internal.terminal.actions.ActionMessages; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.interfaces.ImageConsts; + +/** + * Terminal console tab scroll lock action. + */ +@SuppressWarnings("restriction") +public class TabScrollLockAction extends AbstractTerminalAction { + + /** + * Constructor. + */ + public TabScrollLockAction() { + super(null, TabScrollLockAction.class.getName(), IAction.AS_RADIO_BUTTON); + + setupAction(ActionMessages.SCROLL_LOCK_0, + ActionMessages.SCROLL_LOCK_1, + UIPlugin.getImageDescriptor(ImageConsts.ACTION_ScrollLock_Hover), + UIPlugin.getImageDescriptor(ImageConsts.ACTION_ScrollLock_Enabled), + UIPlugin.getImageDescriptor(ImageConsts.ACTION_ScrollLock_Disabled), + true); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#run() + */ + @Override + public void run() { + ITerminalViewControl target = getTarget(); + if (target != null) { + target.setScrollLock(!target.isScrollLock()); + setChecked(target.isScrollLock()); + } + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#updateAction(boolean) + */ + @Override + public void updateAction(boolean aboutToShow) { + setEnabled(getTarget() != null && aboutToShow); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/activator/UIPlugin.java new file mode 100644 index 000000000..a919f570e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/activator/UIPlugin.java @@ -0,0 +1,143 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.te.ui.terminals.activator;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tm.te.runtime.preferences.ScopedEclipsePreferences;
+import org.eclipse.tm.te.runtime.tracing.TraceHandler;
+import org.eclipse.tm.te.ui.terminals.interfaces.ImageConsts;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class UIPlugin extends AbstractUIPlugin {
+ // The shared instance
+ private static UIPlugin plugin;
+
+ private static ScopedEclipsePreferences scopedPreferences = null;
+ // The trace handler instance
+ private static TraceHandler traceHandler;
+
+ /**
+ * The constructor
+ */
+ public UIPlugin() {
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static UIPlugin getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Convenience method which returns the unique identifier of this plugin.
+ */
+ public static String getUniqueIdentifier() {
+ if (getDefault() != null && getDefault().getBundle() != null) {
+ return getDefault().getBundle().getSymbolicName();
+ }
+ return null;
+ }
+
+ /**
+ * Return the scoped preferences for this plugin.
+ */
+ public static ScopedEclipsePreferences getScopedPreferences() {
+ if (scopedPreferences == null) {
+ scopedPreferences = new ScopedEclipsePreferences(getUniqueIdentifier());
+ }
+ return scopedPreferences;
+ }
+
+ /**
+ * Returns the bundles trace handler.
+ *
+ * @return The bundles trace handler.
+ */
+ public static TraceHandler getTraceHandler() {
+ if (traceHandler == null) {
+ traceHandler = new TraceHandler(getUniqueIdentifier());
+ }
+ return traceHandler;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ scopedPreferences = null;
+ traceHandler = null;
+ super.stop(context);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#initializeImageRegistry(org.eclipse.jface.resource.ImageRegistry)
+ */
+ @Override
+ protected void initializeImageRegistry(ImageRegistry registry) {
+ Bundle bundle = Platform.getBundle("org.eclipse.ui.console"); //$NON-NLS-1$
+ if (bundle != null && (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.ACTIVE)) {
+ URL url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_EVIEW + "console_view.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.VIEW_Terminals, ImageDescriptor.createFromURL(url));
+
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_CLCL + "lock_co.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_ScrollLock_Hover, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_ELCL + "lock_co.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_ScrollLock_Enabled, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_DLCL + "lock_co.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_ScrollLock_Disabled, ImageDescriptor.createFromURL(url));
+ }
+ }
+
+ /**
+ * Loads the image registered under the specified key from the image
+ * registry and returns the <code>Image</code> object instance.
+ *
+ * @param key The key the image is registered with.
+ * @return The <code>Image</code> object instance or <code>null</code>.
+ */
+ public static Image getImage(String key) {
+ return getDefault().getImageRegistry().get(key);
+ }
+
+ /**
+ * Loads the image registered under the specified key from the image
+ * registry and returns the <code>ImageDescriptor</code> object instance.
+ *
+ * @param key The key the image is registered with.
+ * @return The <code>ImageDescriptor</code> object instance or <code>null</code>.
+ */
+ public static ImageDescriptor getImageDescriptor(String key) {
+ return getDefault().getImageRegistry().getDescriptor(key);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/events/SelectionChangedBroadcastEvent.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/events/SelectionChangedBroadcastEvent.java new file mode 100644 index 000000000..1eb267f5d --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/events/SelectionChangedBroadcastEvent.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.events; + +import java.util.EventObject; + +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.tm.te.runtime.events.EventManager; +import org.eclipse.tm.te.ui.terminals.tabs.TabFolderManager; + +/** + * Terminal console selection changed broadcast event. The event is typically fired + * by a terminal console tab folder manager to signal a tab switch to all listeners. + */ +public class SelectionChangedBroadcastEvent extends EventObject { + private static final long serialVersionUID = -4970244776543572896L; + + // The selection changed event to broadcast + private final SelectionChangedEvent selectionChangedEvent; + + /** + * Constructor. + * + * @param source The event source. Must not be <code>null</code>. + * @param selectionChangedEvent The selection changed event or <code>null</code>. + */ + public SelectionChangedBroadcastEvent(TabFolderManager source, SelectionChangedEvent selectionChangedEvent) { + super(source); + this.selectionChangedEvent = selectionChangedEvent; + } + + /** + * Convenience method to return the source tab folder manager. + * + * @return The source tab folder manager. + */ + public TabFolderManager getSourceTabFolderManager() { + return (TabFolderManager)getSource(); + } + + /** + * Returns the broadcasted selection changed event. + * + * @return The broadcasted selection changed event or <code>null</code>. + */ + public SelectionChangedEvent getSelectionChangedEvent() { + return selectionChangedEvent; + } + + /* (non-Javadoc) + * @see java.util.EventObject#toString() + */ + @Override + public String toString() { + StringBuilder toString = new StringBuilder(getClass().getName()); + + String prefix = ""; //$NON-NLS-1$ + // if debugging the event, formating them a little bit better readable. + if (EventManager.isTracingEnabled()) + prefix = "\n\t\t"; //$NON-NLS-1$ + + toString.append(prefix + "source="); //$NON-NLS-1$ + toString.append(source); + toString.append("," + prefix + "selectionChangedEvent="); //$NON-NLS-1$ //$NON-NLS-2$ + toString.append(selectionChangedEvent); + toString.append("}"); //$NON-NLS-1$ + + return toString.toString(); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/IPreferenceKeys.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/IPreferenceKeys.java new file mode 100644 index 000000000..b07359704 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/IPreferenceKeys.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.interfaces; + +/** + * Terminals plug-in preference key definitions. + */ +public interface IPreferenceKeys { + /** + * Preference keys family prefix. + */ + public final String PREF_TERMINAL = "terminals"; //$NON-NLS-1$ + + /** + * Preference key: Remove terminated terminals when a new terminal is created. + */ + public final String PREF_REMOVE_TERMINATED_TERMINALS = PREF_TERMINAL + ".removeTerminatedTerminals"; //$NON-NLS-1$ +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/ITerminalsView.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/ITerminalsView.java new file mode 100644 index 000000000..3c856f0c1 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/ITerminalsView.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.interfaces; + +import org.eclipse.ui.IViewPart; + +/** + * Terminals view public interface. + */ +public interface ITerminalsView extends IViewPart { + + /** + * Switch to the empty page control. + */ + public void switchToEmptyPageControl(); + + /** + * Switch to the tab folder control. + */ + public void switchToTabFolderControl(); + + /** + * Returns the context help id associated with the terminal + * console view instance. + * + * @return The context help id or <code>null</code> if none is associated. + */ + public String getContextHelpId(); +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/IUIConstants.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/IUIConstants.java new file mode 100644 index 000000000..87fc1307c --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/IUIConstants.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.interfaces; + +/** + * Terminals common UI constants. + */ +public interface IUIConstants { + /** + * The view id of the terminals view. + */ + public static final String ID = "org.eclipse.tm.te.ui.terminals.TerminalsView"; //$NON-NLS-1$ + +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/ImageConsts.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/ImageConsts.java new file mode 100644 index 000000000..0934c14c8 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/interfaces/ImageConsts.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.interfaces; + +/** + * Target Explorer: Image registry constants. + */ +public interface ImageConsts { + /** + * The root directory where to load the images from, relative to + * the bundle directory. + */ + public final static String IMAGE_DIR_ROOT = "icons/"; //$NON-NLS-1$ + + /** + * The directory where to load colored local toolbar images from, + * relative to the image root directory. + */ + public final static String IMAGE_DIR_CLCL = "clcl16/"; //$NON-NLS-1$ + + /** + * The directory where to load disabled local toolbar images from, + * relative to the image root directory. + */ + public final static String IMAGE_DIR_DLCL = "dlcl16/"; //$NON-NLS-1$ + + /** + * The directory where to load enabled local toolbar images from, + * relative to the image root directory. + */ + public final static String IMAGE_DIR_ELCL = "elcl16/"; //$NON-NLS-1$ + + /** + * The directory where to load view related images from, relative to + * the image root directory. + */ + public final static String IMAGE_DIR_EVIEW = "eview16/"; //$NON-NLS-1$ + + /** + * The key to access the terminal consoles view image. + */ + public static final String VIEW_Terminals = "TerminalsView"; //$NON-NLS-1$ + + /** + * The key to access the scroll lock action image (enabled). + */ + public static final String ACTION_ScrollLock_Enabled = "ScrollLockAction_enabled"; //$NON-NLS-1$ + + /** + * The key to access the scroll lock action image (disabled). + */ + public static final String ACTION_ScrollLock_Disabled = "ScrollLockAction_disabled"; //$NON-NLS-1$ + + /** + * The key to access the scroll lock action image (hover). + */ + public static final String ACTION_ScrollLock_Hover = "ScrollLockAction_hover"; //$NON-NLS-1$ +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/internal/PreferenceInitializer.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/internal/PreferenceInitializer.java new file mode 100644 index 000000000..8cb06cc34 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/internal/PreferenceInitializer.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.internal; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.tm.te.runtime.preferences.ScopedEclipsePreferences; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.interfaces.IPreferenceKeys; + +/** + * Terminals default preferences initializer. + */ +public class PreferenceInitializer extends AbstractPreferenceInitializer { + + /** + * Constructor. + */ + public PreferenceInitializer() { + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences() + */ + @Override + public void initializeDefaultPreferences() { + ScopedEclipsePreferences prefs = UIPlugin.getScopedPreferences(); + + prefs.putDefaultBoolean(IPreferenceKeys.PREF_REMOVE_TERMINATED_TERMINALS, true); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/internal/SettingsStore.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/internal/SettingsStore.java new file mode 100644 index 000000000..250776a6d --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/internal/SettingsStore.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.internal; + +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.te.runtime.properties.PropertiesContainer; + +/** + * Simple default Terminal settings store implementation keeping the settings + * within memory. + */ +@SuppressWarnings("restriction") +public class SettingsStore extends PropertiesContainer implements ISettingsStore { + + /** + * Constructor. + */ + public SettingsStore() { + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore#get(java.lang.String, java.lang.String) + */ + @Override + public String get(String key, String defaultValue) { + String value = getStringProperty(key); + return value != null ? value : defaultValue; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore#get(java.lang.String) + */ + @Override + public String get(String key) { + return getStringProperty(key); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore#put(java.lang.String, java.lang.String) + */ + @Override + public void put(String key, String value) { + setProperty(key, value); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/manager/ConnectorManager.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/manager/ConnectorManager.java new file mode 100644 index 000000000..3fd6ffc32 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/manager/ConnectorManager.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.manager; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalConnectorExtension; +import org.eclipse.tm.internal.terminal.telnet.TelnetSettings; +import org.eclipse.tm.te.ui.terminals.internal.SettingsStore; +import org.eclipse.tm.te.ui.terminals.process.ProcessSettings; +import org.eclipse.tm.te.ui.terminals.streams.StreamsSettings; + + +/** + * Terminal connector manager implementation. + */ +@SuppressWarnings("restriction") +public class ConnectorManager { + + /* + * Thread save singleton instance creation. + */ + private static class LazyInstanceHolder { + public static ConnectorManager instance = new ConnectorManager(); + } + + /** + * Returns the singleton instance for the terminal connector manager. + */ + public static ConnectorManager getInstance() { + return LazyInstanceHolder.instance; + } + + /** + * Constructor. + */ + ConnectorManager() { + super(); + } + + /** + * Creates a terminal connector object based on the given telnet server attributes. + * <p> + * The telnet server attributes must contain 2 elements: + * <ul> + * <li>attributes[0] --> telnet server host name</li> + * <li>attributes[1] --> telnet port</li> + * <li>attributes[2] --> timeout</li> + * </ul> + * + * @param attributes The telnet server attributes. Must not be <code>null</code> and must have at least two elements. + * @param portOffset Offset to add to the port. + * + * @return The terminal connector object instance or <code>null</code>. + */ + public ITerminalConnector createTelnetConnector(String[] attributes) { + return createTelnetConnector(attributes, 0); + } + + /** + * Creates a terminal connector object based on the given telnet server attributes. + * <p> + * The telnet server attributes must contain at least 2 elements: + * <ul> + * <li>attributes[0] --> telnet server host name</li> + * <li>attributes[1] --> telnet port</li> + * <li>attributes[2] --> timeout (optional)</li> + * </ul> + * + * @param attributes The telnet server attributes. Must not be <code>null</code> and must have at least two elements. + * @param portOffset Offset to add to the port. + * + * @return The terminal connector object instance or <code>null</code>. + */ + public ITerminalConnector createTelnetConnector(String[] attributes, int portOffset) { + assert attributes != null && attributes.length >= 2; + + final String serverName = attributes[0]; + final String serverPort = Integer.toString(Integer.decode(attributes[1]).intValue() + portOffset); + final String timeout = attributes.length >= 3 ? attributes[2] : null; + + // Construct the terminal settings store + ISettingsStore store = new SettingsStore(); + + // Construct the telnet settings + TelnetSettings telnetSettings = new TelnetSettings(); + telnetSettings.setHost(serverName); + telnetSettings.setNetworkPort(serverPort); + if (timeout != null) { + telnetSettings.setTimeout(timeout); + } + // And save the settings to the store + telnetSettings.save(store); + + // Construct the terminal connector instance + ITerminalConnector connector = TerminalConnectorExtension.makeTerminalConnector("org.eclipse.tm.internal.terminal.telnet.TelnetConnector"); //$NON-NLS-1$ + if (connector != null) { + // Apply default settings + connector.makeSettingsPage(); + // And load the real settings + connector.load(store); + } + + return connector; + } + + /** + * Creates a terminal connector object based on the given process image + * and the corresponding process object. + * + * @param connectorTypeId Optional ID of the specific process connector implementation to use. + * If <code>null</code>, the default process connector will be used. + * @param image The process image path. Must not be <code>null</code>. + * @param arguments The process arguments or <code>null</code>. + * @param process The process. Must not be <code>null</code>. + * @param pty The pseudo terminal or <code>null</code>. + * @param localEcho <code>True</code> if the terminal widget local echo shall be enabled, <code>false</code> otherwise. + * + * @return The terminal connector object instance or <code>null</code>. + */ + public ITerminalConnector createProcessConnector(String connectorTypeId, final String image, final String arguments, final Process process, PTY pty, boolean localEcho) { + assert image != null || process != null; + + // Normalize the process connector id + if (connectorTypeId == null) connectorTypeId = "org.eclipse.tm.te.ui.terminals.ProcessConnector"; //$NON-NLS-1$ + + // Construct the terminal settings store + ISettingsStore store = new SettingsStore(); + + // Construct the process settings + ProcessSettings processSettings = new ProcessSettings(); + processSettings.setImage(image); + processSettings.setArguments(arguments); + processSettings.setProcess(process); + processSettings.setPTY(pty); + processSettings.setLocalEcho(localEcho); + // And save the settings to the store + processSettings.save(store); + + // Construct the terminal connector instance + ITerminalConnector connector = TerminalConnectorExtension.makeTerminalConnector(connectorTypeId); + if (connector != null) { + // Apply default settings + connector.makeSettingsPage(); + // And load the real settings + connector.load(store); + } + + return connector; + } + + /** + * Creates a terminal connector object based on the given stream objects. + * + * @param stdin The stdin stream or <code>null</code>. + * @param stdout The stdout stream or <code>null</code>. + * @param stderr The stderr stream or <code>null</code>. + * @param localEcho <code>True</code> if the terminal widget local echo shall be enabled, <code>false</code> otherwise. + * + * @return The terminal connector object instance or <code>null</code>. + */ + public ITerminalConnector createStreamsConnector(OutputStream stdin, InputStream stdout, InputStream stderr, boolean localEcho) { + + // Construct the terminal settings store + ISettingsStore store = new SettingsStore(); + + // Construct the streams settings + StreamsSettings streamsSettings = new StreamsSettings(); + streamsSettings.setStdinStream(stdin); + streamsSettings.setStdoutStream(stdout); + streamsSettings.setStderrStream(stderr); + streamsSettings.setLocalEcho(localEcho); + // And save the settings to the store + streamsSettings.save(store); + + // Construct the terminal connector instance + ITerminalConnector connector = TerminalConnectorExtension.makeTerminalConnector("org.eclipse.tm.te.ui.terminals.StreamsConnector"); //$NON-NLS-1$ + if (connector != null) { + // Apply default settings + connector.makeSettingsPage(); + // And load the real settings + connector.load(store); + } + + return connector; + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/manager/ConsoleManager.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/manager/ConsoleManager.java new file mode 100644 index 000000000..e2ee3c91d --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/manager/ConsoleManager.java @@ -0,0 +1,300 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.manager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.interfaces.IPreferenceKeys; +import org.eclipse.tm.te.ui.terminals.interfaces.ITerminalsView; +import org.eclipse.tm.te.ui.terminals.interfaces.IUIConstants; +import org.eclipse.tm.te.ui.terminals.tabs.TabFolderManager; +import org.eclipse.tm.te.ui.terminals.view.TerminalsView; +import org.eclipse.ui.IPerspectiveDescriptor; +import org.eclipse.ui.IPerspectiveListener; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PerspectiveAdapter; +import org.eclipse.ui.PlatformUI; + +/** + * Terminals console manager. + */ +@SuppressWarnings("restriction") +public class ConsoleManager { + // Reference to the perspective listener instance + private final IPerspectiveListener perspectiveListener; + + // Internal perspective listener implementation + static class TerminalConsoleManagerPerspectiveListener extends PerspectiveAdapter { + private final List<IViewReference> references = new ArrayList<IViewReference>(); + + /* (non-Javadoc) + * @see org.eclipse.ui.PerspectiveAdapter#perspectiveActivated(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor) + */ + @Override + public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) { + // If the old references list is empty, just return + if (references.isEmpty()) return; + // Create a copy of the old view references list + List<IViewReference> oldReferences = new ArrayList<IViewReference>(references); + + // Get the current list of view references + List<IViewReference> references = new ArrayList<IViewReference>(Arrays.asList(page.getViewReferences())); + for (IViewReference reference : oldReferences) { + if (references.contains(reference)) continue; + // Previous visible terminal console view reference, make visible again + try { + page.showView(reference.getId(), reference.getSecondaryId(), IWorkbenchPage.VIEW_VISIBLE); + } catch (PartInitException e) { /* Failure on part instantiation is ignored */ } + } + + } + + /* (non-Javadoc) + * @see org.eclipse.ui.PerspectiveAdapter#perspectivePreDeactivate(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor) + */ + @Override + public void perspectivePreDeactivate(IWorkbenchPage page, IPerspectiveDescriptor perspective) { + references.clear(); + for (IViewReference reference : page.getViewReferences()) { + IViewPart part = reference.getView(false); + if (part instanceof TerminalsView && !references.contains(reference)) { + references.add(reference); + } + } + } + } + + /* + * Thread save singleton instance creation. + */ + private static class LazyInstanceHolder { + public static ConsoleManager fInstance = new ConsoleManager(); + } + + /** + * Returns the singleton instance for the console manager. + */ + public static ConsoleManager getInstance() { + return LazyInstanceHolder.fInstance; + } + + /** + * Constructor. + */ + ConsoleManager() { + super(); + + // Attach the perspective listener + perspectiveListener = new TerminalConsoleManagerPerspectiveListener(); + if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) { + PlatformUI.getWorkbench().getActiveWorkbenchWindow().addPerspectiveListener(perspectiveListener); + } + } + + /** + * Returns the active workbench window page if the workbench is still running. + * + * @return The active workbench window page or <code>null</code> + */ + private final IWorkbenchPage getActiveWorkbenchPage() { + // To lookup the console view, the workbench must be still running + if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) { + return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + } + return null; + } + + /** + * Returns the console view if available within the active workbench window page. + * <p> + * <b>Note:</b> The method must be called within the UI thread. + * + * @param id The terminal console view id or <code>null</code> to show the default terminal console view. + * @return The console view instance if available or <code>null</code> otherwise. + */ + public ITerminalsView findConsoleView(String id) { + assert Display.findDisplay(Thread.currentThread()) != null; + + ITerminalsView view = null; + + // Get the active workbench page + IWorkbenchPage page = getActiveWorkbenchPage(); + if (page != null) { + // Look for the view + IViewPart part = page.findView(id != null ? id : IUIConstants.ID); + // Check the interface + if (part instanceof ITerminalsView) { + view = (ITerminalsView)part; + } + } + + return view; + } + + /** + * Show the terminal console view specified by the given id. + * <p> + * <b>Note:</b> The method must be called within the UI thread. + * + * @param id The terminal console view id or <code>null</code> to show the default terminal console view. + */ + public void showConsoleView(String id) { + assert Display.findDisplay(Thread.currentThread()) != null; + + // Get the active workbench page + IWorkbenchPage page = getActiveWorkbenchPage(); + if (page != null) { + try { + // show the view + IViewPart part = page.showView(id != null ? id : IUIConstants.ID); + // and force the view to the foreground + page.bringToTop(part); + } catch (PartInitException e) { + IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), + e.getLocalizedMessage(), e); + UIPlugin.getDefault().getLog().log(status); + } + } + } + + /** + * Bring the terminal console view, specified by the given id, to the top of the view stack. + * + * @param id The terminal console view id or <code>null</code> to show the default terminal console view. + * @param activate If <code>true</code> activate the console view. + */ + private void bringToTop(String id, boolean activate) { + // Get the active workbench page + IWorkbenchPage page = getActiveWorkbenchPage(); + if (page != null) { + // Look for the view + IViewPart part = page.findView(id != null ? id : IUIConstants.ID); + if (part != null) { + if (activate) { + page.activate(part); + } + else { + page.bringToTop(part); + } + } else if (activate) showConsoleView(id != null ? id : IUIConstants.ID); + } + } + + /** + * Opens the console with the given title and connector. + * <p> + * <b>Note:</b> The method must be called within the UI thread. + * + * @param id The terminal console view id or <code>null</code> to show the default terminal console view. + * @param title The console title. Must not be <code>null</code>. + * @param connector The terminal connector. Must not be <code>null</code>. + * @param data The custom terminal data node or <code>null</code>. + * @param activate If <code>true</code> activate the console view. + */ + public void openConsole(String id, String title, ITerminalConnector connector, Object data, boolean activate) { + assert title != null && connector != null; + assert Display.findDisplay(Thread.currentThread()) != null; + + // make the consoles view visible + bringToTop(id, activate); + + // Get the console view + ITerminalsView view = findConsoleView(id); + if (view == null) return; + + // Get the tab folder manager associated with the view + TabFolderManager manager = (TabFolderManager)view.getAdapter(TabFolderManager.class); + if (manager == null) return; + + // Lookup an existing console first + CTabItem item = findConsole(id, title, connector, data); + + // If no existing console exist -> Create the tab item + if (item == null) { + // If configured, check all existing tab items if they are associated + // with terminated consoles + if (UIPlugin.getScopedPreferences().getBoolean(IPreferenceKeys.PREF_REMOVE_TERMINATED_TERMINALS)) { + manager.removeTerminatedItems(); + } + + // Create a new tab item + item = manager.createTabItem(title, connector, data); + } + // If still null, something went wrong + if (item == null) { + return; + } + + // Make the item the active console + manager.bringToTop(item); + + // Show the tab folder page + view.switchToTabFolderControl(); + } + + /** + * Lookup a console with the given title and the given terminal connector. + * <p> + * <b>Note:</b> The method must be called within the UI thread. + * <b>Note:</b> The method will handle unified console titles itself. + * + * @param id The terminal console view id or <code>null</code> to show the default terminal console view. + * @param title The console title. Must not be <code>null</code>. + * @param connector The terminal connector. Must not be <code>null</code>. + * @param data The custom terminal data node or <code>null</code>. + * + * @return The corresponding console tab item or <code>null</code>. + */ + public CTabItem findConsole(String id, String title, ITerminalConnector connector, Object data) { + assert title != null && connector != null; + assert Display.findDisplay(Thread.currentThread()) != null; + + // Get the console view + ITerminalsView view = findConsoleView(id); + if (view == null) return null; + + // Get the tab folder manager associated with the view + TabFolderManager manager = (TabFolderManager)view.getAdapter(TabFolderManager.class); + if (manager == null) return null; + + return manager.findTabItem(title, connector, data); + } + + /** + * Close the console with the given title and the given terminal connector. + * <p> + * <b>Note:</b> The method must be called within the UI thread. + * <b>Note:</b> The method will handle unified console titles itself. + * + * @param title The console title. Must not be <code>null</code>. + * @param connector The terminal connector. Must not be <code>null</code>. + * @param data The custom terminal data node or <code>null</code>. + */ + public void closeConsole(String id, String title, ITerminalConnector connector, Object data) { + assert title != null && connector != null; + assert Display.findDisplay(Thread.currentThread()) != null; + + // Lookup the console + CTabItem console = findConsole(id, title, connector, data); + // If found, dispose the console + if (console != null) console.dispose(); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/nls/Messages.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/nls/Messages.java new file mode 100644 index 000000000..c5cf6b22f --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/nls/Messages.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.nls; + +import org.eclipse.osgi.util.NLS; + +/** + * Terminals plug-in externalized strings management. + */ +public class Messages extends NLS { + + // The plug-in resource bundle name + private static final String BUNDLE_NAME = "org.eclipse.tm.te.ui.terminals.nls.Messages"; //$NON-NLS-1$ + + /** + * Static constructor. + */ + static { + // Load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + // **** Declare externalized string id's down here ***** + + public static String AbstractAction_error_commandExecutionFailed; + + public static String TabTerminalListener_consoleTerminated; + + public static String ProcessSettingsPage_dialogTitle; + public static String ProcessSettingsPage_processImagePathSelectorControl_label; + public static String ProcessSettingsPage_processImagePathSelectorControl_button; + public static String ProcessSettingsPage_processArgumentsControl_label; + public static String ProcessSettingsPage_localEchoSelectorControl_label; + + public static String OutputStreamMonitor_error_readingFromStream; + + public static String InputStreamMonitor_error_writingToStream; + + public static String ProcessConnector_error_creatingProcess; +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/nls/Messages.properties new file mode 100644 index 000000000..992eeb80e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/nls/Messages.properties @@ -0,0 +1,20 @@ +# +# org.eclipse.tm.te.ui.terminals +# Externalized Strings. +# + +AbstractAction_error_commandExecutionFailed="Failed to execute command (id = {0}). Possibly caused by: {1} + +TabTerminalListener_consoleTerminated=<terminated> {0} + +ProcessSettingsPage_dialogTitle=Select Process Image +ProcessSettingsPage_processImagePathSelectorControl_label=Image Path: +ProcessSettingsPage_processImagePathSelectorControl_button=Browse +ProcessSettingsPage_processArgumentsControl_label=Arguments: +ProcessSettingsPage_localEchoSelectorControl_label=Local Echo + +OutputStreamMonitor_error_readingFromStream=Exception when reading from stream. Possibly caused by: {0} + +InputStreamMonitor_error_writingToStream=Exception when writing to stream. Possibly caused by: {0} + +ProcessConnector_error_creatingProcess=Exception when creating process. Possibly caused by: {0} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessConnector.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessConnector.java new file mode 100644 index 000000000..84f9df3c0 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessConnector.java @@ -0,0 +1,425 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.process; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StreamTokenizer; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.service.environment.Constants; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.nls.Messages; +import org.eclipse.tm.te.ui.terminals.streams.AbstractStreamsConnector; + +/** + * Process connector implementation. + */ +@SuppressWarnings("restriction") +public class ProcessConnector extends AbstractStreamsConnector { + // Reference to the process settings + private final ProcessSettings settings; + + // Reference to the PTY instance. + private PTY pty; + // Reference to the launched process instance. + private Process process; + // Reference to the process monitor + private ProcessMonitor monitor; + + // The terminal width and height. Initially unknown. + private int width = -1; + private int height = -1; + + /** + * Constructor. + */ + public ProcessConnector() { + this(new ProcessSettings()); + } + + /** + * Constructor. + * + * @param settings The process settings. Must not be <code>null</code> + */ + public ProcessConnector(ProcessSettings settings) { + super(); + + Assert.isNotNull(settings); + this.settings = settings; + } + + /** + * Returns the process object or <code>null</code> if the + * connector is connector. + * + * @return The process object or <code>null</code>. + */ + public Process getProcess() { + return process; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#connect(org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl) + */ + @Override + public void connect(ITerminalControl control) { + assert control != null; + super.connect(control); + + pty = null; + width = -1; + height = -1; + + try { + // Try to determine process and PTY instance from the process settings + process = settings.getProcess(); + pty = settings.getPTY(); + + // No process -> create PTY on supported platforms and execute + // process image. + if (process == null) { + if (PTY.isSupported()) { + try { + pty = new PTY(false); + } catch (IOException e) { + // PTY not supported on windows + } + } + + // Build up the command + StringBuilder command = new StringBuilder(settings.getImage()); + String arguments = settings.getArguments(); + if (arguments != null && !"".equals(arguments.trim())) { //$NON-NLS-1$ + // Append to the command now + command.append(" "); //$NON-NLS-1$ + command.append(arguments.trim()); + } + + if (pty != null) { + // A PTY is available -> can use the ProcessFactory. + + // Tokenize the command (ProcessFactory takes an array) + StreamTokenizer st = new StreamTokenizer(new StringReader(command.toString())); + st.resetSyntax(); + st.whitespaceChars(0, 32); + st.whitespaceChars(0xa0, 0xa0); + st.wordChars(33, 255); + st.quoteChar('"'); + st.quoteChar('\''); + + List<String> argv = new ArrayList<String>(); + int ttype = st.nextToken(); + while (ttype != StreamTokenizer.TT_EOF) { + argv.add(st.sval); + ttype = st.nextToken(); + } + + // Execute the process + process = ProcessFactory.getFactory().exec(argv.toArray(new String[argv.size()]), getProcessEnvironment(), null, pty); + } else { + // No PTY -> just execute via the standard Java Runtime implementation. + process = Runtime.getRuntime().exec(command.toString()); + } + } + + // connect the streams + connectStreams(control, process.getOutputStream(), process.getInputStream(), (pty == null ? process.getErrorStream() : null)); + + // Set the terminal control state to CONNECTED + control.setState(TerminalState.CONNECTED); + + // Create the process monitor + monitor = new ProcessMonitor(this); + monitor.startMonitoring(); + } catch (Exception e) { + IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), + NLS.bind(Messages.ProcessConnector_error_creatingProcess, e.getLocalizedMessage()), e); + UIPlugin.getDefault().getLog().log(status); + } + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#isLocalEcho() + */ + @Override + public boolean isLocalEcho() { + return settings.isLocalEcho(); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#doDisconnect() + */ + @Override + public void doDisconnect() { + // Dispose the process + if (process != null) { process.destroy(); process = null; } + + // Dispose the streams + super.doDisconnect(); + + // Set the terminal control state to CLOSED. + fControl.setState(TerminalState.CLOSED); + } + + // ***** Process Connector settings handling ***** + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#makeSettingsPage() + */ + @Override + public ISettingsPage makeSettingsPage() { + return new ProcessSettingsPage(settings); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#getSettingsSummary() + */ + @Override + public String getSettingsSummary() { + return settings.getImage() != null ? settings.getImage() : ""; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#load(org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore) + */ + @Override + public void load(ISettingsStore store) { + settings.load(store); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#save(org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore) + */ + @Override + public void save(ISettingsStore store) { + settings.save(store); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#setTerminalSize(int, int) + */ + @Override + public void setTerminalSize(int newWidth, int newHeight) { + if (width != newWidth || height != newHeight) { + width = newWidth; + height = newHeight; + if (pty != null) { + pty.setTerminalSize(newWidth, newHeight); + } + } + } + + // ***** Process Environment Handling ***** + + // Reference to the monitor to lock if determining the native environment + private final static Object ENV_GET_MONITOR = new Object(); + + // Reference to the native environment once retrieved + private static Map<String, String> nativeEnvironment = null; + // Reference to the native environment with the case of the variable names preserved + private static Map<String, String> nativeEnvironmentCasePreserved = null; + + /** + * Returns the specific environment to set for the process to be launched. + * + * @return The process environment. + */ + private static String[] getProcessEnvironment() { + Map<String, String> env = getNativeEnvironment(); + + env.put("TERM", "vt100"); //$NON-NLS-1$ //$NON-NLS-2$ + + Iterator<Map.Entry<String, String>> iter = env.entrySet().iterator(); + List<String> strings = new ArrayList<String>(env.size()); + StringBuffer buffer = null; + while (iter.hasNext()) { + Map.Entry<String, String>entry = iter.next(); + buffer = new StringBuffer(entry.getKey()); + buffer.append('=').append(entry.getValue()); + strings.add(buffer.toString()); + } + + return strings.toArray(new String[strings.size()]); + } + + /** + * Determine the native environment, but returns all environment variable + * names in upper case. + * + * @return The native environment with upper case variable names, or an empty map. + */ + private static Map<String, String> getNativeEnvironment() { + synchronized (ENV_GET_MONITOR) { + if (nativeEnvironment == null) { + Map<String, String> casePreserved = getNativeEnvironmentCasePreserved(); + if (Platform.getOS().equals(org.eclipse.osgi.service.environment.Constants.OS_WIN32)) { + nativeEnvironment = new HashMap<String, String>(); + Iterator<Map.Entry<String, String>> entries = casePreserved.entrySet().iterator(); + while (entries.hasNext()) { + Map.Entry<String, String> entry = entries.next(); + nativeEnvironment.put(entry.getKey().toUpperCase(), entry.getValue()); + } + } else { + nativeEnvironment = new HashMap<String, String>(casePreserved); + } + } + return new HashMap<String, String>(nativeEnvironment); + } + } + + /** + * Determine the native environment. + * + * @return The native environment, or an empty map. + */ + private static Map<String, String> getNativeEnvironmentCasePreserved() { + synchronized (ENV_GET_MONITOR) { + if (nativeEnvironmentCasePreserved == null) { + nativeEnvironmentCasePreserved= new HashMap<String, String>(); + cacheNativeEnvironment(nativeEnvironmentCasePreserved); + } + return new HashMap<String, String>(nativeEnvironmentCasePreserved); + } + } + + /** + * Query the native environment and store it to the specified cache. + * + * @param cache The environment cache. Must not be <code>null</code>. + */ + private static void cacheNativeEnvironment(Map<String, String> cache) { + Assert.isNotNull(cache); + + try { + String nativeCommand = null; + boolean isWin9xME = false; // see bug 50567 + String fileName = null; + if (Platform.getOS().equals(Constants.OS_WIN32)) { + String osName = System.getProperty("os.name"); //$NON-NLS-1$ + isWin9xME = osName != null && (osName.startsWith("Windows 9") || osName.startsWith("Windows ME")); //$NON-NLS-1$ //$NON-NLS-2$ + if (isWin9xME) { + // Win 95, 98, and ME + // SET might not return therefore we pipe into a file + IPath stateLocation = UIPlugin.getDefault().getStateLocation(); + fileName = stateLocation.toOSString() + File.separator + "env.txt"; //$NON-NLS-1$ + nativeCommand = "command.com /C set > " + fileName; //$NON-NLS-1$ + } else { + // Win NT, 2K, XP + nativeCommand = "cmd.exe /C set"; //$NON-NLS-1$ + } + } else if (!Platform.getOS().equals(Constants.OS_UNKNOWN)) { + nativeCommand = "env"; //$NON-NLS-1$ + } + if (nativeCommand == null) { return; } + Process process = Runtime.getRuntime().exec(nativeCommand); + if (isWin9xME) { + // read piped data on Win 95, 98, and ME + Properties p = new Properties(); + File file = new File(fileName); + InputStream stream = new BufferedInputStream(new FileInputStream(file)); + p.load(stream); + stream.close(); + if (!file.delete()) { + file.deleteOnExit(); // if delete() fails try again on VM close + } + for (Enumeration<Object> enumeration = p.keys(); enumeration.hasMoreElements();) { + // Win32's environment variables are case insensitive. Put everything + // to upper case so that (for example) the "PATH" variable will match + // "pAtH" correctly on Windows. + String key = (String)enumeration.nextElement(); + cache.put(key, (String)p.get(key)); + } + } else { + // read process directly on other platforms + // we need to parse out matching '{' and '}' for function declarations in .bash environments + // pattern is [function name]=() { and we must find the '}' on its own line with no trailing ';' + InputStream stream = process.getInputStream(); + InputStreamReader isreader = new InputStreamReader(stream); + BufferedReader reader = new BufferedReader(isreader); + String line = reader.readLine(); + String key = null; + String value = null; + while (line != null) { + int func = line.indexOf("=()"); //$NON-NLS-1$ + if (func > 0) { + key = line.substring(0, func); + // scan until we find the closing '}' with no following chars + value = line.substring(func + 1); + while (line != null && !line.equals("}")) { //$NON-NLS-1$ + line = reader.readLine(); + if (line != null) { + value += line; + } + } + line = reader.readLine(); + } else { + int separator = line.indexOf('='); + if (separator > 0) { + key = line.substring(0, separator); + value = line.substring(separator + 1); + line = reader.readLine(); + if (line != null) { + // this line has a '=' read ahead to check next line for '=', might be broken on more + // than one line + separator = line.indexOf('='); + while (separator < 0) { + value += line.trim(); + line = reader.readLine(); + if (line == null) { + // if next line read is the end of the file quit the loop + break; + } + separator = line.indexOf('='); + } + } + } + } + if (key != null) { + cache.put(key, value); + key = null; + value = null; + } else { + line = reader.readLine(); + } + } + reader.close(); + } + } catch (IOException e) { + // Native environment-fetching code failed. + // This can easily happen and is not useful to log. + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessMonitor.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessMonitor.java new file mode 100644 index 000000000..98c9d6073 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessMonitor.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.process; + +import org.eclipse.core.runtime.Assert; + + +/** + * Process monitor implementation. + */ +public class ProcessMonitor { + // Reference to the parent process connector + private final ProcessConnector processConnector; + // Reference to the monitored process + private final Process process; + // Reference to the monitor thread + private Thread thread; + // Flag to mark the monitor disposed + private boolean disposed; + + + /** + * Constructor. + * + * @param processConnector The parent process connector. Must not be <code>null</code>. + */ + public ProcessMonitor(ProcessConnector processConnector) { + super(); + + Assert.isNotNull(processConnector); + this.processConnector = processConnector; + + // Query the monitored process for easier access + this.process = processConnector.getProcess(); + } + + /** + * Dispose the process monitor. + */ + public void dispose() { + // Set the disposed status + disposed = true; + // Not initialized -> return immediately + if (thread == null) return; + + // Copy the reference + final Thread oldThread = thread; + // Unlink the monitor from the thread + thread = null; + // And interrupt the writer thread + oldThread.interrupt(); + } + + /** + * Starts the terminal output stream monitor. + */ + public void startMonitoring() { + // If already initialized -> return immediately + if (thread != null) return; + + // Create a new runnable which is constantly reading from the stream + Runnable runnable = new Runnable() { + @Override + public void run() { + monitorProcess(); + } + }; + + // Create the monitor thread + thread = new Thread(runnable, "Terminal Process Monitor Thread"); //$NON-NLS-1$ + + // Configure the monitor thread + thread.setDaemon(true); + + // Start the processing + thread.start(); + } + + /** + * Monitors the associated system process, waiting for it to terminate, + * and notifies the associated process monitor's. + */ + @SuppressWarnings("restriction") + public void monitorProcess() { + // If already disposed -> return immediately + if (disposed) return; + + try { + // Wait for the monitored process to terminate + process.waitFor(); + } catch (InterruptedException ie) { + // clear interrupted state + Thread.interrupted(); + } finally { + // Dispose the parent process connector + processConnector.disconnect(); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessSettings.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessSettings.java new file mode 100644 index 000000000..5976fdfed --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessSettings.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.process; + +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.core.runtime.Assert; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.te.runtime.interfaces.properties.IPropertiesContainer; + +/** + * Process connector settings implementation. + */ +@SuppressWarnings("restriction") +public class ProcessSettings { + // Reference to the process image + private String image; + // Reference to the process arguments (space separated string) + private String arguments; + // Reference to the process object + private Process process; + // Reference to the pseudo terminal object + private PTY pty; + // Flag to control the local echo (defaults to true if + // the PTY is not supported on the current host platform) + private boolean localEcho = !PTY.isSupported(); + + /** + * Sets the process image. + * + * @param image The process image or <code>null</code>. + */ + public void setImage(String image) { + this.image = image; + } + + /** + * Returns the process image. + * + * @return The process image or <code>null</code>. + */ + public String getImage() { + return image; + } + + /** + * Sets the process arguments. + * <p> + * The arguments are space separated. The caller is responsible for + * correct quoting. + * + * @param arguments The process arguments or <code>null</code>. + */ + public void setArguments(String arguments) { + this.arguments = arguments; + } + + /** + * Returns the process arguments. + * + * @return The process arguments as space separated list or <code>null</code>. + */ + public String getArguments() { + return arguments; + } + + /** + * Sets the process object. + * + * @param image The process object or <code>null</code>. + */ + public void setProcess(Process process) { + this.process = process; + } + + /** + * Returns the process object. + * + * @return The process object or <code>null</code>. + */ + public Process getProcess() { + return process; + } + + /** + * Sets the pseudo terminal object. + * + * @param pty The pseudo terminal or <code>null</code>. + */ + public void setPTY(PTY pty) { + this.pty = pty; + // If the PTY is set to "null", the local echo will be set to "true" + if (pty == null) setLocalEcho(true); + } + + /** + * Returns the pseudo terminal object. + * + * @return The pseudo terminal or <code>null</code>. + */ + public PTY getPTY() { + return pty; + } + + /** + * Sets if the process requires a local echo from the + * terminal widget. + * + * @param value Specify <code>true</code> to enable the local echo, <code>false</code> otherwise. + */ + public void setLocalEcho(boolean value) { + this.localEcho = value; + } + + /** + * Returns <code>true</code> if the process requires a local echo + * from the terminal widget. + * + * @return <code>True</code> if local echo is enabled, <code>false</code> otherwise. + */ + public boolean isLocalEcho() { + return localEcho; + } + + /** + * Loads the process settings from the given settings store. + * + * @param store The settings store. Must not be <code>null</code>. + */ + public void load(ISettingsStore store) { + Assert.isNotNull(store); + image = store.get("Path", null);//$NON-NLS-1$ + arguments = store.get("Arguments", null); //$NON-NLS-1$ + localEcho = Boolean.parseBoolean(store.get("LocalEcho", Boolean.FALSE.toString())); //$NON-NLS-1$ + if (store instanceof IPropertiesContainer) { + process = (Process)((IPropertiesContainer)store).getProperty("Process"); //$NON-NLS-1$ + pty = (PTY)((IPropertiesContainer)store).getProperty("PTY"); //$NON-NLS-1$ + } + } + + /** + * Saves the process settings to the given settings store. + * + * @param store The settings store. Must not be <code>null</code>. + */ + public void save(ISettingsStore store) { + Assert.isNotNull(store); + store.put("Path", image);//$NON-NLS-1$ + store.put("Arguments", arguments); //$NON-NLS-1$ + store.put("LocalEcho", Boolean.toString(localEcho)); //$NON-NLS-1$ + if (store instanceof IPropertiesContainer) { + ((IPropertiesContainer)store).setProperty("Process", process); //$NON-NLS-1$ + ((IPropertiesContainer)store).setProperty("PTY", pty); //$NON-NLS-1$ + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessSettingsPage.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessSettingsPage.java new file mode 100644 index 000000000..af7a4e60b --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/process/ProcessSettingsPage.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.process; + +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage; +import org.eclipse.tm.te.ui.swt.SWTControlUtil; +import org.eclipse.tm.te.ui.terminals.nls.Messages; +import org.eclipse.ui.PlatformUI; +import org.osgi.framework.Bundle; + +/** + * Process connector settings page implementation. + */ +@SuppressWarnings("restriction") +public class ProcessSettingsPage implements ISettingsPage { + private Text processImageSelectorControl; + private Button processImageSelectorControlButton; + private Text processArgumentsControl; + private Button localEchoSelectorControl; + + private final ProcessSettings settings; + + /** + * Constructor. + * + * @param settings + */ + public ProcessSettingsPage(ProcessSettings settings) { + super(); + + Assert.isNotNull(settings); + this.settings = settings; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage#createControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout()); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + + // The entry fields shall be properly aligned + Composite panel = new Composite(composite, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + layout.marginWidth = 0; layout.marginHeight = 0; + panel.setLayout(layout); + panel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create the process image selector control + Label label = new Label(panel, SWT.HORIZONTAL); + label.setText(Messages.ProcessSettingsPage_processImagePathSelectorControl_label); + + // Text field and browse button are aligned it their own panel + Composite innerPanel = new Composite(panel, SWT.NONE); + layout = new GridLayout(2, false); + layout.marginWidth = 0; layout.marginHeight = 0; + innerPanel.setLayout(layout); + innerPanel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + processImageSelectorControl = new Text(innerPanel, SWT.SINGLE | SWT.BORDER); + processImageSelectorControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + processImageSelectorControlButton = new Button(innerPanel, SWT.PUSH); + processImageSelectorControlButton.setText(Messages.ProcessSettingsPage_processImagePathSelectorControl_button); + processImageSelectorControlButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onBrowseButtonSelected(e); + } + }); + + // Create the process arguments control + label = new Label(panel, SWT.HORIZONTAL); + label.setText(Messages.ProcessSettingsPage_processArgumentsControl_label); + + processArgumentsControl = new Text(panel, SWT.SINGLE | SWT.BORDER); + processArgumentsControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create the local echo check box + localEchoSelectorControl = new Button(composite, SWT.CHECK); + localEchoSelectorControl.setText(Messages.ProcessSettingsPage_localEchoSelectorControl_label); + localEchoSelectorControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + localEchoSelectorControl.setSelection(!PTY.isSupported()); + + // Initialize the control content + loadSettings(); + } + + /** + * Called once the user pressed the browse button. + * + * @param e The selection event or <code>null</code>. + */ + protected void onBrowseButtonSelected(SelectionEvent e) { + // Determine the shell + Shell shell = e != null ? e.widget.getDisplay().getActiveShell() : PlatformUI.getWorkbench().getDisplay().getActiveShell(); + + // create a standard file dialog + FileDialog dialog = new FileDialog(shell, SWT.OPEN); + dialog.setText(Messages.ProcessSettingsPage_dialogTitle); + + // the dialog should open within the directory of the currently selected + // file. If no file has been currently selected, it should open within the + // last browsed directory. + String selectedFile = SWTControlUtil.getText(processImageSelectorControl); + if (selectedFile != null && selectedFile.trim().length() > 0) { + IPath filePath = new Path(selectedFile); + // If the selected file points to an directory, use the directory as is + IPath filterPath = filePath.toFile().isDirectory() ? filePath : filePath.removeLastSegments(1); + String filterFileName = filePath.toFile().isDirectory() || !filePath.toFile().exists() ? null : filePath.lastSegment(); + + if (!filterPath.isEmpty()) { + dialog.setFilterPath(filterPath.toString()); + } + if (filterFileName != null) { + dialog.setFileName(filterFileName); + } + } else { + Bundle bundle = Platform.getBundle("org.eclipse.core.resources"); //$NON-NLS-1$ + if (bundle != null && (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.ACTIVE)) { + dialog.setFilterPath(org.eclipse.core.resources.ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString()); + } + } + + // Open the dialog + selectedFile = dialog.open(); + if (selectedFile != null) { + SWTControlUtil.setText(processImageSelectorControl, selectedFile); + } + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage#saveSettings() + */ + @Override + public void saveSettings() { + settings.setImage(SWTControlUtil.getText(processImageSelectorControl)); + settings.setArguments(SWTControlUtil.getText(processArgumentsControl)); + settings.setLocalEcho(SWTControlUtil.getSelection(localEchoSelectorControl)); + settings.setProcess(null); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage#loadSettings() + */ + @Override + public void loadSettings() { + SWTControlUtil.setText(processImageSelectorControl, settings.getImage()); + SWTControlUtil.setText(processArgumentsControl, settings.getArguments()); + SWTControlUtil.setSelection(localEchoSelectorControl, settings.isLocalEcho()); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage#validateSettings() + */ + @Override + public boolean validateSettings() { + // The settings are considered valid if the selected process image can be read. + String selectedFile = SWTControlUtil.getText(processImageSelectorControl); + return selectedFile != null && !"".equals(selectedFile.trim()) && new Path(selectedFile).toFile().canRead(); //$NON-NLS-1$ + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/AbstractStreamsConnector.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/AbstractStreamsConnector.java new file mode 100644 index 000000000..18e14fcfc --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/AbstractStreamsConnector.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.streams; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl; +import org.eclipse.ui.services.IDisposable; + +/** + * Streams connector implementation. + */ +@SuppressWarnings("restriction") +public abstract class AbstractStreamsConnector extends TerminalConnectorImpl { + // Reference to the stdin monitor + private InputStreamMonitor stdInMonitor; + // Reference to the stdout monitor + private OutputStreamMonitor stdOutMonitor; + // Reference to the stderr monitor + private OutputStreamMonitor stdErrMonitor; + + /** + * Connect the given streams. The streams connector will wrap each stream + * with a corresponding terminal stream monitor. + * + * @param terminalControl The terminal control. Must not be <code>null</code>. + * @param stdin The stdin stream or <code>null</code>. + * @param stdout The stdout stream or <code>null</code>. + * @param stderr The stderr stream or <code>null</code>. + */ + protected void connectStreams(ITerminalControl terminalControl, OutputStream stdin, InputStream stdout, InputStream stderr) { + assert terminalControl != null; + + // Create the input stream monitor + if (stdin != null) { + stdInMonitor = createStdInMonitor(terminalControl, stdin); + // Register the connector if it implements IDisposable and stdout/stderr are not monitored + if (stdout == null && stderr == null && this instanceof IDisposable) stdInMonitor.addDisposable((IDisposable)this); + // Start the monitoring + stdInMonitor.startMonitoring(); + } + + // Create the output stream monitor + if (stdout != null) { + stdOutMonitor = createStdOutMonitor(terminalControl, stdout); + // Register the connector if it implements IDisposable + if (this instanceof IDisposable) stdOutMonitor.addDisposable((IDisposable)this); + // Start the monitoring + stdOutMonitor.startMonitoring(); + } + + // Create the error stream monitor + if (stderr != null) { + stdErrMonitor = createStdErrMonitor(terminalControl, stderr); + // Register the connector if it implements IDisposable and stdout is not monitored + if (stdout == null && this instanceof IDisposable) stdErrMonitor.addDisposable((IDisposable)this); + // Start the monitoring + stdErrMonitor.startMonitoring(); + } + } + + /** + * Creates an stdin monitor for the given terminal control and stdin stream. + * Subclasses may override to create a specialized stream monitor. + * + * @param terminalControl The terminal control. Must not be <code>null</code>. + * @param stdin The stdin stream or <code>null</code>. + * @return input stream monitor + */ + protected InputStreamMonitor createStdInMonitor(ITerminalControl terminalControl, OutputStream stdin) { + return new InputStreamMonitor(terminalControl, stdin); + } + + /** + * Creates an stdout monitor for the given terminal control and stdout stream. + * Subclasses may override to create a specialized stream monitor. + * + * @param terminalControl The terminal control. Must not be <code>null</code>. + * @param stdout The stdout stream or <code>null</code>. + * @return output stream monitor + */ + protected OutputStreamMonitor createStdOutMonitor(ITerminalControl terminalControl, InputStream stdout) { + return new OutputStreamMonitor(terminalControl, stdout); + } + + /** + * Creates an stderr monitor for the given terminal control and stderr stream. + * Subclasses may override to create a specialized stream monitor. + * + * @param terminalControl The terminal control. Must not be <code>null</code>. + * @param stderr The stderr stream or <code>null</code>. + * @return output stream monitor + */ + protected OutputStreamMonitor createStdErrMonitor(ITerminalControl terminalControl, InputStream stderr) { + return new OutputStreamMonitor(terminalControl, stderr); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#doDisconnect() + */ + @Override + protected void doDisconnect() { + // Dispose the streams + if (stdInMonitor != null) { stdInMonitor.dispose(); stdInMonitor = null; } + if (stdOutMonitor != null) { stdOutMonitor.dispose(); stdOutMonitor = null; } + if (stdErrMonitor != null) { stdErrMonitor.dispose(); stdErrMonitor = null; } + + super.doDisconnect(); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#getTerminalToRemoteStream() + */ + @Override + public OutputStream getTerminalToRemoteStream() { + return stdInMonitor; + } + +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/InputStreamMonitor.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/InputStreamMonitor.java new file mode 100644 index 000000000..d47c53319 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/InputStreamMonitor.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.streams; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.nls.Messages; +import org.eclipse.ui.services.IDisposable; + +/** + * Input stream monitor implementation. + * <p> + * <b>Note:</b> The input is coming <i>from</i> the terminal. Therefore, the input + * stream monitor is attached to the stdin stream of the monitored (remote) process. + */ +@SuppressWarnings("restriction") +public class InputStreamMonitor extends OutputStream implements IDisposable { + // Reference to the parent terminal control + @SuppressWarnings("unused") + private final ITerminalControl terminalControl; + + // Reference to the monitored (output) stream + private final OutputStream stream; + + // Reference to the thread writing the stream + private Thread thread; + + // Flag to mark the monitor disposed. When disposed, + // no further data is written from the monitored stream. + private boolean disposed; + + // A list of object to dispose if this monitor is disposed + private final List<IDisposable> disposables = new ArrayList<IDisposable>(); + + // Queue to buffer the data to write to the output stream + private final Queue<byte[]> queue = new LinkedList<byte[]>(); + + /** + * Constructor. + * + * @param terminalControl The parent terminal control. Must not be <code>null</code>. + * @param stream The stream. Must not be <code>null</code>. + */ + public InputStreamMonitor(ITerminalControl terminalControl, OutputStream stream) { + super(); + + Assert.isNotNull(terminalControl); + this.terminalControl = terminalControl; + Assert.isNotNull(stream); + this.stream = stream; + } + + /** + * Adds the given disposable object to the list. The method will do nothing + * if either the disposable object is already part of the list or the monitor + * is disposed. + * + * @param disposable The disposable object. Must not be <code>null</code>. + */ + public final void addDisposable(IDisposable disposable) { + assert disposable != null; + if (!disposed && !disposables.contains(disposable)) disposables.add(disposable); + } + + /** + * Removes the disposable object from the list. + * + * @param disposable The disposable object. Must not be <code>null</code>. + */ + public final void removeDisposable(IDisposable disposable) { + assert disposable != null; + disposables.remove(disposable); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.services.IDisposable#dispose() + */ + @Override + public void dispose() { + // If already disposed --> return immediately + if (disposed) return; + + // Mark the monitor disposed + disposed = true; + + // Close the stream (ignore exceptions on close) + try { stream.close(); } catch (IOException e) { /* ignored on purpose */ } + // And interrupt the thread + close(); + + // Dispose all registered disposable objects + for (IDisposable disposable : disposables) disposable.dispose(); + // Clear the list + disposables.clear(); + } + + /** + * Close the terminal input stream monitor. + */ + @Override + public void close() { + // Not initialized -> return immediately + if (thread == null) return; + + // Copy the reference + final Thread oldThread = thread; + // Unlink the monitor from the thread + thread = null; + // And interrupt the writer thread + oldThread.interrupt(); + } + + /** + * Starts the terminal output stream monitor. + */ + public void startMonitoring() { + // If already initialized -> return immediately + if (thread != null) return; + + // Create a new runnable which is constantly reading from the stream + Runnable runnable = new Runnable() { + @Override + public void run() { + writeStream(); + } + }; + + // Create the writer thread + thread = new Thread(runnable, "Terminal Input Stream Monitor Thread"); //$NON-NLS-1$ + + // Configure the writer thread + thread.setDaemon(true); + + // Start the processing + thread.start(); + } + + + /** + * Reads from the queue and writes the read content to the stream. + */ + protected void writeStream() { + // Read from the queue and write to the stream until disposed + while (thread != null && !disposed) { + // If the queue is empty, wait until notified + if (queue.isEmpty()) { + synchronized(queue) { + try { queue.wait(); } catch (InterruptedException e) { /* ignored on purpose */ } + } + } + + // If the queue is not empty, take the first element + // and write the data to the stream + while (!queue.isEmpty() && !disposed) { + // Retrieves the queue head (is null if queue is empty (should never happen)) + byte[] data = queue.poll(); + if (data != null) { + try { + // Write the data to the stream + stream.write(data); + // Flush the stream immediately + stream.flush(); + } catch (IOException e) { + // IOException received. If this is happening when already disposed -> ignore + if (!disposed) { + IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), + NLS.bind(Messages.InputStreamMonitor_error_writingToStream, e.getLocalizedMessage()), e); + UIPlugin.getDefault().getLog().log(status); + } + } + } + } + } + + // Dispose the stream + dispose(); + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(int b) throws IOException { + synchronized(queue) { + queue.add(new byte[] { (byte)b }); + queue.notifyAll(); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + // Make sure that the written block is not interlaced with other input. + synchronized(queue) { + super.write(b, off, len); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/OutputStreamMonitor.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/OutputStreamMonitor.java new file mode 100644 index 000000000..ea8ab62fc --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/OutputStreamMonitor.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.streams; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.nls.Messages; +import org.eclipse.ui.services.IDisposable; + +/** + * Output stream monitor implementation. + * <p> + * <b>Note:</b> The output is going <i>to</i> the terminal. Therefore, the output + * stream monitor is attached to the stdout and/or stderr stream of the monitored + * (remote) process. + */ +@SuppressWarnings("restriction") +public class OutputStreamMonitor implements IDisposable { + // The default buffer size to use + private static final int BUFFER_SIZE = 8192; + + // Reference to the parent terminal control + private final ITerminalControl terminalControl; + + // Reference to the monitored (input) stream + private final InputStream stream; + + // Reference to the thread reading the stream + private Thread thread; + + // Flag to mark the monitor disposed. When disposed, + // no further data is read from the monitored stream. + private boolean disposed; + + // A list of object to dispose if this monitor is disposed + private final List<IDisposable> disposables = new ArrayList<IDisposable>(); + + /** + * Constructor. + * + * @param terminalControl The parent terminal control. Must not be <code>null</code>. + * @param stream The stream. Must not be <code>null</code>. + */ + public OutputStreamMonitor(ITerminalControl terminalControl, InputStream stream) { + super(); + + Assert.isNotNull(terminalControl); + this.terminalControl = terminalControl; + Assert.isNotNull(stream); + this.stream = new BufferedInputStream(stream, BUFFER_SIZE); + } + + /** + * Adds the given disposable object to the list. The method will do nothing + * if either the disposable object is already part of the list or the monitor + * is disposed. + * + * @param disposable The disposable object. Must not be <code>null</code>. + */ + public final void addDisposable(IDisposable disposable) { + assert disposable != null; + if (!disposed && !disposables.contains(disposable)) disposables.add(disposable); + } + + /** + * Removes the disposable object from the list. + * + * @param disposable The disposable object. Must not be <code>null</code>. + */ + public final void removeDisposable(IDisposable disposable) { + assert disposable != null; + disposables.remove(disposable); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.services.IDisposable#dispose() + */ + @Override + public void dispose() { + // If already disposed --> return immediately + if (disposed) return; + + // Mark the monitor disposed + disposed = true; + + // Close the stream (ignore exceptions on close) + try { stream.close(); } catch (IOException e) { /* ignored on purpose */ } + + // Dispose all registered disposable objects + for (IDisposable disposable : disposables) disposable.dispose(); + // Clear the list + disposables.clear(); + } + + /** + * Starts the terminal output stream monitor. + */ + protected void startMonitoring() { + // If already initialized -> return immediately + if (thread != null) return; + + // Create a new runnable which is constantly reading from the stream + Runnable runnable = new Runnable() { + @Override + public void run() { + readStream(); + } + }; + + // Create the reader thread + thread = new Thread(runnable, "Terminal Output Stream Monitor Thread"); //$NON-NLS-1$ + + // Configure the reader thread + thread.setDaemon(true); + thread.setPriority(Thread.MIN_PRIORITY); + + // Start the processing + thread.start(); + } + + /** + * Returns the terminal control that this stream monitor is associated with. + */ + protected ITerminalControl getTerminalControl() { + return terminalControl; + } + + /** + * Reads from the output stream and write the read content + * to the terminal control output stream. + */ + void readStream() { + // Creates the read buffer + byte[] readBuffer = new byte[BUFFER_SIZE]; + + // We need to maintain UI responsiveness but still stream the content + // to the terminal control fast. Put the thread to a short sleep each second. + long sleepMarker = System.currentTimeMillis(); + + // Read from the stream until EOS is reached or the + // monitor is marked disposed. + int read = 0; + while (read >= 0 && !disposed) { + try { + // Read from the stream + read = stream.read(readBuffer); + // If some data has been read, append to the terminal + // control output stream + if (read > 0) { + // Allow for post processing the read content before appending + byte[] processedReadBuffer = onContentReadFromStream(readBuffer, read); + if (processedReadBuffer != readBuffer) { + read = processedReadBuffer.length; + } + terminalControl.getRemoteToTerminalOutputStream().write(processedReadBuffer, 0, read); + } + } catch (IOException e) { + // IOException received. If this is happening when already disposed -> ignore + if (!disposed) { + IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), + NLS.bind(Messages.OutputStreamMonitor_error_readingFromStream, e.getLocalizedMessage()), e); + UIPlugin.getDefault().getLog().log(status); + } + break; + } catch (NullPointerException e) { + // killing the stream monitor while reading can cause an NPE + // when reading from the stream + if (!disposed && thread != null) { + IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), + NLS.bind(Messages.OutputStreamMonitor_error_readingFromStream, e.getLocalizedMessage()), e); + UIPlugin.getDefault().getLog().log(status); + } + break; + } + + // See above -> Thread will go to sleep each second + if (System.currentTimeMillis() - sleepMarker > 1000) { + sleepMarker = System.currentTimeMillis(); + try { Thread.sleep(1); } catch (InterruptedException e) { /* ignored on purpose */ } + } + } + + // Dispose ourself + dispose(); + } + + /** + * Allow for processing of data from byte stream after it is read from + * client but before it is appended to the terminal. If the returned byte + * array is different than the one that was passed in with the byteBuffer + * argument, then the bytesRead value will be ignored and the full + * returned array will be written out. + * <p> + * The default implementation is returning the byte stream unmodified. + * + * @param byteBuffer The byte stream. Must not be <code>null</code>. + * @param bytesRead The number of bytes that were read into the read buffer. + * @return The processed byte stream. + * + */ + protected byte[] onContentReadFromStream(byte[] byteBuffer, int bytesRead) { + assert byteBuffer != null; + return byteBuffer; + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/StreamsConnector.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/StreamsConnector.java new file mode 100644 index 000000000..0bc89e64c --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/StreamsConnector.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.streams; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.ui.services.IDisposable; + +/** + * Streams connector implementation. + */ +@SuppressWarnings("restriction") +public class StreamsConnector extends AbstractStreamsConnector implements IDisposable { + // Reference to the streams settings + private final StreamsSettings settings; + + /** + * Constructor. + */ + public StreamsConnector() { + this(new StreamsSettings()); + } + + /** + * Constructor. + * + * @param settings The streams settings. Must not be <code>null</code> + */ + public StreamsConnector(StreamsSettings settings) { + super(); + + Assert.isNotNull(settings); + this.settings = settings; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#connect(org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl) + */ + @Override + public void connect(ITerminalControl control) { + Assert.isNotNull(control); + super.connect(control); + + // connect the streams + connectStreams(control, settings.getStdinStream(), settings.getStdoutStream(), settings.getStderrStream()); + + // Set the terminal control state to CONNECTED + control.setState(TerminalState.CONNECTED); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#isLocalEcho() + */ + @Override + public boolean isLocalEcho() { + return settings.isLocalEcho(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.services.IDisposable#dispose() + */ + @Override + public void dispose() { + disconnect(); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#doDisconnect() + */ + @Override + public void doDisconnect() { + // Dispose the streams + super.doDisconnect(); + + // Set the terminal control state to CLOSED. + fControl.setState(TerminalState.CLOSED); + } + + // ***** Process Connector settings handling ***** + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#getSettingsSummary() + */ + @Override + public String getSettingsSummary() { + return ""; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#load(org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore) + */ + @Override + public void load(ISettingsStore store) { + settings.load(store); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl#save(org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore) + */ + @Override + public void save(ISettingsStore store) { + settings.save(store); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/StreamsSettings.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/StreamsSettings.java new file mode 100644 index 000000000..a3ef45196 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/streams/StreamsSettings.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.streams; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.te.runtime.interfaces.properties.IPropertiesContainer; + +/** + * Streams connector settings implementation. + */ +@SuppressWarnings("restriction") +public class StreamsSettings { + // Reference to the stdin stream + private OutputStream stdin; + // Reference to the stdout stream + private InputStream stdout; + // Reference to the stderr stream + private InputStream stderr; + // Flag to control the local echo + private boolean localEcho = true; + + /** + * Sets the stdin stream instance. + * + * @param stdin The stream instance or <code>null</code>. + */ + public void setStdinStream(OutputStream stdin) { + this.stdin = stdin; + } + + /** + * Returns the stdin stream instance. + * + * @return The stream instance or <code>null</code>. + */ + public OutputStream getStdinStream() { + return stdin; + } + + /** + * Sets the stdout stream instance. + * + * @param stdout The stream instance or <code>null</code>. + */ + public void setStdoutStream(InputStream stdout) { + this.stdout = stdout; + } + + /** + * Returns the stdout stream instance. + * + * @return The stream instance or <code>null</code>. + */ + public InputStream getStdoutStream() { + return stdout; + } + + /** + * Sets the stderr stream instance. + * + * @param stderr The stream instance or <code>null</code>. + */ + public void setStderrStream(InputStream stderr) { + this.stderr = stderr; + } + + /** + * Returns the stderr stream instance. + * + * @return The stream instance or <code>null</code>. + */ + public InputStream getStderrStream() { + return stderr; + } + + /** + * Sets if the process requires a local echo from the terminal widget. + * + * @param value Specify <code>true</code> to enable the local echo, <code>false</code> otherwise. + */ + public void setLocalEcho(boolean value) { + this.localEcho = value; + } + + /** + * Returns <code>true</code> if the process requires a local echo + * from the terminal widget. + * + * @return <code>True</code> if local echo is enabled, <code>false</code> otherwise. + */ + public boolean isLocalEcho() { + return localEcho; + } + + /** + * Loads the streams settings from the given settings store. + * + * @param store The settings store. Must not be <code>null</code>. + */ + public void load(ISettingsStore store) { + Assert.isNotNull(store); + localEcho = Boolean.parseBoolean(store.get("LocalEcho", Boolean.FALSE.toString())); //$NON-NLS-1$ + if (store instanceof IPropertiesContainer) { + stdin = (OutputStream)((IPropertiesContainer)store).getProperty("stdin"); //$NON-NLS-1$ + stdout = (InputStream)((IPropertiesContainer)store).getProperty("stdout"); //$NON-NLS-1$ + stderr = (InputStream)((IPropertiesContainer)store).getProperty("stderr"); //$NON-NLS-1$ + } + } + + /** + * Saves the process settings to the given settings store. + * + * @param store The settings store. Must not be <code>null</code>. + */ + public void save(ISettingsStore store) { + Assert.isNotNull(store); + store.put("LocalEcho", Boolean.toString(localEcho)); //$NON-NLS-1$ + if (store instanceof IPropertiesContainer) { + ((IPropertiesContainer)store).setProperty("stdin", stdin); //$NON-NLS-1$ + ((IPropertiesContainer)store).setProperty("stdout", stdout); //$NON-NLS-1$ + ((IPropertiesContainer)store).setProperty("stderr", stderr); //$NON-NLS-1$ + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabDisposeListener.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabDisposeListener.java new file mode 100644 index 000000000..09f7783a8 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabDisposeListener.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.tabs; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.te.runtime.events.DisposedEvent; +import org.eclipse.tm.te.runtime.events.EventManager; + +/** + * Terminals tab default dispose listener implementation. + */ +@SuppressWarnings("restriction") +public class TabDisposeListener implements DisposeListener { + private final TabFolderManager parentTabFolderManager; + + /** + * Constructor. + * + * @param parentTabFolderManager The parent tab folder manager. Must be not <code>null</code> + */ + public TabDisposeListener(TabFolderManager parentTabFolderManager) { + Assert.isNotNull(parentTabFolderManager); + this.parentTabFolderManager = parentTabFolderManager; + } + + /** + * Returns the parent terminal console tab folder manager instance. + * + * @return The parent terminal console tab folder manager instance. + */ + protected final TabFolderManager getParentTabFolderManager() { + return parentTabFolderManager; + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) + */ + @Override + public void widgetDisposed(DisposeEvent e) { + // If a tab item gets disposed, we have to dispose the terminal as well + if (e.getSource() instanceof CTabItem) { + // Get the terminal control (if any) from the tab item + Object candidate = ((CTabItem)e.getSource()).getData(); + if (candidate instanceof ITerminalViewControl) ((ITerminalViewControl)candidate).disposeTerminal(); + + // If all items got removed, we have to switch back to the empty page control + if (parentTabFolderManager.getTabFolder() != null && parentTabFolderManager.getTabFolder().getItemCount() == 0) { + parentTabFolderManager.getParentView().switchToEmptyPageControl(); + } + // Fire selection changed event + parentTabFolderManager.fireSelectionChanged(); + // Fire the terminal console disposed event + EventManager.getInstance().fireEvent(new DisposedEvent(e.getSource(), ((CTabItem)e.getSource()).getData("customData"))); //$NON-NLS-1$ + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderManager.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderManager.java new file mode 100644 index 000000000..491548ec4 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderManager.java @@ -0,0 +1,720 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.tabs; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.EventObject; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.tm.internal.terminal.control.ITerminalListener; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.control.TerminalViewControlFactory; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.tm.te.runtime.events.EventManager; +import org.eclipse.tm.te.ui.events.AbstractEventListener; +import org.eclipse.tm.te.ui.swt.DisplayUtil; +import org.eclipse.tm.te.ui.terminals.activator.UIPlugin; +import org.eclipse.tm.te.ui.terminals.events.SelectionChangedBroadcastEvent; +import org.eclipse.tm.te.ui.terminals.interfaces.ITerminalsView; +import org.eclipse.tm.te.ui.terminals.interfaces.ImageConsts; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDEEncoding; + +/** + * Terminals tab folder manager. + */ +@SuppressWarnings("restriction") +public class TabFolderManager extends PlatformObject implements ISelectionProvider { + // Reference to the parent terminal consoles view + private final ITerminalsView parentView; + // Reference to the selection listener instance + private final SelectionListener selectionListener; + // Reference to the broadcasted selection changed event listener instance + private final BroadcastedSelectionChangedEventListener broadcastedSelectionChangedEventListener; + + /** + * List of selection changed listeners. + */ + private final List<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>(); + + /** + * The terminal control selection listener implementation. + */ + private class TerminalControlSelectionListener implements DisposeListener, MouseListener { + private final ITerminalViewControl terminal; + private boolean selectMode; + + /** + * Constructor. + * + * @param terminal The terminal control. Must be not <code>null</code>. + */ + public TerminalControlSelectionListener(ITerminalViewControl terminal) { + Assert.isNotNull(terminal); + this.terminal = terminal; + + // Register ourself as the required listener + terminal.getControl().addDisposeListener(this); + terminal.getControl().addMouseListener(this); + } + + /** + * Returns the associated terminal view control. + * + * @return The terminal view control. + */ + protected final ITerminalViewControl getTerminal() { + return terminal; + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) + */ + @Override + public void widgetDisposed(DisposeEvent e) { + // Widget got disposed, check if it is ours + // If a tab item gets disposed, we have to dispose the terminal as well + if (e.getSource().equals(terminal.getControl())) { + // Remove as listener + getTerminal().getControl().removeDisposeListener(this); + getTerminal().getControl().removeMouseListener(this); + } + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) + */ + @Override + public void mouseDown(MouseEvent e) { + // Left button down -> select mode starts + if (e.button == 1) selectMode = true; + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) + */ + @Override + public void mouseUp(MouseEvent e) { + if (e.button == 1 && selectMode) { + selectMode = false; + // Fire a selection changed event with the terminal controls selection + DisplayUtil.safeAsyncExec(new Runnable() { + @Override + public void run() { + fireSelectionChanged(new StructuredSelection(getTerminal().getSelection())); + } + }); + } + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) + */ + @Override + public void mouseDoubleClick(MouseEvent e) { + } + } + + /** + * The event listener to process broadcasted selection changed events + */ + private class BroadcastedSelectionChangedEventListener extends AbstractEventListener { + private final TabFolderManager parent; + + /** + * Constructor. + * + * @param parent The parent tab folder manager. Must not be <code>null</code>. + */ + public BroadcastedSelectionChangedEventListener(TabFolderManager parent) { + super(); + + Assert.isNotNull(parent); + this.parent = parent; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.runtime.interfaces.events.IEventListener#eventFired(java.util.EventObject) + */ + @Override + public void eventFired(EventObject event) { + if (event instanceof SelectionChangedBroadcastEvent && !event.getSource().equals(parent)) { + // Don't need to do anything if the parent tab folder is disposed or does not have a open tab + CTabFolder tabFolder = parent.getTabFolder(); + if (tabFolder == null || tabFolder.isDisposed() || tabFolder.getItemCount() == 0) return; + + // Received a broadcasted selection changed event from another tab folder manager. + SelectionChangedEvent selectionChangedEvent = ((SelectionChangedBroadcastEvent)event).getSelectionChangedEvent(); + if (selectionChangedEvent != null && selectionChangedEvent.getSelection() instanceof IStructuredSelection && !selectionChangedEvent.getSelection().isEmpty()) { + // Extract the selection from the selection changed event + IStructuredSelection selection = (IStructuredSelection)selectionChangedEvent.getSelection(); + // Determine the first element in the selection being a CTabItem + CTabItem item = null; + Iterator<?> iterator = selection.iterator(); + while (iterator.hasNext()) { + Object candidate = iterator.next(); + if (candidate instanceof CTabItem) { item = (CTabItem)candidate; break; } + } + // If we got an CTabItem from the selection, try to find a CTabItem in our own tab folder manager + // which is associated with the exact same data object. + if (item != null && item.getData("customData") != null) { //$NON-NLS-1$ + Object data = item.getData("customData"); //$NON-NLS-1$ + + CTabItem[] ourItems = tabFolder.getItems(); + for (CTabItem ourItem : ourItems) { + Object ourData = ourItem.getData("customData"); //$NON-NLS-1$ + if (data.equals(ourData) && !ourItem.equals(parent.getActiveTabItem())) { + // Select this item and we are done + parent.setSelection(new StructuredSelection(ourItem)); + break; + } + } + } + } + } + } + + } + + /** + * Constructor. + * + * @param parentView The parent terminal console view. Must be not <code>null</code>. + */ + public TabFolderManager(ITerminalsView parentView) { + super(); + Assert.isNotNull(parentView); + this.parentView = parentView; + + // Attach a selection listener to the tab folder + selectionListener = doCreateTabFolderSelectionListener(this); + if (getTabFolder() != null) getTabFolder().addSelectionListener(selectionListener); + + + // Create and register the broadcasted selection changed event listener + broadcastedSelectionChangedEventListener = doCreateBroadcastedSelectionChangedEventListener(this); + if (isListeningToBroadcastedSelectionChangedEvent() && broadcastedSelectionChangedEventListener != null) { + EventManager.getInstance().addEventListener(broadcastedSelectionChangedEventListener, SelectionChangedBroadcastEvent.class); + } + } + + /** + * Creates the terminal console tab folder selection listener instance. + * + * @param parent The parent terminal console tab folder manager. Must be not <code>null</code>. + * @return The selection listener instance. + */ + protected TabFolderSelectionListener doCreateTabFolderSelectionListener(TabFolderManager parent) { + Assert.isNotNull(parent); + return new TabFolderSelectionListener(parent); + } + + /** + * Returns the parent terminal consoles view. + * + * @return The terminal consoles view instance. + */ + protected final ITerminalsView getParentView() { + return parentView; + } + + /** + * Returns the tab folder associated with the parent view. + * + * @return The tab folder or <code>null</code>. + */ + protected final CTabFolder getTabFolder() { + return (CTabFolder)getParentView().getAdapter(CTabFolder.class); + } + + /** + * Returns the selection changed listeners currently registered. + * + * @return The registered selection changed listeners or an empty array. + */ + protected final ISelectionChangedListener[] getSelectionChangedListeners() { + return selectionChangedListeners.toArray(new ISelectionChangedListener[selectionChangedListeners.size()]); + } + + /** + * Dispose the tab folder manager instance. + */ + public void dispose() { + // Dispose the selection listener + if (getTabFolder() != null && !getTabFolder().isDisposed()) getTabFolder().removeSelectionListener(selectionListener); + // Remove the broadcasted selection changed event listener from the notification manager + if (broadcastedSelectionChangedEventListener != null) { + EventManager.getInstance().removeEventListener(broadcastedSelectionChangedEventListener); + } + } + + /** + * Creates a new tab item with the given title and connector. + * + * @param title The tab title. Must be not <code>null</code>. + * @param connector The terminal connector. Must be not <code>null</code>. + * @param data The custom terminal data node or <code>null</code>. + * + * @return The created tab item or <code>null</code> if failed. + */ + @SuppressWarnings("unused") + public CTabItem createTabItem(String title, ITerminalConnector connector, Object data) { + Assert.isNotNull(title); + Assert.isNotNull(connector); + + // The result tab item + CTabItem item = null; + + // Get the tab folder from the parent viewer + CTabFolder tabFolder = getTabFolder(); + if (tabFolder != null) { + // Generate a unique title string for the new tab item (must be called before creating the item itself) + title = makeUniqueTitle(title, tabFolder); + // Create the tab item + item = new CTabItem(tabFolder, SWT.CLOSE); + // Set the tab item title + item.setText(title); + // Set the tab icon + Image image = getTabItemImage(connector, data); + if (image != null) item.setImage(image); + + // Setup the tab item listeners + setupTerminalTabListeners(item); + + // Create the composite to create the terminal control within + Composite composite = new Composite(tabFolder, SWT.NONE); + composite.setLayout(new FillLayout()); + // Associate the composite with the tab item + item.setControl(composite); + + // Refresh the layout + tabFolder.getParent().layout(true); + + // Create the terminal control + ITerminalViewControl terminal = TerminalViewControlFactory.makeControl(doCreateTerminalTabTerminalListener(item), composite, new ITerminalConnector[] { connector }); + // Add the "selection" listener to the terminal control + new TerminalControlSelectionListener(terminal); + // Use the default Eclipse IDE encoding setting to configure the terminals encoding + try { terminal.setEncoding(IDEEncoding.getResourceEncoding()); } catch (UnsupportedEncodingException e) { /* ignored on purpose */ } + // Associated the terminal with the tab item + item.setData(terminal); + // Associated the custom data node with the tab item (if any) + if (data != null) item.setData("customData", data); //$NON-NLS-1$ + + // Overwrite the text canvas help id + String contextHelpId = getParentView().getContextHelpId(); + if (contextHelpId != null) { + PlatformUI.getWorkbench().getHelpSystem().setHelp(terminal.getControl(), contextHelpId); + } + + // Set the context menu + TabFolderMenuHandler menuHandler = (TabFolderMenuHandler)getParentView().getAdapter(TabFolderMenuHandler.class); + if (menuHandler != null) { + Menu menu = (Menu)menuHandler.getAdapter(Menu.class); + if (menu != null) { + // One weird occurrence of IllegalArgumentException: Widget has wrong parent. + // Inspecting the code, this seem extremely unlikely. The terminal is created + // from a composite parent, the composite parent from the tab folder and the menu + // from the tab folder. Means, at the end all should have the same menu shell, shouldn't they? + try { + terminal.getControl().setMenu(menu); + } catch (IllegalArgumentException e) { + // Log exception only if debug mode is set to 1. + if (UIPlugin.getTraceHandler().isSlotEnabled(1, null)) { + e.printStackTrace(); + } + } + } + } + + // Select the created item within the tab folder + tabFolder.setSelection(item); + + // Set the connector + terminal.setConnector(connector); + + // And connect the terminal + terminal.connectTerminal(); + + // Fire selection changed event + fireSelectionChanged(); + } + + // Return the create tab item finally. + return item; + } + + /** + * Generate a unique title string based on the given proposal. + * + * @param proposal The proposal. Must be not <code>null</code>. + * @return The unique title string. + */ + protected String makeUniqueTitle(String proposal, CTabFolder tabFolder) { + Assert.isNotNull(proposal); + Assert.isNotNull(tabFolder); + + String title = proposal; + int index = 0; + + // Loop all existing tab items and check the titles. We have to remember + // all found titles as modifying the proposal might in turn conflict again + // with the title of a tab already checked. + List<String> titles = new ArrayList<String>(); + for (CTabItem item : tabFolder.getItems()) { + // Get the tab item title + titles.add(item.getText()); + // Make the proposal unique be appending (<n>) against all known titles. + while (titles.contains(title)) title = proposal + " (" + index++ + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + return title; + } + + /** + * Setup the terminal console tab item listeners. + * + * @param item The tab item. Must be not <code>null</code>. + */ + protected void setupTerminalTabListeners(CTabItem item) { + Assert.isNotNull(item); + + // Create and associate the disposal listener + item.addDisposeListener(doCreateTerminalTabDisposeListener(this)); + + // Create and register the property change listener + final IPropertyChangeListener propertyChangeListener = doCreateTerminalTabPropertyChangeListener(item); + // Register to the JFace font registry + JFaceResources.getFontRegistry().addListener(propertyChangeListener); + // Remove the listener from the JFace font registry if the tab gets disposed + item.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + JFaceResources.getFontRegistry().removeListener(propertyChangeListener); + } + }); + } + + /** + * Creates a new terminal console tab terminal listener instance. + * + * @param item The tab item. Must be not <code>null</code>. + * @return The terminal listener instance. + */ + protected ITerminalListener doCreateTerminalTabTerminalListener(CTabItem item) { + Assert.isNotNull(item); + return new TabTerminalListener(item); + } + + /** + * Creates a new terminal console tab dispose listener instance. + * + * @param parent The parent terminal console tab folder manager. Must be not <code>null</code>. + * @return The dispose listener instance. + */ + protected DisposeListener doCreateTerminalTabDisposeListener(TabFolderManager parent) { + Assert.isNotNull(parent); + return new TabDisposeListener(parent); + } + + /** + * Creates a new terminal console tab property change listener instance. + * + * @param item The tab item. Must be not <code>null</code>. + * @return The property change listener instance. + */ + protected IPropertyChangeListener doCreateTerminalTabPropertyChangeListener(CTabItem item) { + Assert.isNotNull(item); + return new TabPropertyChangeListener(item); + } + + /** + * Returns the tab item image. + * + * @param connector The terminal connector. Must be not <code>null</code>. + * @param data The custom terminal data node or <code>null</code>. + * + * @return The tab item image or <code>null</code>. + */ + protected Image getTabItemImage(ITerminalConnector connector, Object data) { + Assert.isNotNull(connector); + return UIPlugin.getImage(ImageConsts.VIEW_Terminals); + } + + /** + * Lookup a tab item with the given title and the given terminal connector. + * <p> + * <b>Note:</b> The method will handle unified tab item titles itself. + * + * @param title The tab item title. Must be not <code>null</code>. + * @param connector The terminal connector. Must be not <code>null</code>. + * @param data The custom terminal data node or <code>null</code>. + * + * @return The corresponding tab item or <code>null</code>. + */ + public CTabItem findTabItem(String title, ITerminalConnector connector, Object data) { + Assert.isNotNull(title); + Assert.isNotNull(connector); + + // Get the tab folder + CTabFolder tabFolder = getTabFolder(); + if (tabFolder == null) return null; + + // Loop all existing tab items and try to find a matching title + for (CTabItem item : tabFolder.getItems()) { + // Disposed items cannot be matched + if (item.isDisposed()) continue; + // Get the title from the current tab item + String itemTitle = item.getText(); + // The terminal console state might be signaled to the user via the + // terminal console tab title. Filter out any prefix "<.*>\s*". + itemTitle = itemTitle.replaceFirst("^<.*>\\s*", ""); //$NON-NLS-1$ //$NON-NLS-2$ + if (itemTitle.startsWith(title)) { + // The title string matches -> double check with the terminal connector + ITerminalViewControl terminal = (ITerminalViewControl)item.getData(); + ITerminalConnector connector2 = terminal.getTerminalConnector(); + // If the connector id and name matches -> check on the settings + if (connector.getId().equals(connector2.getId()) && connector.getName().equals(connector2.getName())) { + if (!connector.isInitialized()) { + // an uninitialized connector does not yield a sensible summary + return item; + } + String summary = connector.getSettingsSummary(); + String summary2 = connector2.getSettingsSummary(); + // If we have matching settings -> we've found the matching item + if (summary.equals(summary2)) return item; + } + } + } + + return null; + } + + /** + * Make the given tab item the active tab and bring the tab to the top. + * + * @param item The tab item. Must be not <code>null</code>. + */ + public void bringToTop(CTabItem item) { + Assert.isNotNull(item); + + // Get the tab folder + CTabFolder tabFolder = getTabFolder(); + if (tabFolder == null) return; + + // Set the given tab item as selection to the tab folder + tabFolder.setSelection(item); + // Fire selection changed event + fireSelectionChanged(); + } + + /** + * Returns the currently active tab. + * + * @return The active tab item or <code>null</code> if none. + */ + public CTabItem getActiveTabItem() { + // Get the tab folder + CTabFolder tabFolder = getTabFolder(); + if (tabFolder == null) return null; + + return tabFolder.getSelection(); + } + + /** + * Remove all terminated tab items. + */ + public void removeTerminatedItems() { + // Get the tab folder + CTabFolder tabFolder = getTabFolder(); + if (tabFolder == null) return; + + // Loop the items and check for terminated status + for (CTabItem item: tabFolder.getItems()) { + // Disposed items cannot be matched + if (item.isDisposed()) continue; + // Check if the item is terminated + if (isTerminatedTabItem(item)) { + // item is terminated -> dispose + item.dispose(); + } + } + } + + /** + * Checks if the given tab item represents a terminated console. Subclasses may + * overwrite this method to extend the definition of terminated. + * + * @param item The tab item or <code>null</code>. + * @return <code>True</code> if the tab item represents a terminated console, <code>false</code> otherwise. + */ + protected boolean isTerminatedTabItem(CTabItem item) { + // Null items or disposed items cannot be matched + if (item == null || item.isDisposed()) return false; + + // First, match the item title. If it contains "<terminated>", the item can be removed + String itemTitle = item.getText(); + if (itemTitle != null && itemTitle.contains("<terminated>")) { //$NON-NLS-1$ + return true; + } + // Second, check if the associated terminal control is closed + // The title string matches -> double check with the terminal connector + ITerminalViewControl terminal = (ITerminalViewControl)item.getData(); + if (terminal != null && terminal.getState() == TerminalState.CLOSED) { + return true; + } + + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) + */ + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + if (listener != null && !selectionChangedListeners.contains(listener)) selectionChangedListeners.add(listener); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) + */ + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + if (listener != null) selectionChangedListeners.remove(listener); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection() + */ + @Override + public ISelection getSelection() { + CTabItem activeTabItem = getActiveTabItem(); + return activeTabItem != null ? new StructuredSelection(activeTabItem) : new StructuredSelection(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection) + */ + @Override + public void setSelection(ISelection selection) { + if (selection instanceof IStructuredSelection && !selection.isEmpty()) { + // The first selection element which is a CTabItem will become the active item + Iterator<?> iterator = ((IStructuredSelection)selection).iterator(); + while (iterator.hasNext()) { + Object candidate = iterator.next(); + if (candidate instanceof CTabItem) { bringToTop((CTabItem)candidate); return; } + } + } + // fire a changed event in any case + fireSelectionChanged(selection); + } + + /** + * Fire the selection changed event to the registered listeners. + */ + protected void fireSelectionChanged() { + fireSelectionChanged(getSelection()); + } + + /** + * Fire the selection changed event to the registered listeners. + */ + protected final void fireSelectionChanged(ISelection selection) { + // Create the selection changed event + SelectionChangedEvent event = new SelectionChangedEvent(TabFolderManager.this, selection); + + // First, invoke the registered listeners and let them do their job + for (ISelectionChangedListener listener : selectionChangedListeners) { + listener.selectionChanged(event); + } + + // Second, broadcast the event if desired + if (isBroadcastSelectionChangedEvent()) onBroadcastSelectionChangedEvent(event); + } + + /** + * Controls if or if not a selection changed event, processed by this tab + * folder manager shall be broadcasted to via the global Workbench notification + * mechanism. + * + * @return <code>True</code> to broadcast the selection changed event, <code>false</code> otherwise. + */ + protected boolean isBroadcastSelectionChangedEvent() { + return false; + } + + /** + * Broadcasts the given selection changed event via the global Workbench notification mechanism. + * + * @param selectionChangedEvent The selection changed event or <code>null</code>. + */ + protected void onBroadcastSelectionChangedEvent(SelectionChangedEvent selectionChangedEvent) { + SelectionChangedBroadcastEvent event = doCreateSelectionChangedBroadcastEvent(this, selectionChangedEvent); + if (event != null) EventManager.getInstance().fireEvent(event); + } + + /** + * Creates the selection changed broadcast event. + * + * @param source The event source. Must not be <code>null</code>. + * @param selectionChangedEvent The selection changed event or <code>null</code>. + * + * @return The selection changed broadcast event or <code>null</code>. + */ + protected SelectionChangedBroadcastEvent doCreateSelectionChangedBroadcastEvent(TabFolderManager source, SelectionChangedEvent selectionChangedEvent) { + return new SelectionChangedBroadcastEvent(source, selectionChangedEvent); + } + + /** + * Returns if or if not this tab folder manager is listening to broadcasted selection + * changed events. Broadcasted events by the same tab folder manager are ignored independent + * of the methods return value. + * + * @return <code>True</code> to listen to broadcasted selection changed events, <code>false</code> to not listen. + */ + protected boolean isListeningToBroadcastedSelectionChangedEvent() { + return false; + } + + /** + * Creates a new broadcasted selection changed event listener instance. + * + * @param parent The parent tab folder manager. Must not be <code>null</code>. + * @return The event listener instance or <code>null</code>. + */ + protected BroadcastedSelectionChangedEventListener doCreateBroadcastedSelectionChangedEventListener(TabFolderManager parent) { + return new BroadcastedSelectionChangedEventListener(parent); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderMenuHandler.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderMenuHandler.java new file mode 100644 index 000000000..91d6fb19a --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderMenuHandler.java @@ -0,0 +1,297 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.tabs; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.jface.action.IMenuListener2; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction; +import org.eclipse.tm.internal.terminal.control.actions.TerminalActionClearAll; +import org.eclipse.tm.internal.terminal.control.actions.TerminalActionCopy; +import org.eclipse.tm.internal.terminal.control.actions.TerminalActionPaste; +import org.eclipse.tm.internal.terminal.control.actions.TerminalActionSelectAll; +import org.eclipse.tm.te.ui.terminals.actions.TabScrollLockAction; +import org.eclipse.tm.te.ui.terminals.interfaces.ITerminalsView; +import org.eclipse.ui.IWorkbenchActionConstants; + +/** + * Terminals tab folder menu handler. + */ +@SuppressWarnings("restriction") +public class TabFolderMenuHandler extends PlatformObject { + // Reference to the parent terminal console view + private final ITerminalsView parentView; + // Reference to the tab folder context menu manager + private MenuManager contextMenuManager; + // Reference to the tab folder context menu + private Menu contextMenu; + // The list of actions available within the context menu + private final List<AbstractTerminalAction> contextMenuActions = new ArrayList<AbstractTerminalAction>(); + + /** + * Default menu listener implementation. + */ + protected class MenuListener implements IMenuListener2 { + + /* (non-Javadoc) + * @see org.eclipse.jface.action.IMenuListener2#menuAboutToHide(org.eclipse.jface.action.IMenuManager) + */ + @Override + public void menuAboutToHide(IMenuManager manager) { + // CQ:WIND00192293 and CQ:WIND194204 - don't update actions on menuAboutToHide + // See also http://bugs.eclipse.org/296212 + // updateMenuItems(false); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) + */ + @Override + public void menuAboutToShow(IMenuManager manager) { + updateMenuItems(true); + } + + } + + /** + * Constructor. + * + * @param parentView The parent terminal console view. Must not be <code>null</code>. + */ + public TabFolderMenuHandler(ITerminalsView parentView) { + super(); + Assert.isNotNull(parentView); + this.parentView = parentView; + } + + /** + * Returns the parent terminal console view. + * + * @return The parent terminal console view instance. + */ + protected final ITerminalsView getParentView() { + return parentView; + } + + /** + * Returns the tab folder associated with the parent view. + * + * @return The tab folder or <code>null</code>. + */ + protected final CTabFolder getTabFolder() { + return (CTabFolder)getParentView().getAdapter(CTabFolder.class); + } + + /** + * Dispose the tab folder menu handler instance. + */ + public void dispose() { + // Dispose the context menu + if (contextMenu != null) { contextMenu.dispose(); contextMenu = null; } + // Dispose the context menu manager + if (contextMenuManager != null) { contextMenuManager.dispose(); contextMenuManager = null; } + // Clear all actions + contextMenuActions.clear(); + } + + /** + * Setup the context menu for the tab folder. The method will return + * immediately if the menu handler had been initialized before. + * + * @param tabFolder The tab folder control. Must not be <code>null</code>. + */ + public void initialize() { + // Return immediately if the menu manager and menu got initialized already + if (contextMenuManager != null && contextMenu != null) { + return; + } + + // Get the tab folder + CTabFolder tabFolder = getTabFolder(); + if (tabFolder == null) { + return; + } + + // Create the menu manager if not done before + contextMenuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + // Create and associated the menu listener + contextMenuManager.addMenuListener(new MenuListener()); + // Create the context menu + contextMenu = contextMenuManager.createContextMenu(tabFolder); + + // Create the context menu action instances + doCreateContextMenuActions(); + + // Fill the context menu + doFillContextMenu(contextMenuManager); + + // Register to the view site to open the menu for contributions + getParentView().getSite().registerContextMenu(contextMenuManager, getParentView().getSite().getSelectionProvider()); + } + + /** + * Adds the given action to the context menu actions list. + * + * @param action The action instance. Must be not <code>null</code>. + */ + protected final void add(AbstractTerminalAction action) { + Assert.isNotNull(action); + contextMenuActions.add(action); + } + + /** + * Create the context menu actions. + */ + protected void doCreateContextMenuActions() { + // Create and add the copy action + add(new TerminalActionCopy() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + + // Create and add the paste action + add(new TerminalActionPaste() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + + // Create and add the clear all action + add(new TerminalActionClearAll() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + + // Create and add the select all action + add(new TerminalActionSelectAll() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + + // Create and add the scroll lock action + add (new TabScrollLockAction() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + } + + /** + * Returns the currently active terminal control. + * + * @return The currently active terminal control or <code>null</code>. + */ + protected ITerminalViewControl getActiveTerminalViewControl() { + ITerminalViewControl terminal = null; + + // Get the active tab item from the tab folder manager + TabFolderManager manager = (TabFolderManager)getParentView().getAdapter(TabFolderManager.class); + if (manager != null) { + // If we have the active tab item, we can get the active terminal control + CTabItem activeTabItem = manager.getActiveTabItem(); + if (activeTabItem != null) { + terminal = (ITerminalViewControl)activeTabItem.getData(); + } + } + + return terminal; + } + + /** + * Fill in the context menu content within the given manager. + * + * @param manager The menu manager. Must not be <code>null</code>. + */ + protected void doFillContextMenu(MenuManager manager) { + assert manager != null; + + // Loop all actions and add them to the menu manager + for (AbstractTerminalAction action : contextMenuActions) { + manager.add(action); + // Add a separator after the paste action + if (action instanceof TerminalActionPaste) { + manager.add(new Separator()); + } + // Add a separator after the select all action + if (action instanceof TerminalActionSelectAll) { + manager.add(new Separator()); + } + } + + // Menu contributions will end up here + manager.add(new Separator()); + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + + /** + * Update the context menu items on showing or hiding the context menu. + * + * @param aboutToShow <code>True</code> if the menu is about to show, <code>false</code> otherwise. + */ + protected void updateMenuItems(boolean aboutToShow) { + // Loop all actions and update the status + for (AbstractTerminalAction action : contextMenuActions) { + action.updateAction(aboutToShow); + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class) + */ + @Override + public Object getAdapter(Class adapter) { + if (MenuManager.class.isAssignableFrom(adapter)) { + return contextMenuManager; + } else if (Menu.class.isAssignableFrom(adapter)) { + return contextMenu; + } + + // Try the parent view + Object adapted = getParentView().getAdapter(adapter); + if (adapted != null) { + return adapted; + } + + return super.getAdapter(adapter); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderSelectionListener.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderSelectionListener.java new file mode 100644 index 000000000..de25ec3ea --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderSelectionListener.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.tabs; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; + +/** + * Terminals tab folder default selection listener implementation. + */ +public class TabFolderSelectionListener implements SelectionListener { + private final TabFolderManager parentTabFolderManager; + + /** + * Constructor. + * + * @param parentTabFolderManager The parent tab folder manager. Must be not <code>null</code> + */ + public TabFolderSelectionListener(TabFolderManager parentTabFolderManager) { + Assert.isNotNull(parentTabFolderManager); + this.parentTabFolderManager = parentTabFolderManager; + } + + /** + * Returns the parent terminal console tab folder manager instance. + * + * @return The parent terminal console tab folder manager instance. + */ + protected final TabFolderManager getParentTabFolderManager() { + return parentTabFolderManager; + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) + */ + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) + */ + @Override + public void widgetSelected(SelectionEvent e) { + parentTabFolderManager.fireSelectionChanged(); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderToolbarHandler.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderToolbarHandler.java new file mode 100644 index 000000000..4215771c9 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabFolderToolbarHandler.java @@ -0,0 +1,304 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.tabs; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction; +import org.eclipse.tm.internal.terminal.control.actions.TerminalActionClearAll; +import org.eclipse.tm.internal.terminal.control.actions.TerminalActionCopy; +import org.eclipse.tm.internal.terminal.control.actions.TerminalActionPaste; +import org.eclipse.tm.te.ui.terminals.actions.AbstractAction; +import org.eclipse.tm.te.ui.terminals.actions.TabScrollLockAction; +import org.eclipse.tm.te.ui.terminals.interfaces.ITerminalsView; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IWorkbenchActionConstants; + + +/** + * Terminals tab folder toolbar handler. + */ +@SuppressWarnings("restriction") +public class TabFolderToolbarHandler extends PlatformObject { + // Reference to the parent terminal console view + private final ITerminalsView parentView; + // Reference to the toolbar manager + private IToolBarManager toolbarManager; + // Reference to the selection listener + private ToolbarSelectionChangedListener selectionChangedListener; + // The list of actions available within the toolbar + private final List<AbstractTerminalAction> toolbarActions = new ArrayList<AbstractTerminalAction>(); + + /** + * Default selection listener implementation. + */ + protected class ToolbarSelectionChangedListener implements ISelectionChangedListener { + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) + */ + @Override + public void selectionChanged(SelectionChangedEvent event) { + boolean enable = event != null; + + // The VlmConsoleTabFolderManager is listening to the selection changes of the + // TabFolder and fires selection changed events. + if (enable && event.getSource() instanceof TabFolderManager) { + enable = event.getSelection() instanceof StructuredSelection + && !event.getSelection().isEmpty() + && (((StructuredSelection)event.getSelection()).getFirstElement() instanceof CTabItem + || ((StructuredSelection)event.getSelection()).getFirstElement() instanceof String); + } + + updateToolbarItems(enable); + } + } + + /** + * Constructor. + * + * @param parentView The parent terminal console view. Must not be <code>null</code>. + */ + public TabFolderToolbarHandler(ITerminalsView parentView) { + super(); + Assert.isNotNull(parentView); + this.parentView = parentView; + } + + /** + * Returns the parent terminal console view. + * + * @return The terminal console view instance. + */ + protected final ITerminalsView getParentView() { + return parentView; + } + + /** + * Returns the tab folder associated with the parent view. + * + * @return The tab folder or <code>null</code>. + */ + protected final CTabFolder getTabFolder() { + return (CTabFolder)getParentView().getAdapter(CTabFolder.class); + } + + /** + * Returns the currently active terminal control. + * + * @return The currently active terminal control or <code>null</code>. + */ + public ITerminalViewControl getActiveTerminalViewControl() { + ITerminalViewControl terminal = null; + + // Get the active tab item from the tab folder manager + TabFolderManager manager = (TabFolderManager)getParentView().getAdapter(TabFolderManager.class); + if (manager != null) { + // If we have the active tab item, we can get the active terminal control + CTabItem activeTabItem = manager.getActiveTabItem(); + if (activeTabItem != null && !activeTabItem.isDisposed()) { + terminal = (ITerminalViewControl)activeTabItem.getData(); + } + } + + return terminal; + } + + /** + * Dispose the tab folder menu handler instance. + */ + public void dispose() { + // Dispose the selection changed listener + if (selectionChangedListener != null) { + getParentView().getViewSite().getSelectionProvider().removeSelectionChangedListener(selectionChangedListener); + selectionChangedListener = null; + } + + // Clear all actions + toolbarActions.clear(); + } + + /** + * Setup the context menu for the tab folder. The method will return + * immediately if the toolbar handler had been initialized before. + * + * @param tabFolder The tab folder control. Must not be <code>null</code>. + */ + public void initialize() { + // Return immediately if the toolbar manager got initialized already + if (toolbarManager != null) { + return; + } + + // Register ourself as selection listener to the tab folder + selectionChangedListener = doCreateSelectionChangedListener(); + assert selectionChangedListener != null; + getParentView().getViewSite().getSelectionProvider().addSelectionChangedListener(selectionChangedListener); + + // Get the parent view action bars + IActionBars bars = getParentView().getViewSite().getActionBars(); + + // From the action bars, get the toolbar manager + toolbarManager = bars.getToolBarManager(); + + // Create the toolbar action instances + doCreateToolbarActions(); + + // Fill the toolbar + doFillToolbar(toolbarManager); + + // Update actions + updateToolbarItems(false); + } + + /** + * Creates a new selection changed listener instance. + * + * @return The new selection changed listener instance. + */ + protected ToolbarSelectionChangedListener doCreateSelectionChangedListener() { + return new ToolbarSelectionChangedListener(); + } + + /** + * Adds the given action to the toolbar actions list. + * + * @param action The action instance. Must be not <code>null</code>. + */ + protected final void add(AbstractTerminalAction action) { + Assert.isNotNull(action); + toolbarActions.add(action); + } + + /** + * Create the toolbar actions. + */ + protected void doCreateToolbarActions() { + // Create and add the paste action + add(new TerminalActionPaste() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + + // Create and add the copy action + add(new TerminalActionCopy() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + + // Create and add the scroll lock action + add (new TabScrollLockAction() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + + // Create and add the clear all action + add(new TerminalActionClearAll() { + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget() + */ + @Override + protected ITerminalViewControl getTarget() { + return getActiveTerminalViewControl(); + } + }); + } + + /** + * Fill in the context menu content within the given manager. + * + * @param manager The menu manager. Must not be <code>null</code>. + */ + protected void doFillToolbar(IToolBarManager manager) { + assert manager != null; + + // Note: For the toolbar, the actions are added from left to right! + // So we start with the additions marker here which is the most + // left contribution item. + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + manager.add(new Separator("anchor")); //$NON-NLS-1$ + + // Loop all actions and add them to the menu manager + for (AbstractTerminalAction action : toolbarActions) { + // Add a separator before the clear all action or if the action is a separator + if (action instanceof TabScrollLockAction + || (action instanceof AbstractAction && ((AbstractAction)action).isSeparator())) { + manager.insertAfter("anchor", new Separator()); //$NON-NLS-1$ + } + // Add the action itself + manager.insertAfter("anchor", action); //$NON-NLS-1$ + } + } + + /** + * Update the toolbar items. + * + * @param enabled <code>True</code> if the items shall be enabled, <code>false</code> otherwise. + */ + protected void updateToolbarItems(boolean enabled) { + // Determine the currently active terminal control + ITerminalViewControl control = getActiveTerminalViewControl(); + // Loop all actions and update the status + for (AbstractTerminalAction action : toolbarActions) { + // If the terminal control is not available, the updateAction + // method of certain actions enable the action (bugzilla #260372). + // Workaround by forcing the action to get disabled with setEnabled. + if (control == null) { + action.setEnabled(false); + } + else { + action.updateAction(enabled); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class) + */ + @Override + public Object getAdapter(Class adapter) { + if (IToolBarManager.class.isAssignableFrom(adapter)) { + return toolbarManager; + } + // Try the parent view + Object adapted = getParentView().getAdapter(adapter); + if (adapted != null) { + return adapted; + } + + return super.getAdapter(adapter); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabPropertyChangeListener.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabPropertyChangeListener.java new file mode 100644 index 000000000..b84b8ee83 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabPropertyChangeListener.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.tabs; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.graphics.Font; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.view.TerminalView; + +/** + * Terminals tab default property change listener implementation. + */ +@SuppressWarnings("restriction") +public class TabPropertyChangeListener implements IPropertyChangeListener { + /** + * Default terminal font property key. + * + * @see TerminalView#FONT_DEFINITION + */ + public static final String FONT_DEFINITION = "terminal.views.view.font.definition"; //$NON-NLS-1$ + + // Reference to the parent tab item + private final CTabItem tabItem; + + /** + * Constructor. + * + * @param tabItem The parent tab item. Must not be <code>null</code>. + */ + public TabPropertyChangeListener(CTabItem tabItem) { + super(); + Assert.isNotNull(tabItem); + this.tabItem = tabItem; + } + + /** + * Returns the associated parent tab item. + * + * @return The parent tab item. + */ + protected final CTabItem getTabItem() { + return tabItem; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + @Override + public void propertyChange(PropertyChangeEvent event) { + // In case we are called once after the tab item got disposed + // --> Do nothing + if (getTabItem() == null || getTabItem().isDisposed()) return; + + // Listen to changes of the Font settings + if (event.getProperty().equals(FONT_DEFINITION)) { + onFontDefinitionProperyChanged(); + } + } + + /** + * Called if a property change event for the terminal font + * definition is received. + */ + protected void onFontDefinitionProperyChanged() { + // Get the current font from JFace + Font font = JFaceResources.getFont(FONT_DEFINITION); + // Get the terminal control from the tab item + if (getTabItem().getData() instanceof ITerminalViewControl) { + ITerminalViewControl terminal = (ITerminalViewControl)getTabItem().getData(); + terminal.setFont(font); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabTerminalListener.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabTerminalListener.java new file mode 100644 index 000000000..4d3824fca --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/tabs/TabTerminalListener.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.tabs; + +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tm.internal.terminal.control.ITerminalListener; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.tm.te.ui.terminals.nls.Messages; + +/** + * Terminals tab default terminal listener implementation. + */ +@SuppressWarnings("restriction") +public class TabTerminalListener implements ITerminalListener { + private final CTabItem tabItem; + + /** + * Constructor. + * + * @param tabItem The parent tab item. Must not be <code>null</code>. + */ + public TabTerminalListener(CTabItem tabItem) { + super(); + Assert.isNotNull(tabItem); + this.tabItem = tabItem; + } + + /** + * Returns the associated parent tab item. + * + * @return The parent tab item. + */ + protected final CTabItem getTabItem() { + return tabItem; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.ITerminalListener#setState(org.eclipse.tm.internal.terminal.provisional.api.TerminalState) + */ + @Override + public void setState(final TerminalState state) { + // The tab item must have been not yet disposed + final CTabItem item = getTabItem(); + if (item == null || item.isDisposed()) return; + + // Update the tab item title + item.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + String newTitle = getTerminalConsoleTabTitle(state); + if (newTitle != null) item.setText(newTitle); + } + }); + } + + // The pattern will not change over the session life-time + private static final Pattern TERMINAL_TITLE_TERMINATED_PATTERN = Pattern.compile(Messages.TabTerminalListener_consoleTerminated.replaceAll("\\{[0-9]+\\}", ".*")); //$NON-NLS-1$ //$NON-NLS-2$ + + /** + * Returns the title to set to the terminal console tab for the given state. + * <p> + * <b>Note:</b> This method is called from {@link #setState(TerminalState)} and + * is expected to by called within the UI thread. + * + * @param state The terminal state. Must be not <code>null</code>. + * @return The terminal console tab title to set or <code>null</code> to leave the title unchanged. + */ + protected String getTerminalConsoleTabTitle(TerminalState state) { + assert state != null && Display.findDisplay(Thread.currentThread()) != null; + + // The tab item must have been not yet disposed + CTabItem item = getTabItem(); + if (item == null || item.isDisposed()) return null; + + // Get the current tab title + String oldTitle = item.getText(); + + // Construct the new title + String newTitle = null; + + if (TerminalState.CLOSED.equals(state)) { + // Avoid multiple decorations of the closed state + if (!TERMINAL_TITLE_TERMINATED_PATTERN.matcher(oldTitle).matches()) { + newTitle = NLS.bind(Messages.TabTerminalListener_consoleTerminated, oldTitle); + } else { + newTitle = oldTitle; + } + } + + return newTitle != null && !newTitle.equals(oldTitle) ? newTitle : null; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.internal.terminal.control.ITerminalListener#setTerminalTitle(java.lang.String) + */ + @Override + public void setTerminalTitle(String title) { + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/view/TerminalsView.java b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/view/TerminalsView.java new file mode 100644 index 000000000..29445d972 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.terminals/src/org/eclipse/tm/te/ui/terminals/view/TerminalsView.java @@ -0,0 +1,309 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.terminals.view; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.tm.te.ui.terminals.interfaces.ITerminalsView; +import org.eclipse.tm.te.ui.terminals.tabs.TabFolderManager; +import org.eclipse.tm.te.ui.terminals.tabs.TabFolderMenuHandler; +import org.eclipse.tm.te.ui.terminals.tabs.TabFolderToolbarHandler; +import org.eclipse.ui.IWorkbenchPreferenceConstants; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.part.PageBook; +import org.eclipse.ui.part.ViewPart; + +/** + * Terminals view. + */ +public class TerminalsView extends ViewPart implements ITerminalsView { + + // Reference to the main page book control + private PageBook pageBookControl; + // Reference to the tab folder maintaining the consoles + private CTabFolder tabFolderControl; + // Reference to the tab folder manager + private TabFolderManager tabFolderManager; + // Reference to the tab folder menu handler + private TabFolderMenuHandler tabFolderMenuHandler; + // Reference to the tab folder toolbar handler + private TabFolderToolbarHandler tabFolderToolbarHandler; + // Reference to the empty page control (to be show if no console is open) + private Control emptyPageControl; + + /** + * Constructor. + */ + public TerminalsView() { + super(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#dispose() + */ + @Override + public void dispose() { + // Dispose the tab folder manager + if (tabFolderManager != null) { tabFolderManager.dispose(); tabFolderManager = null; } + // Dispose the tab folder menu handler + if (tabFolderMenuHandler != null) { tabFolderMenuHandler.dispose(); tabFolderMenuHandler = null; } + // Dispose the tab folder toolbar handler + if (tabFolderToolbarHandler != null) { tabFolderToolbarHandler.dispose(); tabFolderToolbarHandler = null; } + + super.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createPartControl(Composite parent) { + // Create the page book control + pageBookControl = doCreatePageBookControl(parent); + Assert.isNotNull(pageBookControl); + // Configure the page book control + doConfigurePageBookControl(pageBookControl); + + // Create the empty page control + emptyPageControl = doCreateEmptyPageControl(pageBookControl); + Assert.isNotNull(emptyPageControl); + // Configure the empty page control + doConfigureEmptyPageControl(emptyPageControl); + + // Create the tab folder control (empty) + tabFolderControl = doCreateTabFolderControl(pageBookControl); + Assert.isNotNull(tabFolderControl); + // Configure the tab folder control + doConfigureTabFolderControl(tabFolderControl); + + // Create the tab folder manager + tabFolderManager = doCreateTabFolderManager(this); + Assert.isNotNull(tabFolderManager); + // Set the tab folder manager as the selection provider + getSite().setSelectionProvider(tabFolderManager); + + // Setup the tab folder menu handler + tabFolderMenuHandler = doCreateTabFolderMenuHandler(this); + Assert.isNotNull(tabFolderMenuHandler); + doConfigureTabFolderMenuHandler(tabFolderMenuHandler); + + // Setup the tab folder toolbar handler + tabFolderToolbarHandler = doCreateTabFolderToolbarHandler(this); + Assert.isNotNull(tabFolderToolbarHandler); + doConfigureTabFolderToolbarHandler(tabFolderToolbarHandler); + + // Show the empty page control by default + switchToEmptyPageControl(); + } + + /** + * Creates the {@link PageBook} instance. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @return The page book instance. Must never be <code>null</code>. + */ + protected PageBook doCreatePageBookControl(Composite parent) { + return new PageBook(parent, SWT.NONE); + } + + /** + * Configure the given page book control. + * + * @param pagebook The page book control. Must not be <code>null</code>. + */ + protected void doConfigurePageBookControl(PageBook pagebook) { + Assert.isNotNull(pagebook); + + if (getContextHelpId() != null) + PlatformUI.getWorkbench().getHelpSystem().setHelp(pagebook, getContextHelpId()); + } + + /** + * Returns the context help id associated with the terminal console view instance. + * <p> + * <b>Note:</b> The default implementation returns the view id as context help id. + * + * @return The context help id or <code>null</code> if none is associated. + */ + @Override + public String getContextHelpId() { + return getViewSite().getId(); + } + + /** + * Creates the empty page control instance. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @return The empty page control instance. Must never be <code>null</code>. + */ + protected Control doCreateEmptyPageControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout()); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + return composite; + } + + /** + * Configures the empty page control. + * + * @param control The empty page control. Must not be <code>null</code>. + */ + protected void doConfigureEmptyPageControl(Control control) { + Assert.isNotNull(control); + } + + /** + * Creates the tab folder control instance. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @return The tab folder control instance. Must never be <code>null</code>. + */ + protected CTabFolder doCreateTabFolderControl(Composite parent) { + return new CTabFolder(parent, SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM | SWT.FLAT | SWT.BORDER); + } + + /** + * Configures the tab folder control. + * + * @param tabFolder The tab folder control. Must not be <code>null</code>. + */ + protected void doConfigureTabFolderControl(CTabFolder tabFolder) { + Assert.isNotNull(tabFolder); + + // Set the layout data + tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH)); + + // Set the tab gradient coloring from the global preferences + if (useGradientTabBackgroundColor()) { + tabFolder.setSelectionBackground(new Color[] { + JFaceResources.getColorRegistry().get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START"), //$NON-NLS-1$ + JFaceResources.getColorRegistry().get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_END") //$NON-NLS-1$ + }, + new int[] {100}, true); + } + // Apply the tab folder selection foreground color + tabFolder.setSelectionForeground(JFaceResources.getColorRegistry().get("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR")); //$NON-NLS-1$ + + // Set the tab style from the global preferences + tabFolder.setSimple(PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS)); + } + + /** + * If <code>True</code> is returned, the inner tabs are colored with + * gradient coloring set in the Eclipse workbench color settings. + * + * @return <code>True</code> to use gradient tab colors, <code>false</code> otherwise. + */ + protected boolean useGradientTabBackgroundColor() { + return false; + } + + /** + * Creates the tab folder manager. + * + * @param parentView The parent view instance. Must be not <code>null</code>. + * @return The tab folder manager. Must never be <code>null</code>. + */ + protected TabFolderManager doCreateTabFolderManager(ITerminalsView parentView) { + Assert.isNotNull(parentView); + return new TabFolderManager(parentView); + } + + /** + * Creates the tab folder menu handler. + * + * @param parentView The parent view instance. Must be not <code>null</code>. + * @return The tab folder menu handler. Must never be <code>null</code>. + */ + protected TabFolderMenuHandler doCreateTabFolderMenuHandler(ITerminalsView parentView) { + Assert.isNotNull(parentView); + return new TabFolderMenuHandler(parentView); + } + + /** + * Configure the tab folder menu handler + * + * @param menuHandler The tab folder menu handler. Must not be <code>null</code>. + */ + protected void doConfigureTabFolderMenuHandler(TabFolderMenuHandler menuHandler) { + Assert.isNotNull(menuHandler); + menuHandler.initialize(); + } + + /** + * Creates the tab folder toolbar handler. + * + * @param parentView The parent view instance. Must be not <code>null</code>. + * @return The tab folder toolbar handler. Must never be <code>null</code>. + */ + protected TabFolderToolbarHandler doCreateTabFolderToolbarHandler(ITerminalsView parentView) { + Assert.isNotNull(parentView); + return new TabFolderToolbarHandler(parentView); + } + + /** + * Configure the tab folder toolbar handler + * + * @param toolbarHandler The tab folder toolbar handler. Must not be <code>null</code>. + */ + protected void doConfigureTabFolderToolbarHandler(TabFolderToolbarHandler toolbarHandler) { + Assert.isNotNull(toolbarHandler); + toolbarHandler.initialize(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + pageBookControl.setFocus(); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.terminals.interfaces.ITerminalsView#switchToEmptyPageControl() + */ + @Override + public void switchToEmptyPageControl() { + if (!pageBookControl.isDisposed() && !emptyPageControl.isDisposed()) pageBookControl.showPage(emptyPageControl); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.terminals.interfaces.ITerminalsView#switchToTabFolderControl() + */ + @Override + public void switchToTabFolderControl() { + if (!pageBookControl.isDisposed() && !tabFolderControl.isDisposed()) pageBookControl.showPage(tabFolderControl); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class) + */ + @Override + public Object getAdapter(Class adapter) { + if (CTabFolder.class.isAssignableFrom(adapter)) { + return tabFolderControl; + } else if (TabFolderManager.class.isAssignableFrom(adapter)) { + return tabFolderManager; + } else if (TabFolderMenuHandler.class.isAssignableFrom(adapter)) { + return tabFolderMenuHandler; + } else if (TabFolderToolbarHandler.class.isAssignableFrom(adapter)) { + return tabFolderToolbarHandler; + } + + return super.getAdapter(adapter); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/activator/UIPlugin.java index 930f47896..49587c040 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/activator/UIPlugin.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/activator/UIPlugin.java @@ -92,7 +92,7 @@ public class UIPlugin extends AbstractUIPlugin { @Override protected void initializeImageRegistry(ImageRegistry registry) { URL url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_EVIEW + "prop_ps.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_EDITOR, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.EDITOR, ImageDescriptor.createFromURL(url)); } /** diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/interfaces/ImageConsts.java b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/interfaces/ImageConsts.java index bc7c5fedb..1b63845dc 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/interfaces/ImageConsts.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/interfaces/ImageConsts.java @@ -28,5 +28,5 @@ public interface ImageConsts { /** * The key to access the Target Explorer editor image. */ - public static final String IMAGE_EDITOR = "TargetExplorerEditor"; //$NON-NLS-1$ + public static final String EDITOR = "TargetExplorerEditor"; //$NON-NLS-1$ } diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/actions/NewActionProvider.java b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/actions/NewActionProvider.java index 6aa9cc4fa..377f846e3 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/actions/NewActionProvider.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/actions/NewActionProvider.java @@ -59,8 +59,8 @@ public class NewActionProvider extends CommonActionProvider { window.getWorkbench().getHelpSystem().setHelp(newWizardCommandAction, IContextHelpIds.NEW_TARGET_WIZARD);
newWizardCommandActionToolbar = new CommandAction(window, "org.eclipse.tm.te.ui.command.newWizards"); //$NON-NLS-1$
- newWizardCommandActionToolbar.setImageDescriptor(org.eclipse.tm.te.ui.activator.UIPlugin.getImageDescriptor(ImageConsts.IMAGE_NEW_TARGET_WIZARD_ENABLED));
- newWizardCommandActionToolbar.setDisabledImageDescriptor(org.eclipse.tm.te.ui.activator.UIPlugin.getImageDescriptor(ImageConsts.IMAGE_NEW_TARGET_WIZARD_DISABLED));
+ newWizardCommandActionToolbar.setImageDescriptor(org.eclipse.tm.te.ui.activator.UIPlugin.getImageDescriptor(ImageConsts.NEW_TARGET_WIZARD_ENABLED));
+ newWizardCommandActionToolbar.setDisabledImageDescriptor(org.eclipse.tm.te.ui.activator.UIPlugin.getImageDescriptor(ImageConsts.NEW_TARGET_WIZARD_DISABLED));
newWizardCommandActionToolbar.setText(Messages.NewActionProvider_NewWizardCommandAction_label);
newWizardCommandActionToolbar.setToolTipText(Messages.NewActionProvider_NewWizardCommandAction_tooltip);
window.getWorkbench().getHelpSystem().setHelp(newWizardCommandActionToolbar, IContextHelpIds.NEW_TARGET_WIZARD);
@@ -100,7 +100,7 @@ public class NewActionProvider extends CommonActionProvider { // Create the new sub menu
IMenuManager newMenu = new MenuManager(Messages.NewActionProvider_NewMenu_label,
- org.eclipse.tm.te.ui.activator.UIPlugin.getImageDescriptor(ImageConsts.IMAGE_NEW_TARGET_WIZARD_ENABLED),
+ org.eclipse.tm.te.ui.activator.UIPlugin.getImageDescriptor(ImageConsts.NEW_TARGET_WIZARD_ENABLED),
IUIConstants.ID_EXPLORER + ".menu.new"); //$NON-NLS-1$
// Add the context sensitive wizards (commonWizard element)
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/editor/EditorInput.java b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/editor/EditorInput.java index cdaf7c9ed..c2e4ee166 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/editor/EditorInput.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui.views/src/org/eclipse/tm/te/ui/views/internal/editor/EditorInput.java @@ -92,7 +92,7 @@ public class EditorInput implements IEditorInput, IPersistableElement { */ @Override public ImageDescriptor getImageDescriptor() { - return UIPlugin.getImageDescriptor(ImageConsts.IMAGE_EDITOR); + return UIPlugin.getImageDescriptor(ImageConsts.EDITOR); } /* (non-Javadoc) diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.properties b/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.properties index 02a60ccf5..a5feb20aa 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.properties +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.properties @@ -28,4 +28,4 @@ Command.newWizards.description=Open the New target wizard # ***** Executors / Executor Utility Delegates ***** -EclipsePlatformDisplayExecutorUtilDelegate.label=Eclipse Platform Display Executor Utility Delegate +SWTDisplayExecutorUtilDelegate.label=SWT Display Executor Utility Delegate diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.xml b/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.xml index 5fde8f5d2..3254da5df 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.xml +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/plugin.xml @@ -60,18 +60,18 @@ <!-- Executor service contributions -->
<extension point="org.eclipse.tm.te.runtime.concurrent.executorServices">
<executorService
- id="org.eclipse.tm.te.ui.executors.platform.display"
+ id="org.eclipse.tm.te.ui.executors.SWTDisplay"
label="Eclipse Platform Display Executor"
- class="org.eclipse.tm.te.ui.internal.executors.EclipsePlatformDisplayExecutor">
+ class="org.eclipse.tm.te.ui.internal.executors.SWTDisplayExecutor">
</executorService>
</extension>
<!-- Executor utility delegate contributions -->
<extension point="org.eclipse.tm.te.runtime.concurrent.executorUtilDelegates">
<executorUtilDelegate
- class="org.eclipse.tm.te.ui.internal.executors.EclipsePlatformDisplayExecutorUtilDelegate"
- id="org.eclipse.tm.te.ui.executors.delegate.display"
- label="%EclipsePlatformDisplayExecutorUtilDelegate.label">
+ class="org.eclipse.tm.te.ui.internal.executors.SWTDisplayExecutorUtilDelegate"
+ id="org.eclipse.tm.te.ui.executors.delegate.SWTDisplay"
+ label="%SWTDisplayExecutorUtilDelegate.label">
</executorUtilDelegate>
</extension>
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/activator/UIPlugin.java index 27427ba60..4705b207c 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/activator/UIPlugin.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/activator/UIPlugin.java @@ -89,13 +89,13 @@ public class UIPlugin extends AbstractUIPlugin { @Override protected void initializeImageRegistry(ImageRegistry registry) { URL url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_WIZBAN + "newtarget_wiz.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_NEW_TARGET_WIZARD, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.NEW_TARGET_WIZARD, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_DLCL + "newtarget_wiz.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_NEW_TARGET_WIZARD_DISABLED, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.NEW_TARGET_WIZARD_DISABLED, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_ELCL + "newtarget_wiz.gif"); //$NON-NLS-1$ - registry.put(ImageConsts.IMAGE_NEW_TARGET_WIZARD_ENABLED, ImageDescriptor.createFromURL(url)); + registry.put(ImageConsts.NEW_TARGET_WIZARD_ENABLED, ImageDescriptor.createFromURL(url)); } /** diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/IUIConstants.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/IUIConstants.java index d5170d922..9ac6f85f5 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/IUIConstants.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/IUIConstants.java @@ -13,8 +13,6 @@ import org.eclipse.tm.te.ui.activator.UIPlugin; /** * Target Explorer: Common UI constants. - * - * @author uwe.stieber@windriver.com */ public interface IUIConstants { diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/ImageConsts.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/ImageConsts.java index edd7a7036..55619f7d1 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/ImageConsts.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/ImageConsts.java @@ -40,15 +40,15 @@ public interface ImageConsts { /** * The key to access the New target wizard banner image. */ - public static final String IMAGE_NEW_TARGET_WIZARD = "NewTargetWizard"; //$NON-NLS-1$ + public static final String NEW_TARGET_WIZARD = "NewTargetWizard"; //$NON-NLS-1$ /** * The key to access the New target wizard image (enabled). */ - public static final String IMAGE_NEW_TARGET_WIZARD_ENABLED = "NewTargetWizard_enabled"; //$NON-NLS-1$ + public static final String NEW_TARGET_WIZARD_ENABLED = "NewTargetWizard_enabled"; //$NON-NLS-1$ /** * The key to access the New target wizard image (disabled). */ - public static final String IMAGE_NEW_TARGET_WIZARD_DISABLED = "NewTargetWizard_disabled"; //$NON-NLS-1$ + public static final String NEW_TARGET_WIZARD_DISABLED = "NewTargetWizard_disabled"; //$NON-NLS-1$ } diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/EclipsePlatformDisplayExecutor.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/SWTDisplayExecutor.java index dcfb218f0..120421a4c 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/EclipsePlatformDisplayExecutor.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/SWTDisplayExecutor.java @@ -18,28 +18,29 @@ import org.eclipse.tm.te.ui.activator.UIPlugin; import org.eclipse.ui.PlatformUI; /** - * Eclipse platform display executor implementation utilizing the platform display. + * SWT display executor implementation utilizing the platform display. */ -public class EclipsePlatformDisplayExecutor extends ExecutableExtension implements IExecutor, ISingleThreadedExecutor, INestableExecutor { +public class SWTDisplayExecutor extends ExecutableExtension implements IExecutor, ISingleThreadedExecutor, INestableExecutor { /* (non-Javadoc) * @see java.util.concurrent.Executor#execute(java.lang.Runnable) */ @Override public void execute(Runnable command) { - // In case we do have a display, just execute the runnable asynchronously using this display - if (PlatformUI.isWorkbenchRunning() && - PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getDisplay() != null) { + // Try the platform display first + if (PlatformUI.isWorkbenchRunning() + && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getDisplay() != null + && !PlatformUI.getWorkbench().getDisplay().isDisposed()) { PlatformUI.getWorkbench().getDisplay().asyncExec(command); } else { - // Check if the current thread is the display thread + // Fallback to the display associated with the current thread Display display = Display.findDisplay(Thread.currentThread()); - // if we got the display for the riverbed dispatch thread, we can execute the - // original runnable now - if (display != null) { + // If there is a display associated with the current thread, + // execute the runnable using that display instance. + if (display != null && !display.isDisposed()) { display.asyncExec(command); } else { - // Well, we don't have any display to execute the runnable at. + // There is no display to execute the runnable at. // Drop execution and write a trace message if enabled UIPlugin.getTraceHandler().trace("DROPPED display command invocation. No display instance found.!", 1, this); //$NON-NLS-1$ } @@ -60,9 +61,16 @@ public class EclipsePlatformDisplayExecutor extends ExecutableExtension implemen @Override public boolean isExecutorThread(Thread thread) { if (thread != null) { - // Find the display for this thread + // Try the platform display first + if (PlatformUI.isWorkbenchRunning() + && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getDisplay() != null + && !PlatformUI.getWorkbench().getDisplay().isDisposed()) { + return thread.equals(PlatformUI.getWorkbench().getDisplay().getThread()); + } + + // Fallback to the display associated with the current thread Display display = Display.findDisplay(thread); - if (display != null) { + if (display != null && !display.isDisposed()) { return thread.equals(display.getThread()); } } @@ -82,8 +90,16 @@ public class EclipsePlatformDisplayExecutor extends ExecutableExtension implemen */ @Override public boolean readAndExecute() { + // Try the platform display first + if (PlatformUI.isWorkbenchRunning() + && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getDisplay() != null + && !PlatformUI.getWorkbench().getDisplay().isDisposed()) { + return PlatformUI.getWorkbench().getDisplay().readAndDispatch(); + } + + // Fallback to the display associated with the current thread Display display = Display.getCurrent(); - if (display != null) { + if (display != null && !display.isDisposed()) { return display.readAndDispatch(); } return false; diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/EclipsePlatformDisplayExecutorUtilDelegate.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/SWTDisplayExecutorUtilDelegate.java index 48e51779e..d456aa42c 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/EclipsePlatformDisplayExecutorUtilDelegate.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/internal/executors/SWTDisplayExecutorUtilDelegate.java @@ -14,9 +14,9 @@ import org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutorUtilDelegate; import org.eclipse.tm.te.runtime.extensions.ExecutableExtension; /** - * Eclipse platform display executor utility delegate implementation. + * SWT display executor utility delegate implementation. */ -public class EclipsePlatformDisplayExecutorUtilDelegate extends ExecutableExtension implements IExecutorUtilDelegate { +public class SWTDisplayExecutorUtilDelegate extends ExecutableExtension implements IExecutorUtilDelegate { /* (non-Javadoc) * @see org.eclipse.tm.te.runtime.concurrent.interfaces.IExecutorUtilDelegate#isHandledExecutorThread() diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/wizards/newWizard/NewWizard.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/wizards/newWizard/NewWizard.java index f79157a5a..dd7fecef3 100644 --- a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/wizards/newWizard/NewWizard.java +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/wizards/newWizard/NewWizard.java @@ -64,7 +64,7 @@ public final class NewWizard extends Wizard implements INewWizard { */
@Override
public Image getDefaultPageImage() {
- return UIPlugin.getImage(ImageConsts.IMAGE_NEW_TARGET_WIZARD);
+ return UIPlugin.getImage(ImageConsts.NEW_TARGET_WIZARD);
}
|