Introduce "New view" action for views based on TmfView
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Wed, 26 Oct 2016 22:27:27 +0000 (18:27 -0400)
committerJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Fri, 11 Nov 2016 15:11:13 +0000 (10:11 -0500)
All views based on TmfView now have a new action button in their
expandable menu providing an easy way to spawn a view of the same type.

Introduce a TmfViewFactory Singleton.

This is an initial work toward providing a cloning ability to TmfView based
views.

Change-Id: Ie8a0c4af305edb6f14968188cfd8feda8983a8cd
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
doc/org.eclipse.tracecompass.doc.user/doc/User-Guide.mediawiki
tmf/org.eclipse.tracecompass.tmf.analysis.xml.ui/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/ui/views/timegraph/XmlTimeGraphView.java
tmf/org.eclipse.tracecompass.tmf.analysis.xml.ui/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/ui/views/xychart/XmlXYView.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/Messages.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/messages.properties
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/NewTmfViewAction.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfView.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfViewFactory.java [new file with mode: 0644]

index f9f51ed37e7398005d07747a345621c0bdeccee7..17db4ac223f8521b79452ebd5a7fa0dede1e12d4 100644 (file)
@@ -2056,6 +2056,10 @@ View Menu
 
 {|
 |
+| New Control Flow view
+| Spawn a new control flow view.
+|-
+|
 | Show Markers
 | A marker highlights a time interval. A marker can be used for instance to indicate a time range where lost events occurred or to bookmark an interesting interval for future reference. Selecting a category name will toggle the visibility of markers of that category.
 |-
@@ -2186,6 +2190,10 @@ View Menu
 
 {|
 |
+| New Resources view
+| Spawn a new resources view.
+|-
+|
 | Show Markers
 | A marker highlights a time interval. A marker can be used for instance to indicate a time range where lost events occurred or to bookmark an interesting interval for future reference. Selecting a category name will toggle the visibility of markers of that category.
 |}
index 1324fbdb2f31c85337f722591a7dc812b943b9d3..cbb10834ed8ee03354f1b925ab5cd61665bc1124 100644 (file)
@@ -56,6 +56,7 @@ import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
 import org.eclipse.tracecompass.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.ui.views.TmfViewFactory;
 import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
@@ -137,7 +138,7 @@ public class XmlTimeGraphView extends AbstractTimeGraphView {
     @Override
     public void createPartControl(Composite parent) {
         super.createPartControl(parent);
-        fViewInfo.setName(NonNullUtils.checkNotNull(getViewSite().getSecondaryId()));
+        fViewInfo.setName(NonNullUtils.checkNotNull(TmfViewFactory.getBaseSecId(getViewSite().getSecondaryId())));
     }
 
     private void loadNewXmlView() {
index 17a805a91b31921e16c19afa9bbe11339fb3e15b..5553cf4f70bc93c86905d511f266c998ce6c882a 100644 (file)
@@ -22,6 +22,7 @@ import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.TmfXmlUiStrings;
 import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.views.XmlViewInfo;
 import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer;
 import org.eclipse.tracecompass.tmf.ui.views.TmfChartView;
+import org.eclipse.tracecompass.tmf.ui.views.TmfViewFactory;
 import org.w3c.dom.Element;
 
 /**
@@ -94,7 +95,7 @@ public class XmlXYView extends TmfChartView {
     @Override
     public void createPartControl(@Nullable Composite parent) {
         super.createPartControl(parent);
-        fViewInfo.setName(NonNullUtils.checkNotNull(getViewSite().getSecondaryId()));
+        fViewInfo.setName(NonNullUtils.checkNotNull(TmfViewFactory.getBaseSecId(getViewSite().getSecondaryId())));
         setViewTitle();
     }
 
index d1ec202d25ceb503200d02d418a84ea0e877d07d..44c73aa290cc9563b294adbe19e4ba88f9b0c65b 100644 (file)
@@ -322,6 +322,8 @@ public class Messages extends NLS {
     public static String TmfView_PinActionToolTipText;
     public static String TmfView_AlignViewsActionNameText;
     public static String TmfView_AlignViewsActionToolTipText;
+    public static String TmfView_NewTmfViewNameText;
+    public static String TmfView_NewTmfViewToolTipText;
 
     public static String CallStackPresentationProvider_Thread;
     public static String CallStackView_FunctionColumn;
index e2e0f058e7475dc0a0f0a9e004a258b9c94d19e6..c07e4e4fa2338a81f3a7a518364b98bdc2fc0a28 100644 (file)
@@ -323,6 +323,8 @@ TmfView_PinActionNameText=Pin View
 TmfView_PinActionToolTipText=Pin View
 TmfView_AlignViewsActionNameText=Align Views
 TmfView_AlignViewsActionToolTipText=Align Views
+TmfView_NewTmfViewNameText=&New {0} view
+TmfView_NewTmfViewToolTipText=Spawn a new {0} view;
 
 # org.eclipse.tracecompass.tmf.ui.views.callstack
 CallStackPresentationProvider_Thread=Thread
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/NewTmfViewAction.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/NewTmfViewAction.java
new file mode 100644 (file)
index 0000000..0376729
--- /dev/null
@@ -0,0 +1,35 @@
+/**********************************************************************
+ * Copyright (c) 2016 EfficiOS Inc.
+ *
+ * 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
+ **********************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.views;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.tracecompass.internal.tmf.ui.Messages;
+
+/**
+ * Action to instantiate a new instance of views that support it.
+ * @author Jonathan Rajotte Julien
+ * @since 2.2
+ */
+public class NewTmfViewAction extends Action {
+
+    /**
+     * Creates a new <code>NewTmfViewAction</code>.
+     *
+     * @param view
+     *            The view for which the action is created
+     */
+    public NewTmfViewAction(TmfView view) {
+        super(MessageFormat.format(Messages.TmfView_NewTmfViewNameText, view.getTitle().toLowerCase()), IAction.AS_PUSH_BUTTON);
+        setToolTipText(MessageFormat.format(Messages.TmfView_NewTmfViewToolTipText, view.getTitle()));
+    }
+}
index f073027a660d920c80f6719f583ae03340f26124..9c4b451f6d77664408e8f67f1a194d094daaf18f 100644 (file)
@@ -14,6 +14,9 @@
 
 package org.eclipse.tracecompass.tmf.ui.views;
 
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.swt.events.ControlAdapter;
@@ -51,8 +54,22 @@ public abstract class TmfView extends ViewPart implements ITmfComponent {
      * Action class for pinning of TmfView.
      */
     protected PinTmfViewAction fPinAction;
+
+    /**
+     * Action class for spawning a new view based on this view type.
+     *
+     * @since 2.2
+     */
+    private NewTmfViewAction fNewAction;
+
     private static TimeAlignViewsAction fAlignViewsAction;
 
+    /**
+     * The separator used for distinguishing between primary and secondary id of a view id.
+     * @since 2.2
+     */
+    public static final String PRIMARY_SECONDARY_ID_SEPARATOR = ":"; //$NON-NLS-1$
+
     // ------------------------------------------------------------------------
     // Constructor
     // ------------------------------------------------------------------------
@@ -132,9 +149,35 @@ public abstract class TmfView extends ViewPart implements ITmfComponent {
         }
     }
 
+    /**
+     * Add the "New view" action to the expandable menu. This action spawn a new
+     * view of the same type as the caller.
+     *
+     * @since 2.2
+     */
+    private void contributeNewActionToMenu(IMenuManager menuManager) {
+        if (fNewAction == null) {
+            fNewAction = new NewTmfViewAction(TmfView.this) {
+                @Override
+                public void run() {
+                    TmfViewFactory.newView(checkNotNull(TmfView.this.getViewId()), true);
+                }
+            };
+            menuManager.add(fNewAction);
+        }
+    }
+
     @Override
     public void createPartControl(final Composite parent) {
         fParentComposite = parent;
+        IMenuManager menuManager = getViewSite().getActionBars()
+                .getMenuManager();
+
+        /* Add to menu */
+        contributeNewActionToMenu(menuManager);
+        menuManager.add(new Separator());
+
+
         if (this instanceof ITmfTimeAligned) {
             contributeAlignViewsActionToToolbar();
 
@@ -215,6 +258,6 @@ public abstract class TmfView extends ViewPart implements ITmfComponent {
         if (secondaryId == null) {
             return viewSite.getId();
         }
-        return viewSite.getId() + ':' + secondaryId;
+        return viewSite.getId() + PRIMARY_SECONDARY_ID_SEPARATOR + secondaryId;
     }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfViewFactory.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfViewFactory.java
new file mode 100644 (file)
index 0000000..cc2ee13
--- /dev/null
@@ -0,0 +1,169 @@
+/**********************************************************************
+ * Copyright (c) 2016 EfficiOS Inc.
+ *
+ * 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
+ **********************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.views;
+
+import java.util.UUID;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Factory for TmfView.
+ *
+ * @author Jonathan Rajotte Julien
+ * @since 2.2
+ */
+public final class TmfViewFactory {
+
+    /**
+     * The separator used for secondary id internal use. This allow to have
+     * multiple level of information inside the secondary id.
+     */
+    @VisibleForTesting
+    public static final String INTERNAL_SECONDARY_ID_SEPARATOR = "&"; //$NON-NLS-1$
+
+    /**
+     * Empty constructor
+     */
+    private TmfViewFactory() {}
+
+    /**
+     * Create a new view. <br>
+     *
+     * If a view with the corresponding id already exist and no suffix were
+     * added the existing view will be given focus.
+     *
+     * @param viewId
+     *            The id of the view to be created. <br>
+     *            Format: <primary_id>[:secondary_id][&third_id]..[&n_id]
+     * @param generateSuffix
+     *            Add a generated suffix id (UUID). This allow multiple view of
+     *            the same id to be displayed.
+     * @return
+     *          NULL if an error occurred.
+     *          The view instance.
+     */
+    @NonNullByDefault
+    public static @Nullable IViewPart newView(String viewId, boolean generateSuffix) {
+        IViewPart viewPart = null;
+        String primaryId = null;
+        String secondaryId = null;
+
+        /* Parse the view id */
+        if (viewId.contains(TmfView.PRIMARY_SECONDARY_ID_SEPARATOR)) {
+            int index = viewId.indexOf(TmfView.PRIMARY_SECONDARY_ID_SEPARATOR);
+
+            primaryId = viewId.substring(0, index);
+            secondaryId = getBaseSecId(viewId.substring(index + 1));
+
+        } else {
+            primaryId = viewId;
+        }
+
+        if (generateSuffix) {
+            if (secondaryId == null) {
+                secondaryId = UUID.randomUUID().toString();
+            } else {
+                secondaryId += INTERNAL_SECONDARY_ID_SEPARATOR + UUID.randomUUID().toString();
+            }
+        }
+
+        IWorkbench workbench = PlatformUI.getWorkbench();
+        IWorkbenchWindow workbenchWindow = workbench.getActiveWorkbenchWindow();
+        IWorkbenchPage page = workbenchWindow.getActivePage();
+        try {
+            viewPart = page.showView(primaryId, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
+            page.activate(viewPart);
+        } catch (PartInitException e) {
+            /* Simply return null on error */
+        }
+
+        return viewPart;
+    }
+
+    /**
+     * Parse a secondary id and return the base secondary id minus any generated
+     * suffix (UUID).
+     *
+     * @param secId
+     *            A view secondary id
+     * @return NULL when the passed string is a UUID. <br>
+     *         The base secondary id
+     */
+    public static String getBaseSecId(String secId) {
+        if (secId == null) {
+            return null;
+        }
+
+        if (!secId.contains(INTERNAL_SECONDARY_ID_SEPARATOR)) {
+            if (isUUID(secId)) {
+                return null;
+            }
+            return secId;
+        }
+
+        int lastIndexSepartor = secId.lastIndexOf(INTERNAL_SECONDARY_ID_SEPARATOR);
+
+        /**
+         * Validate that the right side of the separator is a UUID since the
+         * separator could be a valid value from the base secondary id.
+         */
+        String potentialUUID = secId.substring(lastIndexSepartor + 1);
+
+        if (!isUUID(potentialUUID)) {
+            return secId;
+        }
+
+        return secId.substring(0, lastIndexSepartor);
+    }
+
+    /**
+     * Utility method for testing if a string is a valid full length UUID.
+     * <br>
+     * <pre>
+     * e.g:
+     *     9eaf1840-8a87-4314-a8b7-03e3eccf4766 -> true
+     *     1-1-1-1-1 -> false
+     * </pre>
+     *
+     * @param uuid
+     *            The string to test
+     * @return If the passed string is a UUID
+     */
+    private static boolean isUUID(String uuid) {
+        if (uuid == null) {
+            return false;
+        }
+
+        try {
+            /*
+             * UUID.fromString does not check for length wise valid UUID only the
+             * UUID form so check if the reverse operation is valid.
+             */
+            UUID fromStringUUID = UUID.fromString(uuid);
+            String toStringUUID = fromStringUUID.toString();
+            return toStringUUID.equals(uuid);
+        } catch (IllegalArgumentException e) {
+            /**
+             * The substring is not a UUID. Assume that the separator come from
+             * the initial secondaryId.
+             */
+             return false;
+        }
+    }
+}
This page took 0.036525 seconds and 5 git commands to generate.