diff options
Diffstat (limited to 'org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views')
154 files changed, 40649 insertions, 0 deletions
diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/PinTmfViewAction.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/PinTmfViewAction.java new file mode 100644 index 0000000000..4eb11629b7 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/PinTmfViewAction.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2012 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + *******************************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; + +/** + * + * @version 1.0 + * @author Bernd Hufmann + * @since 2.0 + */ +public class PinTmfViewAction extends Action { + /** + * Creates a new <code>PinPropertySheetAction</code>. + */ + public PinTmfViewAction() { + super(Messages.TmfView_PinActionNameText, IAction.AS_CHECK_BOX); + + setId("org.eclipse.linuxtools.tmf.ui.views.PinTmfViewAction"); //$NON-NLS-1$ + setToolTipText(Messages.TmfView_PinActionToolTipText); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PIN_VIEW)); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfChartView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfChartView.java new file mode 100644 index 0000000000..99c692fb87 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfChartView.java @@ -0,0 +1,118 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer; + +/** + * Base class to be used with a chart viewer {@link TmfXYChartViewer}. + * It is responsible to instantiate the viewer class and load the trace + * into the viewer when the view is created. + * + * @author Bernd Hufmann + * @since 3.0 + */ +abstract public class TmfChartView extends TmfView { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** The TMF XY Chart reference */ + private TmfXYChartViewer fChartViewer; + /** The Trace reference */ + private ITmfTrace fTrace; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Standard Constructor + * + * @param viewName + * The view name + */ + public TmfChartView(String viewName) { + super(viewName); + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + /** + * Returns the TMF XY chart viewer implementation. + * + * @return the TMF XY chart viewer {@link TmfXYChartViewer} + */ + protected TmfXYChartViewer getChartViewer() { + return fChartViewer; + } + + /** + * Sets the TMF XY chart viewer implementation. + * + * @param chartViewer + * The TMF XY chart viewer {@link TmfXYChartViewer} + */ + protected void setChartViewer(TmfXYChartViewer chartViewer) { + fChartViewer = chartViewer; + } + + /** + * Returns the ITmfTrace implementation + * + * @return the ITmfTrace implementation {@link ITmfTrace} + */ + protected ITmfTrace getTrace() { + return fTrace; + } + + /** + * Sets the ITmfTrace implementation + * + * @param trace + * The ITmfTrace implementation {@link ITmfTrace} + */ + protected void setTrace(ITmfTrace trace) { + fTrace = trace; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + @Override + public void createPartControl(Composite parent) { + ITmfTrace trace = getActiveTrace(); + if (trace != null) { + setTrace(trace); + loadTrace(); + } + } + + @Override + public void dispose() { + if (fChartViewer != null) { + fChartViewer.dispose(); + } + } + + /** + * Load the trace into view. + */ + protected void loadTrace() { + if (fChartViewer != null) { + fChartViewer.loadTrace(fTrace); + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfView.java new file mode 100644 index 0000000000..cc874e32f9 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TmfView.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2009, 2014 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Bernd Hufmann - Added possibility to pin view + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views; + +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.tracecompass.tmf.core.component.ITmfComponent; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.part.ViewPart; + +/** + * Basic abstract TMF view class implementation. + * + * It registers any sub class to the signal manager for receiving and sending + * TMF signals. + * + * @version 1.2 + * @author Francois Chouinard + */ +public abstract class TmfView extends ViewPart implements ITmfComponent { + + private final String fName; + + /** + * Action class for pinning of TmfView. + * @since 2.0 + */ + protected PinTmfViewAction fPinAction; + + /** + * Reference to the trace manager + * @since 2.0 + */ + protected final TmfTraceManager fTraceManager; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructor. Creates a TMF view and registers to the signal manager. + * + * @param viewName + * A view name + */ + public TmfView(String viewName) { + super(); + fName = viewName; + fTraceManager = TmfTraceManager.getInstance(); + TmfSignalManager.register(this); + } + + /** + * Disposes this view and de-registers itself from the signal manager + */ + @Override + public void dispose() { + TmfSignalManager.deregister(this); + super.dispose(); + } + + // ------------------------------------------------------------------------ + // ITmfComponent + // ------------------------------------------------------------------------ + + @Override + public String getName() { + return fName; + } + + @Override + public void broadcast(TmfSignal signal) { + TmfSignalManager.dispatchSignal(signal); + } + + /** + * @since 3.0 + */ + @Override + public void broadcastAsync(TmfSignal signal) { + TmfSignalManager.dispatchSignalAsync(signal); + } + + // ------------------------------------------------------------------------ + // View pinning support + // ------------------------------------------------------------------------ + + /** + * Returns whether the pin flag is set. + * For example, this flag can be used to ignore time synchronization signals from other TmfViews. + * + * @return pin flag + * @since 2.0 + */ + public boolean isPinned() { + return ((fPinAction != null) && (fPinAction.isChecked())); + } + + /** + * Method adds a pin action to the TmfView. The pin action allows to toggle the <code>fIsPinned</code> flag. + * For example, this flag can be used to ignore time synchronization signals from other TmfViews. + * + * @since 2.0 + */ + protected void contributePinActionToToolBar() { + if (fPinAction == null) { + fPinAction = new PinTmfViewAction(); + + IToolBarManager toolBarManager = getViewSite().getActionBars() + .getToolBarManager(); + toolBarManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + toolBarManager.add(fPinAction); + } + } + + /** + * Get the currently selected trace, or 'null' if the active editor is not a + * TMF trace. + * + * @return The active trace, or 'null' if not a trace + * @since 2.0 + */ + public ITmfTrace getActiveTrace() { + IEditorPart editor = getSite().getPage().getActiveEditor(); + if (editor instanceof ITmfTraceEditor) { + ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace(); + return trace; + } + return null; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TracingPerspectiveFactory.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TracingPerspectiveFactory.java new file mode 100644 index 0000000000..2cc89f70f5 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/TracingPerspectiveFactory.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views; + +import org.eclipse.tracecompass.tmf.ui.project.wizards.NewTmfProjectWizard; +import org.eclipse.tracecompass.tmf.ui.views.histogram.HistogramView; +import org.eclipse.tracecompass.tmf.ui.views.statistics.TmfStatisticsView; +import org.eclipse.ui.IFolderLayout; +import org.eclipse.ui.IPageLayout; +import org.eclipse.ui.IPerspectiveFactory; + +/** + * The tracing perspective definition. + * + * @version 1.0 + * @author Francois Chouinard + */ +public class TracingPerspectiveFactory implements IPerspectiveFactory { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** The Perspective ID */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.perspective"; //$NON-NLS-1$ + + // Standard TMF views + private static final String HISTOGRAM_VIEW_ID = HistogramView.ID; + + // Standard Eclipse views + private static final String PROJECT_VIEW_ID = IPageLayout.ID_PROJECT_EXPLORER; + private static final String STATISTICS_VIEW_ID = TmfStatisticsView.ID; + private static final String PROPERTIES_VIEW_ID = IPageLayout.ID_PROP_SHEET; + private static final String BOOKMARKS_VIEW_ID = IPageLayout.ID_BOOKMARKS; + + + // ------------------------------------------------------------------------ + // IPerspectiveFactory + // ------------------------------------------------------------------------ + + @Override + public void createInitialLayout(IPageLayout layout) { + + // Editor area + layout.setEditorAreaVisible(true); + + // Create the top left folder + IFolderLayout topLeftFolder = layout.createFolder( + "topLeftFolder", IPageLayout.LEFT, 0.15f, IPageLayout.ID_EDITOR_AREA); //$NON-NLS-1$ + topLeftFolder.addView(PROJECT_VIEW_ID); + + // Create the middle right folder + IFolderLayout middleRightFolder = layout.createFolder( + "middleRightFolder", IPageLayout.BOTTOM, 0.50f, IPageLayout.ID_EDITOR_AREA); //$NON-NLS-1$ + middleRightFolder.addView(STATISTICS_VIEW_ID); + + // Create the bottom right folder + IFolderLayout bottomRightFolder = layout.createFolder( + "bottomRightFolder", IPageLayout.BOTTOM, 0.55f, "middleRightFolder"); //$NON-NLS-1$ //$NON-NLS-2$ + bottomRightFolder.addView(HISTOGRAM_VIEW_ID); + bottomRightFolder.addView(PROPERTIES_VIEW_ID); + bottomRightFolder.addView(BOOKMARKS_VIEW_ID); + + // Populate menus, etc + layout.addPerspectiveShortcut(ID); + layout.addNewWizardShortcut(NewTmfProjectWizard.ID); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/AbstractCallStackAnalysis.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/AbstractCallStackAnalysis.java new file mode 100644 index 0000000000..7ccc51977d --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/AbstractCallStackAnalysis.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2014 Ericsson + * + * 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: + * Alexandre Montplaisir - Initial API and implementation + * Patrick Tasse - Add methods to get attribute paths + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.callstack; + +import org.eclipse.tracecompass.tmf.core.callstack.CallStackStateProvider; +import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; +import org.eclipse.tracecompass.tmf.ui.analysis.TmfAnalysisViewOutput; + +/** + * The base classes for analyses who want to populate the CallStack View. + * + * @author Alexandre Montplaisir + * @since 3.0 + */ +public abstract class AbstractCallStackAnalysis extends TmfStateSystemAnalysisModule { + + private static final String[] DEFAULT_THREADS_PATTERN = + new String[] { CallStackStateProvider.THREADS, "*" }; //$NON-NLS-1$; + + private static final String[] DEFAULT_CALL_STACK_PATH = + new String[] { CallStackStateProvider.CALL_STACK }; + + /** + * Abstract constructor (should only be called via the sub-classes' + * constructors. + */ + public AbstractCallStackAnalysis() { + super(); + registerOutput(new TmfAnalysisViewOutput(CallStackView.ID)); + } + + /** + * Get the pattern of thread attributes. Override this method if the state + * system attributes do not match the default pattern defined by + * {@link CallStackStateProvider}. + * + * @return the absolute pattern of the thread attributes + * @since 3.1 + */ + public String[] getThreadsPattern() { + return DEFAULT_THREADS_PATTERN; + } + + /** + * Get the call stack attribute path relative to a thread attribute found by + * {@link #getThreadsPattern()}. Override this method if the state system + * attributes do not match the default pattern defined by + * {@link CallStackStateProvider}. + * + * @return the relative path of the call stack attribute + * @since 3.1 + */ + public String[] getCallStackPath() { + return DEFAULT_CALL_STACK_PATH; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackEntry.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackEntry.java new file mode 100644 index 0000000000..31c831527b --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackEntry.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.callstack; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.linuxtools.statesystem.core.ITmfStateSystem; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry; + +/** + * An entry, or row, in the Call Stack view + * + * @author Patrick Tasse + * @since 2.0 + */ +public class CallStackEntry extends TimeGraphEntry { + + private final int fQuark; + private final int fStackLevel; + private final ITmfTrace fTrace; + private String fFunctionName; + private long fFunctionEntryTime; + private long fFunctionExitTime; + private @NonNull ITmfStateSystem fSS; + + /** + * Standard constructor + * + * @param quark + * The call stack quark + * @param stackLevel + * The stack level + * @param trace + * The trace that this view is talking about + * @deprecated Use {@link #CallStackEntry(String, int, int, ITmfTrace, ITmfStateSystem)} + */ + @Deprecated + public CallStackEntry(int quark, int stackLevel, ITmfTrace trace) { + super(null, 0, 0); + throw new UnsupportedOperationException(); + } + + /** + * Standard constructor + * + * @param name + * The parent thread name + * @param quark + * The call stack quark + * @param stackLevel + * The stack level + * @param trace + * The trace that this view is talking about + * @param ss + * The call stack state system + * @since 3.1 + */ + public CallStackEntry(String name, int quark, int stackLevel, ITmfTrace trace, @NonNull ITmfStateSystem ss) { + super(name, 0, 0); + fQuark = quark; + fStackLevel = stackLevel; + fTrace = trace; + fFunctionName = ""; //$NON-NLS-1$ + fSS = ss; + } + + /** + * Get the function name of the call stack entry + * @return the function name + */ + public String getFunctionName() { + return fFunctionName; + } + + /** + * Set the function name of the call stack entry + * @param functionName the function name + */ + public void setFunctionName(String functionName) { + fFunctionName = functionName; + } + + /** + * Set the start time of the call stack entry + * @param startTime the start time + * @deprecated Use {@link #setFunctionEntryTime(long)} + */ + @Deprecated + public void setStartTime(long startTime) { + throw new UnsupportedOperationException(); + } + + /** + * Set the end time of the call stack entry + * @param endTime the end time + * @deprecated Use {@link #setFunctionExitTime(long)} + */ + @Deprecated + public void setEndTime(long endTime) { + throw new UnsupportedOperationException(); + } + + /** + * Set the selected function entry time + * + * @param entryTime + * the function entry time + * @since 3.1 + */ + public void setFunctionEntryTime(long entryTime) { + fFunctionEntryTime = entryTime; + } + + /** + * Get the selected function entry time + * + * @return the function entry time + * @since 3.1 + */ + public long getFunctionEntryTime() { + return fFunctionEntryTime; + } + + /** + * Set the selected function exit time + * + * @param exitTime + * the function exit time + * @since 3.1 + */ + public void setFunctionExitTime(long exitTime) { + fFunctionExitTime = exitTime; + } + + /** + * Get the selected function exit time + * + * @return the function exit time + * @since 3.1 + */ + public long getFunctionExitTime() { + return fFunctionExitTime; + } + + /** + * Retrieve the attribute quark that's represented by this entry. + * + * @return The integer quark + */ + public int getQuark() { + return fQuark; + } + + /** + * Retrieve the stack level associated with this entry. + * + * @return The stack level or 0 + */ + public int getStackLevel() { + return fStackLevel; + } + + /** + * Retrieve the trace that is associated to this view. + * + * @return The trace + */ + public ITmfTrace getTrace() { + return fTrace; + } + + /** + * Retrieve the call stack state system associated with this entry. + * + * @return The call stack state system + * @since 3.1 + */ + public @NonNull ITmfStateSystem getStateSystem() { + return fSS; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackEvent.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackEvent.java new file mode 100644 index 0000000000..05fc4ae20f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackEvent.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.callstack; + +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent; + +/** + * Time Event implementation specific to the Call Stack View + * + * @author Patrick Tasse + * @since 2.0 + */ +public class CallStackEvent extends TimeEvent { + + /** + * Standard constructor + * + * @param entry + * The entry that this event affects + * @param time + * The start time of the event + * @param duration + * The duration of the event + * @param value + * The event value (1-256) + */ + public CallStackEvent(CallStackEntry entry, long time, long duration, int value) { + super(entry, time, duration, value); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackPresentationProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackPresentationProvider.java new file mode 100644 index 0000000000..e23e399652 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackPresentationProvider.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2013, 2014 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.callstack; + +import org.eclipse.linuxtools.statesystem.core.ITmfStateSystem; +import org.eclipse.linuxtools.statesystem.core.exceptions.AttributeNotFoundException; +import org.eclipse.linuxtools.statesystem.core.exceptions.StateSystemDisposedException; +import org.eclipse.linuxtools.statesystem.core.exceptions.TimeRangeException; +import org.eclipse.linuxtools.statesystem.core.statevalue.ITmfStateValue; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils; + +/** + * Presentation provider for the Call Stack view, based on the generic TMF + * presentation provider. + * + * @author Patrick Tasse + * @since 2.0 + */ +public class CallStackPresentationProvider extends TimeGraphPresentationProvider { + + /** Number of colors used for call stack events */ + public static final int NUM_COLORS = 360; + + private final CallStackView fView; + + private Integer fAverageCharWidth; + + private enum State { + MULTIPLE (new RGB(100, 100, 100)), + EXEC (new RGB(0, 200, 0)); + + private final RGB rgb; + + private State (RGB rgb) { + this.rgb = rgb; + } + } + + /** + * Constructor + * + * @param view + * The callstack view that will contain the time events + * @since 3.0 + */ + public CallStackPresentationProvider(CallStackView view) { + fView = view; + } + + @Override + public String getStateTypeName(ITimeGraphEntry entry) { + return Messages.CallStackPresentationProvider_Thread; + } + + @Override + public StateItem[] getStateTable() { + final float saturation = 0.6f; + final float brightness = 0.6f; + StateItem[] stateTable = new StateItem[NUM_COLORS + 1]; + stateTable[0] = new StateItem(State.MULTIPLE.rgb, State.MULTIPLE.toString()); + for (int i = 0; i < NUM_COLORS; i++) { + RGB rgb = new RGB(i, saturation, brightness); + stateTable[i + 1] = new StateItem(rgb, State.EXEC.toString()); + } + return stateTable; + } + + @Override + public int getStateTableIndex(ITimeEvent event) { + if (event instanceof CallStackEvent) { + CallStackEvent callStackEvent = (CallStackEvent) event; + return callStackEvent.getValue() + 1; + } else if (event instanceof NullTimeEvent) { + return INVISIBLE; + } + return State.MULTIPLE.ordinal(); + } + + @Override + public String getEventName(ITimeEvent event) { + if (event instanceof CallStackEvent) { + CallStackEntry entry = (CallStackEntry) event.getEntry(); + ITmfStateSystem ss = entry.getStateSystem(); + try { + ITmfStateValue value = ss.querySingleState(event.getTime(), entry.getQuark()).getStateValue(); + if (!value.isNull()) { + String address = value.toString(); + return fView.getFunctionName(address); + } + } catch (AttributeNotFoundException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (TimeRangeException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (StateSystemDisposedException e) { + /* Ignored */ + } + return null; + } + return State.MULTIPLE.toString(); + } + + @Override + public void postDrawEvent(ITimeEvent event, Rectangle bounds, GC gc) { + if (fAverageCharWidth == null) { + fAverageCharWidth = gc.getFontMetrics().getAverageCharWidth(); + } + if (bounds.width <= fAverageCharWidth) { + return; + } + if (!(event instanceof CallStackEvent)) { + return; + } + CallStackEntry entry = (CallStackEntry) event.getEntry(); + ITmfStateSystem ss = entry.getStateSystem(); + try { + ITmfStateValue value = ss.querySingleState(event.getTime(), entry.getQuark()).getStateValue(); + if (!value.isNull()) { + String address = value.toString(); + String name = fView.getFunctionName(address); + gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE)); + Utils.drawText(gc, name, bounds.x, bounds.y - 2, bounds.width, true, true); + } + } catch (AttributeNotFoundException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (TimeRangeException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (StateSystemDisposedException e) { + /* Ignored */ + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackView.java new file mode 100644 index 0000000000..58be6c2a22 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/CallStackView.java @@ -0,0 +1,1569 @@ +/******************************************************************************* + * Copyright (c) 2013, 2014 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + * Bernd Hufmann - Updated signal handling + * Marc-Andre Laperle - Map from binary file + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.callstack; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.linuxtools.statesystem.core.ITmfStateSystem; +import org.eclipse.linuxtools.statesystem.core.exceptions.AttributeNotFoundException; +import org.eclipse.linuxtools.statesystem.core.exceptions.StateSystemDisposedException; +import org.eclipse.linuxtools.statesystem.core.exceptions.StateValueTypeException; +import org.eclipse.linuxtools.statesystem.core.exceptions.TimeRangeException; +import org.eclipse.linuxtools.statesystem.core.interval.ITmfStateInterval; +import org.eclipse.linuxtools.statesystem.core.statevalue.ITmfStateValue; +import org.eclipse.linuxtools.statesystem.core.statevalue.ITmfStateValue.Type; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.tmf.core.signal.TmfRangeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampDelta; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphRangeListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphCombo; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphRangeUpdateEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphSelection; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorPart; + +/** + * Main implementation for the Call Stack view + * + * @author Patrick Tasse + * @since 2.0 + */ +public class CallStackView extends TmfView { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** View ID. */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.callstack"; //$NON-NLS-1$ + + /** + * Redraw state enum + */ + private enum State { + IDLE, BUSY, PENDING + } + + private static final String[] COLUMN_TIMES = new String[] { + Messages.CallStackView_FunctionColumn, + Messages.CallStackView_DepthColumn, + Messages.CallStackView_EntryTimeColumn, + Messages.CallStackView_ExitTimeColumn, + Messages.CallStackView_DurationColumn + }; + + private static final int[] COLUMN_WIDTHS = new int[] { + 200, + 50, + 120, + 120, + 120 + }; + + /** Timeout between updates in the build thread in ms */ + private static final long BUILD_UPDATE_TIMEOUT = 500; + + // Fraction of a function duration to be added as spacing + private static final double SPACING_RATIO = 0.01; + + private static final Image THREAD_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/thread_obj.gif"); //$NON-NLS-1$ + private static final Image STACKFRAME_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/stckframe_obj.gif"); //$NON-NLS-1$ + + private static final String IMPORT_MAPPING_ICON_PATH = "icons/etool16/import.gif"; //$NON-NLS-1$ + private static final String IMPORT_BINARY_ICON_PATH = "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$ + + private static final ImageDescriptor SORT_BY_NAME_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha.gif"); //$NON-NLS-1$ + private static final ImageDescriptor SORT_BY_NAME_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha_rev.gif"); //$NON-NLS-1$ + private static final ImageDescriptor SORT_BY_ID_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num.gif"); //$NON-NLS-1$ + private static final ImageDescriptor SORT_BY_ID_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num_rev.gif"); //$NON-NLS-1$ + private static final ImageDescriptor SORT_BY_TIME_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_time.gif"); //$NON-NLS-1$ + private static final ImageDescriptor SORT_BY_TIME_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_time_rev.gif"); //$NON-NLS-1$ + private static final String SORT_OPTION_KEY = "sort.option"; //$NON-NLS-1$ + + private enum SortOption { + BY_NAME, BY_NAME_REV, BY_ID, BY_ID_REV, BY_TIME, BY_TIME_REV + } + + private SortOption fSortOption; + private Comparator<ITimeGraphEntry> fThreadComparator = null; + private Action fSortByNameAction; + private Action fSortByIdAction; + private Action fSortByTimeAction; + + // ------------------------------------------------------------------------ + // Fields + // ------------------------------------------------------------------------ + + // The time graph combo + private TimeGraphCombo fTimeGraphCombo; + + // The selected trace + private ITmfTrace fTrace; + + // The selected thread map + private final Map<ITmfTrace, String> fSelectedThreadMap = new HashMap<>(); + + // The time graph entry list + private List<TraceEntry> fEntryList; + + // The trace to entry list hash map + private final Map<ITmfTrace, List<TraceEntry>> fEntryListMap = new HashMap<>(); + + // The trace to build thread hash map + private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<>(); + + /** The map to map function addresses to function names */ + private Map<String, String> fNameMapping; + + // The start time + private long fStartTime; + + // The end time + private long fEndTime; + + // The display width + private int fDisplayWidth; + + // The next event action + private Action fNextEventAction; + + // The previous event action + private Action fPrevEventAction; + + // The next item action + private Action fNextItemAction; + + // The previous item action + private Action fPreviousItemAction; + + // The action to import a function-name mapping file + private Action fImportMappingAction; + + // The action to import a binary file mapping */ + private Action fImportBinaryFileMappingAction; + + // The zoom thread + private ZoomThread fZoomThread; + + // The redraw state used to prevent unnecessary queuing of display runnables + private State fRedrawState = State.IDLE; + + // The redraw synchronization object + private final Object fSyncObj = new Object(); + + // The saved time sync. signal used when switching off the pinning of a view + private TmfTimeSynchSignal fSavedTimeSyncSignal; + + // The saved time range sync. signal used when switching off the pinning of + // a view + private TmfRangeSynchSignal fSavedRangeSyncSignal; + + // ------------------------------------------------------------------------ + // Classes + // ------------------------------------------------------------------------ + + private class TraceEntry extends TimeGraphEntry { + public TraceEntry(String name, long startTime, long endTime) { + super(name, startTime, endTime); + } + + @Override + public boolean hasTimeEvents() { + return false; + } + } + + private class ThreadEntry extends TimeGraphEntry { + // The call stack quark + private final int fCallStackQuark; + // The state system from which this entry comes + private final ITmfStateSystem fSS; + // The thread id + private final long fThreadId; + + public ThreadEntry(ITmfStateSystem ss, String name, long threadId, int callStackQuark, long startTime, long endTime) { + super(name, startTime, endTime); + fCallStackQuark = callStackQuark; + fThreadId = threadId; + fSS = ss; + } + + @Override + public boolean hasTimeEvents() { + return false; + } + + public int getCallStackQuark() { + return fCallStackQuark; + } + + public long getThreadId() { + return fThreadId; + } + + @Nullable + public ITmfStateSystem getStateSystem() { + return fSS; + } + } + + private class ThreadNameComparator implements Comparator<ITimeGraphEntry> { + private boolean reverse; + + public ThreadNameComparator(boolean reverse) { + this.reverse = reverse; + } + + @Override + public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) { + return reverse ? o2.getName().compareTo(o1.getName()) : + o1.getName().compareTo(o2.getName()); + } + } + + private class ThreadIdComparator implements Comparator<ITimeGraphEntry> { + private boolean reverse; + + public ThreadIdComparator(boolean reverse) { + this.reverse = reverse; + } + + @Override + public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) { + ThreadEntry t1 = (ThreadEntry) o1; + ThreadEntry t2 = (ThreadEntry) o2; + return reverse ? Long.compare(t2.getThreadId(), t1.getThreadId()) : + Long.compare(t1.getThreadId(), t2.getThreadId()); + } + } + + private class ThreadTimeComparator implements Comparator<ITimeGraphEntry> { + private boolean reverse; + + public ThreadTimeComparator(boolean reverse) { + this.reverse = reverse; + } + + @Override + public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) { + return reverse ? Long.compare(o2.getStartTime(), o1.getStartTime()) : + Long.compare(o1.getStartTime(), o2.getStartTime()); + } + } + + private class TreeContentProvider implements ITreeContentProvider { + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + if (inputElement != null) { + try { + return ((List<?>) inputElement).toArray(new ITimeGraphEntry[0]); + } catch (ClassCastException e) { + } + } + return new ITimeGraphEntry[0]; + } + + @Override + public Object[] getChildren(Object parentElement) { + ITimeGraphEntry entry = (ITimeGraphEntry) parentElement; + return entry.getChildren().toArray(); + } + + @Override + public Object getParent(Object element) { + ITimeGraphEntry entry = (ITimeGraphEntry) element; + return entry.getParent(); + } + + @Override + public boolean hasChildren(Object element) { + ITimeGraphEntry entry = (ITimeGraphEntry) element; + return entry.hasChildren(); + } + + } + + private class TreeLabelProvider implements ITableLabelProvider { + + @Override + public void addListener(ILabelProviderListener listener) { + } + + @Override + public void dispose() { + } + + @Override + public boolean isLabelProperty(Object element, String property) { + return false; + } + + @Override + public void removeListener(ILabelProviderListener listener) { + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + if (columnIndex == 0) { + if (element instanceof ThreadEntry) { + return THREAD_IMAGE; + } else if (element instanceof CallStackEntry) { + CallStackEntry entry = (CallStackEntry) element; + if (entry.getFunctionName().length() > 0) { + return STACKFRAME_IMAGE; + } + } + } + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + if (element instanceof CallStackEntry) { + CallStackEntry entry = (CallStackEntry) element; + if (columnIndex == 0) { + return entry.getFunctionName(); + } else if (columnIndex == 1 && entry.getFunctionName().length() > 0) { + int depth = entry.getStackLevel(); + return Integer.toString(depth); + } else if (columnIndex == 2 && entry.getFunctionName().length() > 0) { + ITmfTimestamp ts = new TmfTimestamp(entry.getFunctionEntryTime(), ITmfTimestamp.NANOSECOND_SCALE); + return ts.toString(); + } else if (columnIndex == 3 && entry.getFunctionName().length() > 0) { + ITmfTimestamp ts = new TmfTimestamp(entry.getFunctionExitTime(), ITmfTimestamp.NANOSECOND_SCALE); + return ts.toString(); + } else if (columnIndex == 4 && entry.getFunctionName().length() > 0) { + ITmfTimestamp ts = new TmfTimestampDelta(entry.getFunctionExitTime() - entry.getFunctionEntryTime(), ITmfTimestamp.NANOSECOND_SCALE); + return ts.toString(); + } + } else if (element instanceof ITimeGraphEntry) { + if (columnIndex == 0) { + return ((ITimeGraphEntry) element).getName(); + } + } + return ""; //$NON-NLS-1$ + } + + } + + private class TimeGraphContentProvider implements ITimeGraphContentProvider { + + @Override + public ITimeGraphEntry[] getElements(Object inputElement) { + if (inputElement != null) { + try { + return ((List<?>) inputElement).toArray(new ITimeGraphEntry[0]); + } catch (ClassCastException e) { + } + } + return new ITimeGraphEntry[0]; + } + + } + + private class BuildThread extends Thread { + private final ITmfTrace fBuildTrace; + private final ITmfTrace fParentTrace; + private final IProgressMonitor fMonitor; + + public BuildThread(ITmfTrace trace, ITmfTrace parentTrace) { + super("CallStackView build"); //$NON-NLS-1$ + fBuildTrace = trace; + fParentTrace = parentTrace; + fMonitor = new NullProgressMonitor(); + } + + @Override + public void run() { + buildThreadList(fBuildTrace, fParentTrace, fMonitor); + synchronized (fBuildThreadMap) { + fBuildThreadMap.remove(fBuildTrace); + } + } + + public void cancel() { + fMonitor.setCanceled(true); + } + } + + private class ZoomThread extends Thread { + private final List<TraceEntry> fZoomEntryList; + private final long fZoomStartTime; + private final long fZoomEndTime; + private final IProgressMonitor fMonitor; + + public ZoomThread(List<TraceEntry> entryList, long startTime, long endTime) { + super("CallStackView zoom"); //$NON-NLS-1$ + fZoomEntryList = entryList; + fZoomStartTime = startTime; + fZoomEndTime = endTime; + fMonitor = new NullProgressMonitor(); + } + + @Override + public void run() { + if (fZoomEntryList == null) { + return; + } + long resolution = Math.max(1, (fZoomEndTime - fZoomStartTime) / fDisplayWidth); + for (TraceEntry traceEntry : fZoomEntryList) { + for (ITimeGraphEntry threadEntry : traceEntry.getChildren()) { + ITmfStateSystem ss = ((ThreadEntry) threadEntry).getStateSystem(); + if (ss == null) { + continue; + } + ss.waitUntilBuilt(); + if (ss.isCancelled()) { + continue; + } + for (ITimeGraphEntry child : threadEntry.getChildren()) { + if (fMonitor.isCanceled()) { + break; + } + CallStackEntry entry = (CallStackEntry) child; + if (fZoomStartTime <= fStartTime && fZoomEndTime >= fEndTime) { + entry.setZoomedEventList(null); + } else { + List<ITimeEvent> zoomedEventList = getEventList(entry, fZoomStartTime, fZoomEndTime, resolution, fMonitor); + if (zoomedEventList != null) { + entry.setZoomedEventList(zoomedEventList); + } + } + redraw(); + } + } + } + } + + public void cancel() { + fMonitor.setCanceled(true); + } + } + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor + */ + public CallStackView() { + super(ID); + fDisplayWidth = Display.getDefault().getBounds().width; + } + + // ------------------------------------------------------------------------ + // ViewPart + // ------------------------------------------------------------------------ + + @Override + public void createPartControl(Composite parent) { + fTimeGraphCombo = new TimeGraphCombo(parent, SWT.NONE); + + fTimeGraphCombo.setTreeContentProvider(new TreeContentProvider()); + + fTimeGraphCombo.setTreeLabelProvider(new TreeLabelProvider()); + + fTimeGraphCombo.setTreeColumns(COLUMN_TIMES); + + fTimeGraphCombo.getTreeViewer().getTree().getColumn(0).setWidth(COLUMN_WIDTHS[0]); + fTimeGraphCombo.getTreeViewer().getTree().getColumn(1).setWidth(COLUMN_WIDTHS[1]); + fTimeGraphCombo.getTreeViewer().getTree().getColumn(2).setWidth(COLUMN_WIDTHS[2]); + fTimeGraphCombo.getTreeViewer().getTree().getColumn(3).setWidth(COLUMN_WIDTHS[3]); + fTimeGraphCombo.getTreeViewer().getTree().getColumn(4).setWidth(COLUMN_WIDTHS[4]); + + fTimeGraphCombo.setTimeGraphContentProvider(new TimeGraphContentProvider()); + fTimeGraphCombo.setTimeGraphProvider(new CallStackPresentationProvider(this)); + fTimeGraphCombo.getTimeGraphViewer().setTimeFormat(TimeFormat.CALENDAR); + + fTimeGraphCombo.getTimeGraphViewer().addRangeListener(new ITimeGraphRangeListener() { + @Override + public void timeRangeUpdated(TimeGraphRangeUpdateEvent event) { + long startTime = event.getStartTime(); + long endTime = event.getEndTime(); + TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(startTime), new TmfNanoTimestamp(endTime)); + broadcast(new TmfRangeSynchSignal(CallStackView.this, range)); + startZoomThread(startTime, endTime); + } + }); + + fTimeGraphCombo.getTimeGraphViewer().addTimeListener(new ITimeGraphTimeListener() { + @Override + public void timeSelected(TimeGraphTimeEvent event) { + long beginTime = event.getBeginTime(); + long endTime = event.getEndTime(); + synchingToTime(beginTime); + broadcast(new TmfTimeSynchSignal(CallStackView.this, new TmfNanoTimestamp(beginTime), new TmfNanoTimestamp(endTime))); + } + }); + + fTimeGraphCombo.getTimeGraphViewer().getControl().addControlListener(new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + fDisplayWidth = fTimeGraphCombo.getTimeGraphViewer().getControl().getSize().x; + if (fEntryList != null) { + startZoomThread(fTimeGraphCombo.getTimeGraphViewer().getTime0(), fTimeGraphCombo.getTimeGraphViewer().getTime1()); + } + } + }); + + fTimeGraphCombo.getTreeViewer().addDoubleClickListener(new IDoubleClickListener() { + @Override + public void doubleClick(DoubleClickEvent event) { + Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (selection instanceof CallStackEntry) { + CallStackEntry entry = (CallStackEntry) selection; + if (entry.getFunctionName().length() > 0) { + long entryTime = entry.getFunctionEntryTime(); + long exitTime = entry.getFunctionExitTime(); + long spacingTime = (long) ((exitTime - entryTime) * SPACING_RATIO); + entryTime -= spacingTime; + exitTime += spacingTime; + TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(entryTime), new TmfNanoTimestamp(exitTime)); + broadcast(new TmfRangeSynchSignal(CallStackView.this, range)); + fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(entryTime, exitTime); + startZoomThread(entryTime, exitTime); + } + } + } + }); + + fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl().addMouseListener(new MouseAdapter() { + @Override + public void mouseDoubleClick(MouseEvent e) { + TimeGraphControl timeGraphControl = fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl(); + ISelection selection = timeGraphControl.getSelection(); + if (selection instanceof TimeGraphSelection) { + Object o = ((TimeGraphSelection) selection).getFirstElement(); + if (o instanceof CallStackEvent) { + CallStackEvent event = (CallStackEvent) o; + long startTime = event.getTime(); + long endTime = startTime + event.getDuration(); + long spacingTime = (long) ((endTime - startTime) * SPACING_RATIO); + startTime -= spacingTime; + endTime += spacingTime; + TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(startTime), new TmfNanoTimestamp(endTime)); + broadcast(new TmfRangeSynchSignal(CallStackView.this, range)); + fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime); + startZoomThread(startTime, endTime); + } + } + } + }); + + IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager(); + fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl().setStatusLineManager(statusLineManager); + + // View Action Handling + makeActions(); + contributeToActionBars(); + createContextMenu(); + loadSortOption(); + + IEditorPart editor = getSite().getPage().getActiveEditor(); + if (editor instanceof ITmfTraceEditor) { + ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace(); + if (trace != null) { + traceSelected(new TmfTraceSelectedSignal(this, trace)); + } + } + } + + @Override + public void setFocus() { + fTimeGraphCombo.setFocus(); + } + + // ------------------------------------------------------------------------ + // Signal handlers + // ------------------------------------------------------------------------ + /** + * Handler for the trace opened signal. + * + * @param signal + * The incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceOpened(TmfTraceOpenedSignal signal) { + fTrace = signal.getTrace(); + loadTrace(); + } + + /** + * Handler for the trace selected signal + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public void traceSelected(final TmfTraceSelectedSignal signal) { + if (signal.getTrace() == fTrace) { + return; + } + fTrace = signal.getTrace(); + loadTrace(); + } + + /** + * Trace is closed: clear the data structures and the view + * + * @param signal + * the signal received + */ + @TmfSignalHandler + public void traceClosed(final TmfTraceClosedSignal signal) { + synchronized (fBuildThreadMap) { + ITmfTrace[] traces = TmfTraceManager.getTraceSet(signal.getTrace()); + for (ITmfTrace trace : traces) { + BuildThread buildThread = fBuildThreadMap.remove(trace); + if (buildThread != null) { + buildThread.cancel(); + } + } + } + synchronized (fEntryListMap) { + fEntryListMap.remove(signal.getTrace()); + } + fSelectedThreadMap.remove(signal.getTrace()); + if (signal.getTrace() == fTrace) { + fTrace = null; + fStartTime = 0; + fEndTime = 0; + refresh(); + } + } + + /** + * Handler for the TimeSynch signal + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public void synchToTime(final TmfTimeSynchSignal signal) { + + fSavedTimeSyncSignal = isPinned() ? new TmfTimeSynchSignal(signal.getSource(), signal.getBeginTime(), signal.getEndTime()) : null; + + if (signal.getSource() == this || fTrace == null || isPinned()) { + return; + } + final long beginTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + final long endTime = signal.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fTimeGraphCombo.isDisposed()) { + return; + } + if (beginTime == endTime) { + fTimeGraphCombo.getTimeGraphViewer().setSelectedTime(beginTime, true); + } else { + fTimeGraphCombo.getTimeGraphViewer().setSelectionRange(beginTime, endTime); + } + synchingToTime(beginTime); + startZoomThread(fTimeGraphCombo.getTimeGraphViewer().getTime0(), fTimeGraphCombo.getTimeGraphViewer().getTime1()); + if (fEntryList == null) { + return; + } + TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer(); + for (TraceEntry traceEntry : fEntryList) { + for (ITimeGraphEntry child : traceEntry.getChildren()) { + ThreadEntry threadEntry = (ThreadEntry) child; + ITmfStateSystem ss = threadEntry.getStateSystem(); + if (ss == null || beginTime < ss.getStartTime() || beginTime > ss.getCurrentEndTime()) { + continue; + } + try { + int quark = threadEntry.getCallStackQuark(); + ITmfStateInterval stackInterval = ss.querySingleState(beginTime, quark); + if (beginTime == stackInterval.getStartTime()) { + int stackLevel = stackInterval.getStateValue().unboxInt(); + ITimeGraphEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1)); + fTimeGraphCombo.setSelection(selectedEntry); + viewer.getTimeGraphControl().fireSelectionChanged(); + break; + } + } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException | StateValueTypeException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } + } + } + } + }); + } + + /** + * Handler for the RangeSynch signal + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public void synchToRange(final TmfRangeSynchSignal signal) { + + if (isPinned()) { + fSavedRangeSyncSignal = + new TmfRangeSynchSignal(signal.getSource(), new TmfTimeRange(signal.getCurrentRange().getStartTime(), signal.getCurrentRange().getEndTime())); + + fSavedTimeSyncSignal = null; + } + + if (signal.getSource() == this || fTrace == null || isPinned()) { + return; + } + if (signal.getCurrentRange().getIntersection(fTrace.getTimeRange()) == null) { + return; + } + final long startTime = signal.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + final long endTime = signal.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fTimeGraphCombo.isDisposed()) { + return; + } + fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime); + startZoomThread(startTime, endTime); + } + }); + } + + // ------------------------------------------------------------------------ + // Internal + // ------------------------------------------------------------------------ + + private void loadTrace() { + synchronized (fEntryListMap) { + fEntryList = fEntryListMap.get(fTrace); + if (fEntryList == null) { + fStartTime = Long.MAX_VALUE; + fEndTime = Long.MIN_VALUE; + synchronized (fBuildThreadMap) { + ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace); + for (ITmfTrace trace : traces) { + BuildThread buildThread = new BuildThread(trace, fTrace); + fBuildThreadMap.put(trace, buildThread); + buildThread.start(); + } + } + } else { + fStartTime = fTrace.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + fEndTime = fTrace.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + refresh(); + } + } + } + + private void buildThreadList(final ITmfTrace trace, final ITmfTrace parentTrace, IProgressMonitor monitor) { + if (monitor.isCanceled()) { + return; + } + AbstractCallStackAnalysis module = getCallStackModule(trace); + if (module == null) { + addUnavailableEntry(trace, parentTrace); + return; + } + ITmfStateSystem ss = module.getStateSystem(); + if (ss == null) { + addUnavailableEntry(trace, parentTrace); + return; + } + + Map<ITmfTrace, TraceEntry> traceEntryMap = new HashMap<>(); + Map<Integer, ThreadEntry> threadEntryMap = new HashMap<>(); + String[] threadPaths = module.getThreadsPattern(); + + long start = ss.getStartTime(); + + boolean complete = false; + while (!complete) { + if (monitor.isCanceled()) { + return; + } + complete = ss.waitUntilBuilt(BUILD_UPDATE_TIMEOUT); + if (ss.isCancelled()) { + return; + } + long end = ss.getCurrentEndTime(); + if (start == end && !complete) { // when complete execute one last time regardless of end time + continue; + } + List<Integer> threadQuarks = ss.getQuarks(threadPaths); + TraceEntry traceEntry = traceEntryMap.get(trace); + if (traceEntry == null) { + traceEntry = new TraceEntry(trace.getName(), start, end + 1); + traceEntryMap.put(trace, traceEntry); + traceEntry.sortChildren(fThreadComparator); + addToEntryList(parentTrace, Collections.singletonList(traceEntry)); + } else { + traceEntry.updateEndTime(end); + } + for (int i = 0; i < threadQuarks.size(); i++) { + if (monitor.isCanceled()) { + return; + } + int threadQuark = threadQuarks.get(i); + try { + String[] callStackPath = module.getCallStackPath(); + int callStackQuark = ss.getQuarkRelative(threadQuark, callStackPath); + String threadName = ss.getAttributeName(threadQuark); + long threadEnd = end + 1; + ITmfStateInterval endInterval = ss.querySingleState(ss.getCurrentEndTime(), callStackQuark); + if (endInterval.getStateValue().isNull() && endInterval.getStartTime() != ss.getStartTime()) { + threadEnd = endInterval.getStartTime(); + } + ThreadEntry threadEntry = threadEntryMap.get(threadQuark); + if (threadEntry == null) { + long threadId = ss.querySingleState(ss.getCurrentEndTime(), threadQuark).getStateValue().unboxLong(); + long threadStart = start; + ITmfStateInterval startInterval = ss.querySingleState(start, callStackQuark); + if (startInterval.getStateValue().isNull()) { + threadStart = Math.min(startInterval.getEndTime() + 1, end + 1); + } + threadEntry = new ThreadEntry(ss, threadName, threadId, callStackQuark, threadStart, threadEnd); + threadEntryMap.put(threadQuark, threadEntry); + traceEntry.addChild(threadEntry); + } else { + threadEntry.updateEndTime(threadEnd); + } + int level = 1; + for (int stackLevelQuark : ss.getSubAttributes(callStackQuark, false)) { + if (level > threadEntry.getChildren().size()) { + CallStackEntry callStackEntry = new CallStackEntry(threadName, stackLevelQuark, level, trace, ss); + threadEntry.addChild(callStackEntry); + } + level++; + } + } catch (AttributeNotFoundException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (StateSystemDisposedException e) { + /* Ignored */ + } + } + if (parentTrace == fTrace) { + synchronized (fEntryListMap) { + fStartTime = Math.min(fStartTime, start); + fEndTime = Math.max(fEndTime, end + 1); + } + refresh(); + } + for (ITimeGraphEntry threadEntry : traceEntry.getChildren()) { + for (ITimeGraphEntry callStackEntry : threadEntry.getChildren()) { + if (monitor.isCanceled()) { + return; + } + buildStatusEvents(parentTrace, (CallStackEntry) callStackEntry, monitor, start, end); + } + } + start = end; + } + } + + private void addToEntryList(ITmfTrace trace, List<TraceEntry> list) { + synchronized (fEntryListMap) { + List<TraceEntry> entryList = fEntryListMap.get(trace); + if (entryList == null) { + fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list)); + } else { + entryList.addAll(list); + } + } + } + + private void addUnavailableEntry(ITmfTrace trace, ITmfTrace parentTrace) { + String name = Messages.CallStackView_StackInfoNotAvailable + ' ' + '(' + trace.getName() + ')'; + TraceEntry unavailableEntry = new TraceEntry(name, 0, 0); + addToEntryList(parentTrace, Collections.singletonList(unavailableEntry)); + if (parentTrace == fTrace) { + refresh(); + } + } + + private void buildStatusEvents(ITmfTrace trace, CallStackEntry entry, IProgressMonitor monitor, long start, long end) { + ITmfStateSystem ss = entry.getStateSystem(); + long resolution = Math.max(1, (end - ss.getStartTime()) / fDisplayWidth); + List<ITimeEvent> eventList = getEventList(entry, start, end + 1, resolution, monitor); + if (eventList != null) { + for (ITimeEvent event : eventList) { + entry.addEvent(event); + } + } + if (trace == fTrace) { + redraw(); + } + } + + private static List<ITimeEvent> getEventList(CallStackEntry entry, + long startTime, long endTime, long resolution, + IProgressMonitor monitor) { + ITmfStateSystem ss = entry.getStateSystem(); + long start = Math.max(startTime, ss.getStartTime()); + long end = Math.min(endTime, ss.getCurrentEndTime() + 1); + if (end <= start) { + return null; + } + List<ITimeEvent> eventList = null; + try { + List<ITmfStateInterval> stackIntervals = ss.queryHistoryRange(entry.getQuark(), start, end - 1, resolution, monitor); + eventList = new ArrayList<>(stackIntervals.size()); + long lastEndTime = -1; + boolean lastIsNull = true; + for (ITmfStateInterval statusInterval : stackIntervals) { + if (monitor.isCanceled()) { + return null; + } + long time = statusInterval.getStartTime(); + long duration = statusInterval.getEndTime() - time + 1; + if (!statusInterval.getStateValue().isNull()) { + final int modulo = CallStackPresentationProvider.NUM_COLORS / 2; + int value = statusInterval.getStateValue().toString().hashCode() % modulo + modulo; + eventList.add(new CallStackEvent(entry, time, duration, value)); + lastIsNull = false; + } else { + if (lastEndTime == -1) { + // add null event if it intersects the start time + eventList.add(new NullTimeEvent(entry, time, duration)); + } else { + if (lastEndTime != time && lastIsNull) { + // add unknown event if between two null states + eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime)); + } + if (time + duration >= endTime) { + // add null event if it intersects the end time + eventList.add(new NullTimeEvent(entry, time, duration)); + } + } + lastIsNull = true; + } + lastEndTime = time + duration; + } + } catch (AttributeNotFoundException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (TimeRangeException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (StateSystemDisposedException e) { + /* Ignored */ + } + return eventList; + } + + private void synchingToTime(long time) { + if (fEntryList == null) { + return; + } + for (TraceEntry traceEntry : fEntryList) { + for (ITimeGraphEntry threadEntry : traceEntry.getChildren()) { + ITmfStateSystem ss = ((ThreadEntry) threadEntry).getStateSystem(); + if (ss == null) { + continue; + } + if (ss.isCancelled()) { + continue; + } + if (time < ss.getStartTime() || time > ss.getCurrentEndTime()) { + continue; + } + for (ITimeGraphEntry child : threadEntry.getChildren()) { + CallStackEntry callStackEntry = (CallStackEntry) child; + try { + ITmfStateInterval stackLevelInterval = ss.querySingleState(time, callStackEntry.getQuark()); + ITmfStateValue nameValue = stackLevelInterval.getStateValue(); + String name = ""; //$NON-NLS-1$ + try { + if (nameValue.getType() == Type.STRING) { + String address = nameValue.unboxStr(); + name = getFunctionName(address); + } else if (nameValue.getType() == Type.INTEGER) { + name = "0x" + Integer.toHexString(nameValue.unboxInt()); //$NON-NLS-1$ + } else if (nameValue.getType() == Type.LONG) { + name = "0x" + Long.toHexString(nameValue.unboxLong()); //$NON-NLS-1$ + } + } catch (StateValueTypeException e) { + } + callStackEntry.setFunctionName(name); + if (name.length() > 0) { + callStackEntry.setFunctionEntryTime(stackLevelInterval.getStartTime()); + callStackEntry.setFunctionExitTime(stackLevelInterval.getEndTime() + 1); + } + } catch (AttributeNotFoundException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } catch (StateSystemDisposedException e) { + /* Ignored */ + } + } + } + } + fTimeGraphCombo.refresh(); + } + + private void refresh() { + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fTimeGraphCombo.isDisposed()) { + return; + } + synchronized (fEntryListMap) { + fEntryList = fEntryListMap.get(fTrace); + if (fEntryList == null) { + fEntryList = new ArrayList<>(); + } + for (TraceEntry traceEntry : fEntryList) { + traceEntry.sortChildren(fThreadComparator); + } + } + if (fEntryList != fTimeGraphCombo.getInput()) { + fTimeGraphCombo.setInput(fEntryList); + } else { + fTimeGraphCombo.refresh(); + } + fTimeGraphCombo.getTimeGraphViewer().setTimeBounds(fStartTime, fEndTime); + + long selectionBeginTime = fTrace == null ? 0 : fTraceManager.getSelectionBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long selectionEndTime = fTrace == null ? 0 : fTraceManager.getSelectionEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long startTime = fTrace == null ? 0 : fTraceManager.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long endTime = fTrace == null ? 0 : fTraceManager.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + startTime = Math.max(startTime, fStartTime); + endTime = Math.min(endTime, fEndTime); + fTimeGraphCombo.getTimeGraphViewer().setSelectionRange(selectionBeginTime, selectionEndTime); + synchingToTime(selectionBeginTime); + fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime); + startZoomThread(startTime, endTime); + } + }); + } + + private void redraw() { + synchronized (fSyncObj) { + if (fRedrawState == State.IDLE) { + fRedrawState = State.BUSY; + } else { + fRedrawState = State.PENDING; + return; + } + } + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fTimeGraphCombo.isDisposed()) { + return; + } + fTimeGraphCombo.redraw(); + fTimeGraphCombo.update(); + synchronized (fSyncObj) { + if (fRedrawState == State.PENDING) { + fRedrawState = State.IDLE; + redraw(); + } else { + fRedrawState = State.IDLE; + } + } + } + }); + } + + private void startZoomThread(long startTime, long endTime) { + if (fZoomThread != null) { + fZoomThread.cancel(); + } + fZoomThread = new ZoomThread(fEntryList, startTime, endTime); + fZoomThread.start(); + } + + private void makeActions() { + fPreviousItemAction = fTimeGraphCombo.getTimeGraphViewer().getPreviousItemAction(); + fPreviousItemAction.setText(Messages.TmfTimeGraphViewer_PreviousItemActionNameText); + fPreviousItemAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousItemActionToolTipText); + fNextItemAction = fTimeGraphCombo.getTimeGraphViewer().getNextItemAction(); + fNextItemAction.setText(Messages.TmfTimeGraphViewer_NextItemActionNameText); + fNextItemAction.setToolTipText(Messages.TmfTimeGraphViewer_NextItemActionToolTipText); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + fillLocalToolBar(bars.getToolBarManager()); + + // Create pin action + contributePinActionToToolBar(); + fPinAction.addPropertyChangeListener(new IPropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + if (IAction.CHECKED.equals(event.getProperty()) && !isPinned()) { + if (fSavedRangeSyncSignal != null) { + synchToRange(fSavedRangeSyncSignal); + fSavedRangeSyncSignal = null; + } + + if (fSavedTimeSyncSignal != null) { + synchToTime(fSavedTimeSyncSignal); + fSavedTimeSyncSignal = null; + } + } + } + }); + } + + private void fillLocalToolBar(IToolBarManager manager) { + manager.add(getImportBinaryAction()); + manager.add(getImportMappingAction()); + manager.add(new Separator()); + manager.add(getSortByNameAction()); + manager.add(getSortByIdAction()); + manager.add(getSortByTimeAction()); + manager.add(new Separator()); + manager.add(fTimeGraphCombo.getTimeGraphViewer().getResetScaleAction()); + manager.add(getPreviousEventAction()); + manager.add(getNextEventAction()); + manager.add(fPreviousItemAction); + manager.add(fNextItemAction); + manager.add(fTimeGraphCombo.getTimeGraphViewer().getZoomInAction()); + manager.add(fTimeGraphCombo.getTimeGraphViewer().getZoomOutAction()); + manager.add(new Separator()); + } + + private void createContextMenu() { + final MenuManager contextMenu = new MenuManager(); + contextMenu.add(getSortByNameAction()); + contextMenu.add(getSortByIdAction()); + contextMenu.add(getSortByTimeAction()); + + Tree tree = fTimeGraphCombo.getTreeViewer().getTree(); + Menu menu = contextMenu.createContextMenu(tree); + tree.setMenu(menu); + } + + /** + * Get the the next event action. + * + * @return The action object + */ + private Action getNextEventAction() { + if (fNextEventAction == null) { + fNextEventAction = new Action() { + @Override + public void run() { + TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer(); + ITimeGraphEntry entry = viewer.getSelection(); + if (entry instanceof CallStackEntry) { + try { + CallStackEntry callStackEntry = (CallStackEntry) entry; + ITmfStateSystem ss = callStackEntry.getStateSystem(); + long time = Math.max(ss.getStartTime(), Math.min(ss.getCurrentEndTime(), viewer.getSelectionBegin())); + ThreadEntry threadEntry = (ThreadEntry) callStackEntry.getParent(); + int quark = ss.getParentAttributeQuark(callStackEntry.getQuark()); + ITmfStateInterval stackInterval = ss.querySingleState(time, quark); + long newTime = stackInterval.getEndTime() + 1; + viewer.setSelectedTimeNotify(newTime, true); + stackInterval = ss.querySingleState(Math.min(ss.getCurrentEndTime(), newTime), quark); + int stackLevel = stackInterval.getStateValue().unboxInt(); + ITimeGraphEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1)); + fTimeGraphCombo.setSelection(selectedEntry); + viewer.getTimeGraphControl().fireSelectionChanged(); + startZoomThread(viewer.getTime0(), viewer.getTime1()); + + } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException | StateValueTypeException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } + } + } + }; + + fNextEventAction.setText(Messages.TmfTimeGraphViewer_NextEventActionNameText); + fNextEventAction.setToolTipText(Messages.TmfTimeGraphViewer_NextEventActionToolTipText); + fNextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_EVENT)); + } + + return fNextEventAction; + } + + /** + * Get the previous event action. + * + * @return The Action object + */ + private Action getPreviousEventAction() { + if (fPrevEventAction == null) { + fPrevEventAction = new Action() { + @Override + public void run() { + TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer(); + ITimeGraphEntry entry = viewer.getSelection(); + if (entry instanceof CallStackEntry) { + try { + CallStackEntry callStackEntry = (CallStackEntry) entry; + ITmfStateSystem ss = callStackEntry.getStateSystem(); + long time = Math.max(ss.getStartTime(), Math.min(ss.getCurrentEndTime(), viewer.getSelectionBegin())); + ThreadEntry threadEntry = (ThreadEntry) callStackEntry.getParent(); + int quark = ss.getParentAttributeQuark(callStackEntry.getQuark()); + ITmfStateInterval stackInterval = ss.querySingleState(time, quark); + if (stackInterval.getStartTime() == time && time > ss.getStartTime()) { + stackInterval = ss.querySingleState(time - 1, quark); + } + viewer.setSelectedTimeNotify(stackInterval.getStartTime(), true); + int stackLevel = stackInterval.getStateValue().unboxInt(); + ITimeGraphEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1)); + fTimeGraphCombo.setSelection(selectedEntry); + viewer.getTimeGraphControl().fireSelectionChanged(); + startZoomThread(viewer.getTime0(), viewer.getTime1()); + + } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException | StateValueTypeException e) { + Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$ + } + } + } + }; + + fPrevEventAction.setText(Messages.TmfTimeGraphViewer_PreviousEventActionNameText); + fPrevEventAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousEventActionToolTipText); + fPrevEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_EVENT)); + } + + return fPrevEventAction; + } + + private static @Nullable AbstractCallStackAnalysis getCallStackModule(ITmfTrace trace) { + /* + * Since we cannot know the exact analysis ID (in separate plugins), we + * will search using the analysis type. + */ + Iterable<AbstractCallStackAnalysis> modules = + trace.getAnalysisModulesOfClass(AbstractCallStackAnalysis.class); + Iterator<AbstractCallStackAnalysis> it = modules.iterator(); + if (!it.hasNext()) { + /* This trace does not provide a call-stack analysis */ + return null; + } + + /* + * We only look at the first module we find. + * + * TODO Handle the advanced case where one trace provides more than one + * call-stack analysis. + */ + AbstractCallStackAnalysis module = it.next(); + /* This analysis is not automatic, we need to schedule it on-demand */ + module.schedule(); + module.waitForInitialization(); + return module; + } + + // ------------------------------------------------------------------------ + // Methods related to function name mapping + // ------------------------------------------------------------------------ + + /** + * Common code for all import file mapping actions + */ + private abstract class AbstractImportFileMappingAction extends Action { + private final String fDialogTitle; + + private AbstractImportFileMappingAction(String dialogTitle) { + fDialogTitle = dialogTitle; + } + + @Override + public void run() { + FileDialog dialog = new FileDialog(getViewSite().getShell()); + dialog.setText(fDialogTitle); + final String filePath = dialog.open(); + if (filePath == null) { + /* No file was selected, don't change anything */ + return; + } + + /* + * Start the mapping import in a separate thread (we do not want to + * UI thread to do this). + */ + Job job = new Job(Messages.CallStackView_ImportMappingJobName) { + @Override + public IStatus run(IProgressMonitor monitor) { + fNameMapping = doMapping(new File(filePath)); + + /* Refresh call stack entries and event labels */ + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + synchingToTime(fTimeGraphCombo.getTimeGraphViewer().getSelectionBegin()); + } + }); + return Status.OK_STATUS; + } + }; + job.schedule(); + } + + abstract Map<String, String> doMapping(File file); + } + + /** + * Toolbar icon to import the function address-to-name mapping file. + */ + private Action getImportMappingAction() { + if (fImportMappingAction != null) { + return fImportMappingAction; + } + fImportMappingAction = new AbstractImportFileMappingAction(Messages.CallStackView_ImportMappingDialogTitle) { + @Override + Map<String, String> doMapping(File file) { + return FunctionNameMapper.mapFromNmTextFile(file); + } + }; + + fImportMappingAction.setText(Messages.CallStackView_ImportMappingButtonText); + fImportMappingAction.setToolTipText(Messages.CallStackView_ImportMappingButtonTooltip); + fImportMappingAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(IMPORT_MAPPING_ICON_PATH)); + + return fImportMappingAction; + } + + private Action getSortByNameAction() { + if (fSortByNameAction == null) { + fSortByNameAction = new Action(Messages.CallStackView_SortByThreadName, IAction.AS_CHECK_BOX) { + @Override + public void run() { + if (fSortOption == SortOption.BY_NAME) { + saveSortOption(SortOption.BY_NAME_REV); + } else { + saveSortOption(SortOption.BY_NAME); + } + } + }; + fSortByNameAction.setToolTipText(Messages.CallStackView_SortByThreadName); + fSortByNameAction.setImageDescriptor(SORT_BY_NAME_ICON); + } + return fSortByNameAction; + } + + private Action getSortByIdAction() { + if (fSortByIdAction == null) { + fSortByIdAction = new Action(Messages.CallStackView_SortByThreadId, IAction.AS_CHECK_BOX) { + @Override + public void run() { + if (fSortOption == SortOption.BY_ID) { + saveSortOption(SortOption.BY_ID_REV); + } else { + saveSortOption(SortOption.BY_ID); + } + } + }; + fSortByIdAction.setToolTipText(Messages.CallStackView_SortByThreadId); + fSortByIdAction.setImageDescriptor(SORT_BY_ID_ICON); + } + return fSortByIdAction; + } + + private Action getSortByTimeAction() { + if (fSortByTimeAction == null) { + fSortByTimeAction = new Action(Messages.CallStackView_SortByThreadTime, IAction.AS_CHECK_BOX) { + @Override + public void run() { + if (fSortOption == SortOption.BY_TIME) { + saveSortOption(SortOption.BY_TIME_REV); + } else { + saveSortOption(SortOption.BY_TIME); + } + } + }; + fSortByTimeAction.setToolTipText(Messages.CallStackView_SortByThreadTime); + fSortByTimeAction.setImageDescriptor(SORT_BY_TIME_ICON); + } + return fSortByTimeAction; + } + + private void loadSortOption() { + IDialogSettings settings = Activator.getDefault().getDialogSettings(); + IDialogSettings section = settings.getSection(getClass().getName()); + if (section == null) { + return; + } + String sortOption = section.get(SORT_OPTION_KEY); + if (sortOption == null) { + return; + } + + // reset defaults + getSortByNameAction().setChecked(false); + getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON); + getSortByIdAction().setChecked(false); + getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON); + getSortByTimeAction().setChecked(false); + getSortByTimeAction().setImageDescriptor(SORT_BY_TIME_ICON); + + if (sortOption.equals(SortOption.BY_NAME.name())) { + fSortOption = SortOption.BY_NAME; + fThreadComparator = new ThreadNameComparator(false); + getSortByNameAction().setChecked(true); + } else if (sortOption.equals(SortOption.BY_NAME_REV.name())) { + fSortOption = SortOption.BY_NAME_REV; + fThreadComparator = new ThreadNameComparator(true); + getSortByNameAction().setChecked(true); + getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON); + } else if (sortOption.equals(SortOption.BY_ID.name())) { + fSortOption = SortOption.BY_ID; + fThreadComparator = new ThreadIdComparator(false); + getSortByIdAction().setChecked(true); + } else if (sortOption.equals(SortOption.BY_ID_REV.name())) { + fSortOption = SortOption.BY_ID_REV; + fThreadComparator = new ThreadIdComparator(true); + getSortByIdAction().setChecked(true); + getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON); + } else if (sortOption.equals(SortOption.BY_TIME.name())) { + fSortOption = SortOption.BY_TIME; + fThreadComparator = new ThreadTimeComparator(false); + getSortByTimeAction().setChecked(true); + } else if (sortOption.equals(SortOption.BY_TIME_REV.name())) { + fSortOption = SortOption.BY_TIME_REV; + fThreadComparator = new ThreadTimeComparator(true); + getSortByTimeAction().setChecked(true); + getSortByTimeAction().setImageDescriptor(SORT_BY_TIME_REV_ICON); + } + } + + private void saveSortOption(SortOption sortOption) { + IDialogSettings settings = Activator.getDefault().getDialogSettings(); + IDialogSettings section = settings.getSection(getClass().getName()); + if (section == null) { + section = settings.addNewSection(getClass().getName()); + } + section.put(SORT_OPTION_KEY, sortOption.name()); + loadSortOption(); + if (fEntryList == null) { + return; + } + for (TraceEntry traceEntry : fEntryList) { + traceEntry.sortChildren(fThreadComparator); + } + refresh(); + } + + /** + * Toolbar icon to import the function address-to-name mapping binary file. + */ + private Action getImportBinaryAction() { + if (fImportBinaryFileMappingAction != null) { + return fImportBinaryFileMappingAction; + } + + fImportBinaryFileMappingAction = new AbstractImportFileMappingAction(Messages.CallStackView_ImportBinaryFileDialogTitle) { + @Override + Map<String, String> doMapping(File file) { + return FunctionNameMapper.mapFromBinaryFile(file); + } + }; + + fImportBinaryFileMappingAction.setText(Messages.CallStackView_ImportBinaryFileButtonText); + fImportBinaryFileMappingAction.setToolTipText(Messages.CallStackView_ImportBinaryFileButtonTooltip); + fImportBinaryFileMappingAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(IMPORT_BINARY_ICON_PATH)); + + return fImportBinaryFileMappingAction; + } + + String getFunctionName(String address) { + if (fNameMapping == null) { + /* No mapping available, just print the addresses */ + return address; + } + String ret = fNameMapping.get(address); + if (ret == null) { + /* + * We didn't find this address in the mapping file, just use the + * address + */ + return address; + } + return ret; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/FunctionNameMapper.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/FunctionNameMapper.java new file mode 100644 index 0000000000..2c421a799b --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/callstack/FunctionNameMapper.java @@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * 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: + * Alexandre Montplaisir - Initial API and implementation + * Marc-Andre Laperle - Map from binary file + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.callstack; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IBinaryParser; +import org.eclipse.cdt.core.IBinaryParser.IBinaryFile; +import org.eclipse.cdt.core.IBinaryParser.ISymbol; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; + +/** + * Class containing the different methods to import an address->name mapping. + * + * @author Alexandre Montplaisir + */ +class FunctionNameMapper { + + public static @Nullable Map<String, String> mapFromNmTextFile(File mappingFile) { + Map<String, String> map = new HashMap<>(); + + try (FileReader fr = new FileReader(mappingFile); + BufferedReader reader = new BufferedReader(fr);) { + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + String[] elems = line.split(" "); //$NON-NLS-1$ + /* Only lines with 3 elements contain addresses */ + if (elems.length == 3) { + /* Strip the leading zeroes from the address */ + String address = elems[0].replaceFirst("^0+(?!$)", ""); //$NON-NLS-1$ //$NON-NLS-2$; + String name = elems[elems.length - 1]; + map.put(address, name); + } + } + } catch (FileNotFoundException e) { + return null; + } catch (IOException e) { + /* Stop reading the file at this point */ + } + + if (map.isEmpty()) { + return null; + } + return Collections.unmodifiableMap(map); + } + + /** + * Strip the leading zeroes from the address + * */ + private static String stripLeadingZeros(String address) { + return address.replaceFirst("^0+(?!$)", ""); //$NON-NLS-1$ //$NON-NLS-2$; + } + + public static @Nullable Map<String, String> mapFromBinaryFile(File file) { + Map<String, String> map = new HashMap<>(); + IBinaryParser.IBinaryObject binaryObject = getBinaryObject(file); + if (binaryObject != null) { + ISymbol[] symbols = binaryObject.getSymbols(); + for (ISymbol symbol : symbols) { + String address = symbol.getAddress().toHexAddressString(); + /* Remove "0x" */ + address = address.substring(2); + /* Strip the leading zeroes from the address */ + address = stripLeadingZeros(address); + map.put(address, symbol.getName()); + } + } + + return map; + } + + private static @Nullable IBinaryParser.IBinaryObject getBinaryObject(File file) { + IPath filePath = new Path(file.toString()); + + /* Get all the available binary parsers */ + final List<IBinaryParser> binaryParsers = new ArrayList<>(); + IConfigurationElement[] elements = Platform.getExtensionRegistry() + .getConfigurationElementsFor(CCorePlugin.BINARY_PARSER_UNIQ_ID); + for (IConfigurationElement element : elements) { + IConfigurationElement[] children = element.getChildren("run"); //$NON-NLS-1$ + for (final IConfigurationElement run : children) { + SafeRunner.run(new ISafeRunnable() { + @Override + public void run() throws Exception { + IBinaryParser binaryParser = (IBinaryParser) run.createExecutableExtension("class"); //$NON-NLS-1$ + binaryParsers.add(binaryParser); + } + + @Override + public void handleException(Throwable exception) { + Activator.getDefault().logError("Error creating binary parser", exception); //$NON-NLS-1$ + } + }); + } + } + + /* Find the maximum "hint" buffer size we'll need from all the parsers */ + int hintBufferSize = 0; + for (IBinaryParser parser : binaryParsers) { + if (parser.getHintBufferSize() > hintBufferSize) { + hintBufferSize = Math.max(hintBufferSize, parser.getHintBufferSize()); + } + } + + /* Read the initial "hint" bytes */ + byte[] hintBuffer = new byte[hintBufferSize]; + if (hintBufferSize > 0) { + try (InputStream is = new FileInputStream(file) ){ + + int count = 0; + // Make sure we read up to 'hints' bytes if we possibly can + while (count < hintBufferSize) { + int bytesRead = is.read(hintBuffer, count, hintBufferSize - count); + if (bytesRead < 0) { + break; + } + count += bytesRead; + } + if (count > 0 && count < hintBuffer.length) { + byte[] array = new byte[count]; + System.arraycopy(hintBuffer, 0, array, 0, count); + hintBuffer = array; + } + } catch (IOException e) { + Activator.getDefault().logError("Error reading initial bytes of binary file", e); //$NON-NLS-1$ + return null; + } + } + + /* For all binary parsers, try to get a binary object */ + for (IBinaryParser parser : binaryParsers) { + if (parser.isBinary(hintBuffer, filePath)) { + IBinaryFile binFile; + try { + binFile = parser.getBinary(hintBuffer, filePath); + if (binFile != null && binFile instanceof IBinaryParser.IBinaryObject) { + return (IBinaryParser.IBinaryObject)binFile; + } + } catch (IOException e) { + Activator.getDefault().logError("Error parsing binary file", e); //$NON-NLS-1$ + } + } + } + + return null; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSetting.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSetting.java new file mode 100644 index 0000000000..15c4d7dabc --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSetting.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + * Bernd Hufmann - Updated to use RGB for the tick color + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.colors; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.ui.themes.ColorUtil; + +/** + * Class for storing color settings of a TMF filter. + * + * Application code must explicitly invoke the ColorSetting.dispose() method to release the operating system + * resources managed by each instance when those instances are no longer required. + * + * @version 1.0 + * @author Patrick Tasse + */ +public class ColorSetting { + + private RGB fForegroundRGB; + private RGB fBackgroundRGB; + private RGB fTickColorRGB; + private Color fForegroundColor; + private Color fBackgroundColor; + private Color fDimmedForegroundColor; + private Color fDimmedBackgroundColor; + private Color fTickColor; + private ITmfFilterTreeNode fFilter; + + /** + * Constructor + * + * You must dispose the color setting when it is no longer required. + * + * @param foreground + * The foreground color + * @param background + * The background color + * @param tickColorRGB + * The color for the checkbox ticks + * @param filter + * The filter tree node + */ + public ColorSetting(RGB foreground, RGB background, RGB tickColorRGB, ITmfFilterTreeNode filter) { + fForegroundRGB = foreground; + fBackgroundRGB = background; + fTickColorRGB = tickColorRGB; + fFilter = filter; + Display display = Display.getDefault(); + fForegroundColor = new Color(display, fForegroundRGB); + fBackgroundColor = new Color(display, fBackgroundRGB); + fDimmedForegroundColor = new Color(display, ColorUtil.blend( + fForegroundRGB, fBackgroundRGB)); + fDimmedBackgroundColor = new Color(display, ColorUtil.blend( + fBackgroundRGB, display.getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB())); + fTickColor = new Color(display, fTickColorRGB); + } + + /** + * Dispose the color setting resources + */ + public void dispose() { + fForegroundColor.dispose(); + fBackgroundColor.dispose(); + fDimmedForegroundColor.dispose(); + fDimmedBackgroundColor.dispose(); + fTickColor.dispose(); + } + + /** + * Returns foreground RGB value. + * + * @return the foreground RGB + */ + public RGB getForegroundRGB() { + return fForegroundRGB; + } + + /** + * Sets the foreground RGB value + * + * @param foreground the foreground to set + */ + public void setForegroundRGB(RGB foreground) { + fForegroundRGB = foreground; + fForegroundColor.dispose(); + fDimmedForegroundColor.dispose(); + Display display = Display.getDefault(); + fForegroundColor = new Color(display, fForegroundRGB); + fDimmedForegroundColor = new Color(display, ColorUtil.blend( + fForegroundRGB, fBackgroundRGB)); + } + + /** + * Returns the background RGB value. + * + * @return the background RGB + */ + public RGB getBackgroundRGB() { + return fBackgroundRGB; + } + + /** + * Sets the background RGB value. + * + * @param background the background to set + */ + public void setBackgroundRGB(RGB background) { + fBackgroundRGB = background; + fBackgroundColor.dispose(); + fDimmedBackgroundColor.dispose(); + Display display = Display.getDefault(); + fBackgroundColor = new Color(display, fBackgroundRGB); + fDimmedBackgroundColor = new Color(display, ColorUtil.blend( + fBackgroundRGB, display.getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB())); + } + + /** + * Returns the RGB of the tick color + * + * @return the RGB of the tick color + */ + public RGB getTickColorRGB() { + return fTickColorRGB; + } + + /** + * Sets the RGB of the tick color + * + * @param tickColorRGB the tick color TGB + */ + public void setTickColorRGB(RGB tickColorRGB) { + fTickColorRGB = tickColorRGB; + fTickColor.dispose(); + Display display = Display.getDefault(); + fTickColor = new Color(display, fTickColorRGB); + } + + /** + * Returns the filter implementation. + * @return the filter + */ + public ITmfFilterTreeNode getFilter() { + return fFilter; + } + + /** + * Sets the filter implementation. + * + * @param filter the filter to set + */ + public void setFilter(ITmfFilterTreeNode filter) { + fFilter = filter; + } + + /** + * Returns the foreground color. + * + * @return the foreground color + */ + public Color getForegroundColor() { + return fForegroundColor; + } + + /** + * Returns the background color. + * + * @return the background color + */ + public Color getBackgroundColor() { + return fBackgroundColor; + } + + /** + * Returns the dimmed foreground color. + * + * @return the dimmed foreground color + */ + public Color getDimmedForegroundColor() { + return fDimmedForegroundColor; + } + + /** + * Returns the dimmed background color. + * + * @return the dimmed background color + */ + public Color getDimmedBackgroundColor() { + return fDimmedBackgroundColor; + } + + /** + * Returns the tick color. + * + * @return the tick color + */ + public Color getTickColor() { + return fTickColor; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSettingsManager.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSettingsManager.java new file mode 100644 index 0000000000..7e6c7ce906 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSettingsManager.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + * Bernd Hufmann - Updated to use RGB for the tick color + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.colors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; + +/** + * Static class for managing color settings. + * + * @version 1.0 + * @author Patrick Tasse + * + */ +public class ColorSettingsManager { + + // The color settings file name + private static final String COLOR_SETTINGS_FILE_NAME = "color_settings.xml"; //$NON-NLS-1$ + + // The path for the color settings file + private static final String COLOR_SETTINGS_PATH_NAME = + Activator.getDefault().getStateLocation().addTrailingSeparator().append(COLOR_SETTINGS_FILE_NAME).toString(); + + // The default color setting + private static final ColorSetting DEFAULT_COLOR_SETTING = new ColorSetting( + Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND).getRGB(), + Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB(), + Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND).getRGB(), + null); + + /** + * Special value for priority if unknown. + */ + public static final int PRIORITY_NONE = Integer.MAX_VALUE; + + // The stored color settings + private static ColorSetting[] fColorSettings = ColorSettingsXML.load(COLOR_SETTINGS_PATH_NAME); + + // The listener list + private static List<IColorSettingsListener> fListeners = new ArrayList<>(); + + /** + * Returns an array of color settings. + * + * @return an array of color settings. + */ + public static ColorSetting[] getColorSettings() { + return (fColorSettings != null) ? Arrays.copyOf(fColorSettings, fColorSettings.length) : null; + } + + /** + * Sets the array of color settings. + * + * @param colorSettings A array of color settings to set + */ + public static void setColorSettings(ColorSetting[] colorSettings) { + fColorSettings = (colorSettings != null) ? Arrays.copyOf(colorSettings, colorSettings.length) : null; + ColorSettingsXML.save(COLOR_SETTINGS_PATH_NAME, fColorSettings); + fireColorSettingsChanged(); + } + + /** + * Gets the color settings that matches the filter for given event. + * + * @param event + * The event to check + * + * @return color settings defined for filter if found else default color + * settings + */ + public static ColorSetting getColorSetting(ITmfEvent event) { + for (int i = 0; i < fColorSettings.length; i++) { + ColorSetting colorSetting = fColorSettings[i]; + if (colorSetting.getFilter() != null && colorSetting.getFilter().matches(event)) { + return colorSetting; + } + } + return DEFAULT_COLOR_SETTING; + } + + /** + * Gets the color settings priority for the given event. + * + * @param event A event the event to check + * @return the priority defined for the filter else PRIORITY_NONE + */ + public static int getColorSettingPriority(ITmfEvent event) { + for (int i = 0; i < fColorSettings.length; i++) { + ColorSetting colorSetting = fColorSettings[i]; + if (colorSetting.getFilter() != null && colorSetting.getFilter().matches(event)) { + return i; + } + } + return PRIORITY_NONE; + } + + /** + * Returns the color settings based the priority. + * + * @param priority A priority (index) of color settings + * @return the color settings defined for the priority else default color settings + */ + public static ColorSetting getColorSetting(int priority) { + if (priority < fColorSettings.length) { + return fColorSettings[priority]; + } + return DEFAULT_COLOR_SETTING; + } + + /** + * Adds a color settings listener. + * + * @param listener A listener to add. + */ + public static void addColorSettingsListener(IColorSettingsListener listener) { + if (! fListeners.contains(listener)) { + fListeners.add(listener); + } + } + + /** + * Removes a color settings listener. + * + * @param listener A listener to remove. + */ + public static void removeColorSettingsListener(IColorSettingsListener listener) { + fListeners.remove(listener); + } + + // Notify listeners + private static void fireColorSettingsChanged() { + for (IColorSettingsListener listener : fListeners) { + listener.colorSettingsChanged(fColorSettings); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSettingsXML.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSettingsXML.java new file mode 100644 index 0000000000..a123b25c6c --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorSettingsXML.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2010, 2014 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + * Bernd Hufmann - Updated to use RGB for the tick color + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.colors; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.xml.TmfFilterContentHandler; +import org.eclipse.tracecompass.tmf.core.filter.xml.TmfFilterXMLWriter; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Class for saving and loading of color settings to/from file. + * + * @version 1.0 + * @author Patrick Tasse + * + */ +public class ColorSettingsXML { + + // XML Tags and attributes + private static final String COLOR_SETTINGS_TAG = "COLOR_SETTINGS"; //$NON-NLS-1$ + private static final String COLOR_SETTING_TAG = "COLOR_SETTING"; //$NON-NLS-1$ + private static final String FG_TAG = "FG"; //$NON-NLS-1$ + private static final String BG_TAG = "BG"; //$NON-NLS-1$ + private static final String R_ATTR = "R"; //$NON-NLS-1$ + private static final String G_ATTR = "G"; //$NON-NLS-1$ + private static final String B_ATTR = "B"; //$NON-NLS-1$ + private static final String TICK_TAG = "TICK"; //$NON-NLS-1$ + private static final String FILTER_TAG = "FILTER"; //$NON-NLS-1$ + + /** + * Saves the given color settings to file. + * + * @param pathName + * A file name with path + * @param colorSettings + * -An array of color settings to save. + */ + public static void save(String pathName, ColorSetting[] colorSettings) { + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.newDocument(); + + Element rootElement = document.createElement(COLOR_SETTINGS_TAG); + document.appendChild(rootElement); + + for (ColorSetting colorSetting : colorSettings) { + Element colorSettingElement = document.createElement(COLOR_SETTING_TAG); + rootElement.appendChild(colorSettingElement); + + Element fgElement = document.createElement(FG_TAG); + colorSettingElement.appendChild(fgElement); + RGB foreground = colorSetting.getForegroundRGB(); + fgElement.setAttribute(R_ATTR, Integer.toString(foreground.red)); + fgElement.setAttribute(G_ATTR, Integer.toString(foreground.green)); + fgElement.setAttribute(B_ATTR, Integer.toString(foreground.blue)); + + Element bgElement = document.createElement(BG_TAG); + colorSettingElement.appendChild(bgElement); + RGB background = colorSetting.getBackgroundRGB(); + bgElement.setAttribute(R_ATTR, Integer.toString(background.red)); + bgElement.setAttribute(G_ATTR, Integer.toString(background.green)); + bgElement.setAttribute(B_ATTR, Integer.toString(background.blue)); + + Element tickColorElement = document.createElement(TICK_TAG); + colorSettingElement.appendChild(tickColorElement); + RGB tickColor = colorSetting.getTickColorRGB(); + tickColorElement.setAttribute(R_ATTR, Integer.toString(tickColor.red)); + tickColorElement.setAttribute(G_ATTR, Integer.toString(tickColor.green)); + tickColorElement.setAttribute(B_ATTR, Integer.toString(tickColor.blue)); + + if (colorSetting.getFilter() != null) { + Element filterElement = document.createElement(FILTER_TAG); + colorSettingElement.appendChild(filterElement); + TmfFilterXMLWriter.buildXMLTree(document, colorSetting.getFilter(), filterElement); + } + } + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(document); + StreamResult result = new StreamResult(new File(pathName)); + transformer.transform(source, result); + } catch (ParserConfigurationException e) { + Activator.getDefault().logError("Error saving color xml file: " + pathName, e); //$NON-NLS-1$ + } catch (TransformerConfigurationException e) { + Activator.getDefault().logError("Error saving color xml file: " + pathName, e); //$NON-NLS-1$ + } catch (TransformerException e) { + Activator.getDefault().logError("Error saving color xml file: " + pathName, e); //$NON-NLS-1$ + } + } + + /** + * Loads color settings from file and returns it in an array. + * + * @param pathName + * A file name with path + * + * @return the color settings array loaded from file + */ + public static ColorSetting[] load(String pathName) { + if (!new File(pathName).canRead()) { + return new ColorSetting[0]; + } + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + + ColorSettingsContentHandler handler = new ColorSettingsContentHandler(); + try { + XMLReader saxReader = parserFactory.newSAXParser().getXMLReader(); + saxReader.setContentHandler(handler); + saxReader.parse(pathName); + return handler.colorSettings.toArray(new ColorSetting[0]); + } catch (ParserConfigurationException e) { + Activator.getDefault().logError("Error loading color xml file: " + pathName, e); //$NON-NLS-1$ + } catch (SAXException e) { + Activator.getDefault().logError("Error loading color xml file: " + pathName, e); //$NON-NLS-1$ + } catch (IOException e) { + Activator.getDefault().logError("Error loading color xml file: " + pathName, e); //$NON-NLS-1$ + } + // In case of error, dispose the partial list of color settings + for (ColorSetting colorSetting : handler.colorSettings) { + colorSetting.dispose(); + } + return new ColorSetting[0]; + } + + // Helper class + private static class ColorSettingsContentHandler extends DefaultHandler { + + private List<ColorSetting> colorSettings = new ArrayList<>(0); + private RGB fg = new RGB(0, 0, 0); + private RGB bg = new RGB(255, 255, 255); + private RGB tickColor = new RGB(0, 0, 0); + private ITmfFilterTreeNode filter; + private TmfFilterContentHandler filterContentHandler; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + if (localName.equals(COLOR_SETTINGS_TAG)) { + colorSettings = new ArrayList<>(); + } else if (localName.equals(COLOR_SETTING_TAG)) { + fg = null; + bg = null; + filter = null; + } else if (localName.equals(FG_TAG)) { + int r = Integer.parseInt(attributes.getValue(R_ATTR)); + int g = Integer.parseInt(attributes.getValue(G_ATTR)); + int b = Integer.parseInt(attributes.getValue(B_ATTR)); + fg = new RGB(r, g, b); + } else if (localName.equals(BG_TAG)) { + int r = Integer.parseInt(attributes.getValue(R_ATTR)); + int g = Integer.parseInt(attributes.getValue(G_ATTR)); + int b = Integer.parseInt(attributes.getValue(B_ATTR)); + bg = new RGB(r, g, b); + } else if (localName.equals(TICK_TAG)) { + int r = Integer.parseInt(attributes.getValue(R_ATTR)); + int g = Integer.parseInt(attributes.getValue(G_ATTR)); + int b = Integer.parseInt(attributes.getValue(B_ATTR)); + tickColor = new RGB(r, g, b); + } else if (localName.equals(FILTER_TAG)) { + filterContentHandler = new TmfFilterContentHandler(); + } else if (filterContentHandler != null) { + filterContentHandler.startElement(uri, localName, qName, attributes); + } + } + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { + if (localName.equals(COLOR_SETTINGS_TAG)) { + // Nothing to do + } else if (localName.equals(COLOR_SETTING_TAG)) { + ColorSetting colorSetting = new ColorSetting(fg, bg, tickColor, filter); + colorSettings.add(colorSetting); + } else if (localName.equals(FILTER_TAG)) { + filter = filterContentHandler.getTree(); + filterContentHandler = null; + } else if (filterContentHandler != null) { + filterContentHandler.endElement(uri, localName, qName); + } + } + + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorsView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorsView.java new file mode 100644 index 0000000000..a5081a0566 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/ColorsView.java @@ -0,0 +1,588 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + * Bernd Hufmann - Updated to use RGB for the tick color + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.colors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.ColorDialog; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.tracecompass.tmf.ui.views.filter.FilterDialog; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme; +import org.eclipse.ui.IActionBars; + +/** + * Color view implementation. This view provides support for managing color settings for filters. + * + * @version 1.0 + * @author Patrick Tasse + * + */ +public class ColorsView extends TmfView { + + /** ID for the color view */ + public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.ui.views.colors"; //$NON-NLS-1$ + + private static final Image ADD_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/add_button.gif"); //$NON-NLS-1$ + private static final Image DELETE_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/delete_button.gif"); //$NON-NLS-1$ + private static final Image MOVE_UP_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/moveup_button.gif"); //$NON-NLS-1$ + private static final Image MOVE_DOWN_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/movedown_button.gif"); //$NON-NLS-1$ + private static final Image IMPORT_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/import_button.gif"); //$NON-NLS-1$ + private static final Image EXPORT_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/export_button.gif"); //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Main data structures + // ------------------------------------------------------------------------ + + /** + * The composite shell. + */ + protected Shell fShell; + /** + * The main composite (scrolled composite) + */ + protected ScrolledComposite fScrolledComposite; + /** + * The list composite. + */ + protected Composite fListComposite; + /** + * The filler composite. + */ + protected Composite fFillerComposite; + /** + * The selected color settings row + */ + protected ColorSettingRow fSelectedRow = null; + /** + * The color scheme instance for managing colors + */ + protected TimeGraphColorScheme traceColorScheme = new TimeGraphColorScheme(); + /** + * An action to add a color settings row + */ + protected Action fAddAction; + /** + * An action to delete a color settings row + */ + protected Action fDeleteAction; + /** + * An action to move up a color settings row in the list. + */ + protected Action fMoveUpAction; + /** + * An action to move down a color settings row in the list. + */ + protected Action fMoveDownAction; + /** + * An action to import color settings from file. + */ + protected Action fImportAction; + /** + * An action to export color settings from file. + */ + protected Action fExportAction; + /** + * The list of existing color settings + */ + protected List<ColorSetting> fColorSettings; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Default Constructor + */ + public ColorsView() { + super("Colors"); //$NON-NLS-1$ + } + + @Override + public void createPartControl(Composite parent) { + fShell = parent.getShell(); + + fScrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL); + fScrolledComposite.setExpandHorizontal(true); + fScrolledComposite.setExpandVertical(true); + fListComposite = new Composite(fScrolledComposite, SWT.NONE); + fScrolledComposite.setContent(fListComposite); + + GridLayout gl = new GridLayout(); + gl.marginHeight = 0; + gl.marginWidth = 0; + gl.verticalSpacing = 1; + fListComposite.setLayout(gl); + + fColorSettings = new ArrayList<>(Arrays.asList(ColorSettingsManager.getColorSettings())); + for (ColorSetting colorSetting : fColorSettings) { + new ColorSettingRow(fListComposite, colorSetting); + } + + fFillerComposite = new Composite(fListComposite, SWT.NONE); + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.heightHint = 0; + fFillerComposite.setLayoutData(gd); + gl = new GridLayout(); + gl.marginHeight = 1; + gl.marginWidth = 1; + fFillerComposite.setLayout(gl); + Label fillerLabel = new Label(fFillerComposite, SWT.NONE); + fillerLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + fillerLabel.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + + fFillerComposite.addPaintListener(new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + if (fSelectedRow == null) { + Color lineColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK); + Point p = fFillerComposite.getSize(); + GC gc = e.gc; + gc.setForeground(lineColor); + gc.drawLine(0, 0, p.x - 1, 0); + } + } + }); + + MouseListener mouseListener = new MouseAdapter() { + @Override + public void mouseDown(MouseEvent e) { + fSelectedRow = null; + refresh(); + } + }; + fillerLabel.addMouseListener(mouseListener); + + fScrolledComposite.setMinSize(fListComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + + fillToolBar(); + } + + @Override + public void setFocus() { + fScrolledComposite.setFocus(); + } + + /** + * Refreshes the view display and updates the view actions enablements. + */ + public void refresh() { + fListComposite.layout(); + fScrolledComposite.setMinSize(fListComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + fListComposite.redraw(0, 0, fListComposite.getBounds().width, fListComposite.getBounds().height, true); + if (fSelectedRow == null) { + fDeleteAction.setEnabled(false); + fMoveUpAction.setEnabled(false); + fMoveDownAction.setEnabled(false); + } else { + fDeleteAction.setEnabled(true); + fMoveUpAction.setEnabled(true); + fMoveDownAction.setEnabled(true); + } + } + + private void fillToolBar() { + + fAddAction = new AddAction(); + fAddAction.setImageDescriptor(ImageDescriptor.createFromImage(ADD_IMAGE)); + fAddAction.setToolTipText(Messages.ColorsView_AddActionToolTipText); + + fDeleteAction = new DeleteAction(); + fDeleteAction.setImageDescriptor(ImageDescriptor.createFromImage(DELETE_IMAGE)); + fDeleteAction.setToolTipText(Messages.ColorsView_DeleteActionToolTipText); + fDeleteAction.setEnabled(false); + + fMoveUpAction = new MoveUpAction(); + fMoveUpAction.setImageDescriptor(ImageDescriptor.createFromImage(MOVE_UP_IMAGE)); + fMoveUpAction.setToolTipText(Messages.ColorsView_MoveUpActionToolTipText); + fMoveUpAction.setEnabled(false); + + fMoveDownAction = new MoveDownAction(); + fMoveDownAction.setImageDescriptor(ImageDescriptor.createFromImage(MOVE_DOWN_IMAGE)); + fMoveDownAction.setToolTipText(Messages.ColorsView_MoveDownActionToolTipText); + fMoveDownAction.setEnabled(false); + + fExportAction = new ExportAction(); + fExportAction.setImageDescriptor(ImageDescriptor.createFromImage(EXPORT_IMAGE)); + fExportAction.setToolTipText(Messages.ColorsView_ExportActionToolTipText); + + fImportAction = new ImportAction(); + fImportAction.setImageDescriptor(ImageDescriptor.createFromImage(IMPORT_IMAGE)); + fImportAction.setToolTipText(Messages.ColorsView_ImportActionToolTipText); + + IActionBars bars = getViewSite().getActionBars(); + IToolBarManager manager = bars.getToolBarManager(); + manager.add(fAddAction); + manager.add(fDeleteAction); + manager.add(fMoveUpAction); + manager.add(fMoveDownAction); + manager.add(new Separator()); + manager.add(fExportAction); + manager.add(fImportAction); + } + + private class AddAction extends Action { + @Override + public void run() { + ColorSetting colorSetting = new ColorSetting( + Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND).getRGB(), + Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB(), + Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND).getRGB(), + null); + ColorSettingRow row = new ColorSettingRow(fListComposite, colorSetting); + if (fSelectedRow == null) { + fColorSettings.add(colorSetting); + row.moveAbove(fFillerComposite); + } else { + fColorSettings.add(fColorSettings.indexOf(fSelectedRow.getColorSetting()), colorSetting); + row.moveAbove(fSelectedRow); + } + fSelectedRow = row; + refresh(); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + } + } + + private class DeleteAction extends Action { + + @Override + public void run() { + if (fSelectedRow != null) { + int index = fColorSettings.indexOf(fSelectedRow.getColorSetting()); + fColorSettings.remove(index); + fSelectedRow.fColorSetting.dispose(); + fSelectedRow.dispose(); + if (index < fColorSettings.size()) { + fSelectedRow = (ColorSettingRow) fListComposite.getChildren()[index]; + } else { + fSelectedRow = null; + } + refresh(); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + } + } + } + + private class MoveUpAction extends Action { + @Override + public void run() { + if (fSelectedRow != null) { + int index = fColorSettings.indexOf(fSelectedRow.getColorSetting()); + if (index > 0) { + fColorSettings.add(index - 1, fColorSettings.remove(index)); + fSelectedRow.moveAbove(fListComposite.getChildren()[index - 1]); + refresh(); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + } + } + } + } + + private class MoveDownAction extends Action { + @Override + public void run() { + if (fSelectedRow != null) { + int index = fColorSettings.indexOf(fSelectedRow.getColorSetting()); + if (index < fColorSettings.size() - 1) { + fColorSettings.add(index + 1, fColorSettings.remove(index)); + + fSelectedRow.moveBelow(fListComposite.getChildren()[index + 1]); + refresh(); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + } + } + } + } + + private class ExportAction extends Action { + @Override + public void run() { + FileDialog fileDialog = new FileDialog(fShell, SWT.SAVE); + fileDialog.setFilterExtensions(new String[] {"*.xml"}); //$NON-NLS-1$ + fileDialog.setOverwrite(true); + String pathName = fileDialog.open(); + if (pathName != null) { + ColorSettingsXML.save(pathName, fColorSettings.toArray(new ColorSetting[0])); + } + } + } + + private class ImportAction extends Action { + @Override + public void run() { + FileDialog fileDialog = new FileDialog(fShell, SWT.OPEN); + fileDialog.setFilterExtensions(new String[] {"*.xml"}); //$NON-NLS-1$ + String pathName = fileDialog.open(); + if (pathName != null) { + ColorSetting[] colorSettings = ColorSettingsXML.load(pathName); + if (colorSettings.length > 0) { + if (fColorSettings.size() > 0) { + boolean overwrite = MessageDialog.openQuestion(fShell, + Messages.ColorsView_ImportOverwriteDialogTitle, + Messages.ColorsView_ImportOverwriteDialogMessage1 + + Messages.ColorsView_ImportOverwriteDialogMessage2); + if (overwrite) { + for (Control control : fListComposite.getChildren()) { + if (control instanceof ColorSettingRow) { + ((ColorSettingRow) control).fColorSetting.dispose(); + control.dispose(); + } + } + fColorSettings = new ArrayList<>(); + fSelectedRow = null; + } + } + for (ColorSetting colorSetting : colorSettings) { + ColorSettingRow row = new ColorSettingRow(fListComposite, colorSetting); + if (fSelectedRow == null) { + fColorSettings.add(colorSetting); + row.moveAbove(fFillerComposite); + } else { + fColorSettings.add(fColorSettings.indexOf(fSelectedRow.getColorSetting()), colorSetting); + row.moveAbove(fSelectedRow); + } + } + refresh(); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + } + } + } + } + + private class ColorSettingRow extends Composite { + + ColorSetting fColorSetting; + + public ColorSettingRow(final Composite parent, final ColorSetting colorSetting) { + super(parent, SWT.NONE); + fColorSetting = colorSetting; + + setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + + setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + GridLayout gl = new GridLayout(7, false); + gl.marginHeight = 1; + gl.marginWidth = 1; + gl.horizontalSpacing = 1; + gl.verticalSpacing = 0; + setLayout(gl); + + final Button fgButton = new Button(this, SWT.PUSH); + fgButton.setText(Messages.ColorsView_ForegroundButtonText); + fgButton.setSize(fgButton.computeSize(SWT.DEFAULT, 19)); + fgButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + + final Button bgButton = new Button(this, SWT.PUSH); + bgButton.setText(Messages.ColorsView_BackgroundButtonText); + bgButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + + final Composite labelComposite = new Composite(this, SWT.NONE); + labelComposite.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); + gl = new GridLayout(); + gl.marginHeight = 0; + gl.marginWidth = 0; + labelComposite.setLayout(gl); + labelComposite.setBackground(colorSetting.getBackgroundColor()); + + final Label label = new Label(labelComposite, SWT.NONE); + label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, true)); + label.setText(" Text "); //$NON-NLS-1$ + label.setForeground(colorSetting.getForegroundColor()); + label.setBackground(colorSetting.getBackgroundColor()); + + fgButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fSelectedRow = ColorSettingRow.this; + refresh(); + ColorDialog dialog = new ColorDialog(fShell); + dialog.setRGB(colorSetting.getForegroundRGB()); + dialog.setText(Messages.ColorsView_ForegroundDialogText); + dialog.open(); + RGB rgb = dialog.getRGB(); + if (rgb != null) { + colorSetting.setForegroundRGB(rgb); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + label.setForeground(colorSetting.getForegroundColor()); + } + }}); + + bgButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fSelectedRow = ColorSettingRow.this; + refresh(); + ColorDialog dialog = new ColorDialog(fShell); + dialog.setRGB(colorSetting.getBackgroundRGB()); + dialog.setText(Messages.ColorsView_BackgroundDialogText); + dialog.open(); + RGB rgb = dialog.getRGB(); + if (rgb != null) { + colorSetting.setBackgroundRGB(rgb); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + labelComposite.setBackground(colorSetting.getBackgroundColor()); + label.setBackground(colorSetting.getBackgroundColor()); + } + }}); + + final Button tickButton = new Button(this, SWT.PUSH); + tickButton.setText(Messages.ColorsView_TickButtonText); + tickButton.setSize(tickButton.computeSize(SWT.DEFAULT, 19)); + tickButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + + final Canvas tickCanvas = new Canvas(this, SWT.NONE); + GridData gd = new GridData(SWT.CENTER, SWT.FILL, false, false); + gd.widthHint = 12; + gd.heightHint = bgButton.getSize().y; + tickCanvas.setLayoutData(gd); + tickCanvas.setBackground(traceColorScheme.getBkColor(false, false, false)); + tickCanvas.addPaintListener(new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + Rectangle bounds = tickCanvas.getBounds(); + e.gc.setForeground(traceColorScheme.getColor(TimeGraphColorScheme.MID_LINE)); + int midy = bounds.y + bounds.height / 2 - 1; + //int midy = e.y + e.height / 2; + e.gc.drawLine(e.x, midy, e.x + e.width, midy); + Rectangle rect = new Rectangle(e.x + 1, bounds.y + 2, 0, bounds.height - 6); + for (int i = 1; i <= 3; i++) { + rect.x += i; + rect.width = i; + e.gc.setBackground(fColorSetting.getTickColor()); + e.gc.fillRectangle(rect); + } + }}); + + tickButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fSelectedRow = ColorSettingRow.this; + ColorDialog dialog = new ColorDialog(fShell); + dialog.setRGB(colorSetting.getTickColorRGB()); + dialog.setText(Messages.TickColorDialog_TickColorDialogTitle); + dialog.open(); + RGB rgb = dialog.getRGB(); + if (rgb != null) { + colorSetting.setTickColorRGB(rgb); + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + refresh(); + } + }}); + + final Button filterButton = new Button(this, SWT.PUSH); + filterButton.setText(Messages.ColorsView_FilterButtonText); + filterButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + + final Label filterText = new Label(this, SWT.NONE); + if (colorSetting.getFilter() != null) { + filterText.setText(colorSetting.getFilter().toString()); + filterText.setToolTipText(colorSetting.getFilter().toString()); + } + filterText.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + filterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + filterButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fSelectedRow = ColorSettingRow.this; + refresh(); + FilterDialog dialog = new FilterDialog(fShell); + dialog.setFilter(colorSetting.getFilter()); + dialog.open(); + if (dialog.getReturnCode() == Window.OK) { + if (dialog.getFilter() != null) { + colorSetting.setFilter(dialog.getFilter()); + filterText.setText(dialog.getFilter().toString()); + filterText.setToolTipText(dialog.getFilter().toString()); + } else { + colorSetting.setFilter(null); + filterText.setText(""); //$NON-NLS-1$ + filterText.setToolTipText(""); //$NON-NLS-1$ + } + ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0])); + refresh(); + } + }}); + + addPaintListener(new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + if (fSelectedRow == ColorSettingRow.this) { + Color borderColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK); + Point p = ColorSettingRow.this.getSize(); + Rectangle rect = new Rectangle(0, 0, p.x - 1, p.y - 1); + GC gc = e.gc; + gc.setForeground(borderColor); + gc.drawRectangle(rect); + } + } + }); + + MouseListener mouseListener = new MouseAdapter() { + @Override + public void mouseDown(MouseEvent e) { + fSelectedRow = ColorSettingRow.this; + refresh(); + } + }; + addMouseListener(mouseListener); + label.addMouseListener(mouseListener); + tickCanvas.addMouseListener(mouseListener); + filterText.addMouseListener(mouseListener); + } + + /** + * @return the ColorSetting + */ + public ColorSetting getColorSetting() { + return fColorSetting; + } + + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/IColorSettingsListener.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/IColorSettingsListener.java new file mode 100644 index 0000000000..629dbf1c79 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/colors/IColorSettingsListener.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.colors; + +/** + * A color change listener + * + * @version 1.0 + * @author Patrick Tasse + */ +public interface IColorSettingsListener { + + /** + * Notify the listener that the color settings have changed. + * + * @param colorSettings + * The new color settings + */ + void colorSettingsChanged(ColorSetting[] colorSettings); +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/distribution/model/BaseDistributionData.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/distribution/model/BaseDistributionData.java new file mode 100644 index 0000000000..3d9201ae60 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/distribution/model/BaseDistributionData.java @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + * Francois Chouinard - Moved from LTTng to TMF + ******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.distribution.model; + +/** + * Class with basic distribution data used for distribution models. + * + * It stores number of events (with timestamp) in buckets with a start time and a + * certain duration. The duration is the same across all buckets. + * Note that Timestamps are stored as long values. + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class BaseDistributionData { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * Constant indication that bucket is not filled. + */ + public final static int OUT_OF_RANGE_BUCKET = -1; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * Number of buckets + */ + protected final int fNbBuckets; + /** + * Duration of each bucket + */ + protected long fBucketDuration; + /** + * Bucket index of last event time + */ + protected int fLastBucket; + /** + * Timestamp of the first bucket. (could be negative when analyzing events with descending time!!!) + */ + protected long fFirstBucketTime; + /** + * Timestamp of the first event + */ + protected long fFirstEventTime; + /** + * Timestamp of the last event + */ + protected long fLastEventTime; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructs a base distribution data object. + * @param nbBuckets A total number of buckets + */ + public BaseDistributionData(int nbBuckets) { + fNbBuckets = nbBuckets; + clear(); + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + /** + * Returns the total number of buckets. + * + * @return the number of buckets. + */ + public int getNbBuckets() { + return fNbBuckets; + } + + /** + * Returns the duration of buckets. + * + * @return bucket duration + */ + public long getBucketDuration() { + return fBucketDuration; + } + + /** + * Set the bucket duration. + * + * @param bucketDuration The duration to set. + */ + public void setBucketDuration(long bucketDuration) { + fBucketDuration = bucketDuration; + } + + /** + * Returns the index of the last used bucket. + * + * @return last bucket index. + */ + public int getLastBucket() { + return fLastBucket; + } + + /** + * Sets the index of the last bucket used. + * + * @param lastBucket The last bucket index to set. + */ + public void setLastBucket(int lastBucket) { + fLastBucket = lastBucket; + } + + /** + * Returns the start time of the first bucket. + * + * @return first bucket time. + */ + public long getFirstBucketTime() { + return fFirstBucketTime; + } + + /** + * Sets the start time of the first bucket. + * + * @param firstBucketTime The bucket time to ser. + */ + public void setFirstBucketTime(long firstBucketTime) { + fFirstBucketTime = firstBucketTime; + } + + /** + * Returns the start time of the last bucket used. + * + * @return the start time of the last bucket. + */ + public long getLastBucketTime() { + return getBucketStartTime(fLastBucket); + } + + /** + * Returns the time of the event with the lowest timestamp. + * + * @return first event time. + */ + public long getFirstEventTime() { + return fFirstEventTime; + } + + /** + * Sets the time of the event with the lowest timestamp. + * + * @param firstEventTime The first event time to set. + */ + public void setFirstEventTime(long firstEventTime) { + fFirstEventTime = firstEventTime; + } + + /** + * Returns the time of the event with the biggest timestamp. + * + * @return the last event time. + */ + public long getLastEventTime() { + return fLastEventTime; + } + + /** + * Sets the time of the event with the biggest timestamp. + * + * @param lastEventTime The last event time to set. + */ + public void setLastEventTime(long lastEventTime) { + fLastEventTime = lastEventTime; + } + + /** + * Returns the bucket start time of a given bucket index. + * + * @param index The bucket index. + * @return the bucket start time of a given bucket index. + */ + public long getBucketStartTime(int index) { + return fFirstBucketTime + index * fBucketDuration; + } + + /** + * Returns the bucket end time of a given bucket index. + * + * @param index The bucket index. + * @return the bucket start time of a given bucket index. + */ + public long getBucketEndTime(int index) { + return getBucketStartTime(index) + fBucketDuration; + } + + /** + * Returns the bucket index of the bucket containing a given time. + * + * @param time The timestamp to check. + * @return the bucket index of the bucket containing the given time. + */ + public int getIndex(long time) { + return (int)((time - fFirstBucketTime) / fBucketDuration); + } + + /** + * Check if an index is valid. + * + * @param index + * The index to check + * @return If it's valid, true or false. + */ + public boolean isIndexValid(int index) { + return ((index >= 0) && (index <= fNbBuckets - 1)); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * Clears the data model to default values. + */ + public void clear() { + fFirstBucketTime = 0; + fFirstEventTime = 0; + fLastEventTime = 0; + fLastBucket = 0; + fBucketDuration = 1; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/distribution/model/IBaseDistributionModel.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/distribution/model/IBaseDistributionModel.java new file mode 100644 index 0000000000..34ba642ec6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/distribution/model/IBaseDistributionModel.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + * Francois Chouinard - Moved from LTTng to TMF + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.distribution.model; + +/** + * Base distribution model interface. + * + * Distribution models such histograms need to implement this interface. + * + * @version 1.0 + * @author Bernd Hufmann + * + */ +public interface IBaseDistributionModel { + /** + * Complete the model (all data received) + */ + void complete(); + + /** + * Clear the model (delete all data). + */ + void clear(); +}
\ No newline at end of file diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/CopyHandler.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/CopyHandler.java new file mode 100644 index 0000000000..9acb2ced8f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/CopyHandler.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2013 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.util.LocalSelectionTransfer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * Handler for copy command in filter view + * @author Xavier Raynaud <xavier.raynaud@kalray.eu> + * @since 3.0 + */ +public class CopyHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + // Check if we are closing down + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + return null; + } + IWorkbenchPage page = window.getActivePage(); + FilterView part = (FilterView) page.getActivePart(); + ISelection selection = getSelection(part); + + LocalSelectionTransfer.getTransfer().setSelection(selection); + LocalSelectionTransfer.getTransfer().setSelectionSetTime(System.currentTimeMillis()); + return null; + } + + /** + * Retrieve the current selection + * + * @param tcv + * the FilterView + * @return the current selection in the FilterView + */ + protected ISelection getSelection(FilterView tcv) { + return tcv.getViewSite().getSelectionProvider().getSelection(); + } + + @Override + public boolean isEnabled() { + // Check if we are closing down + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + return false; + } + + // Get the selection + IWorkbenchPage page = window.getActivePage(); + IWorkbenchPart part = page.getActivePart(); + if (part instanceof FilterView) { + FilterView tcv = (FilterView) part; + ISelection selection = tcv.getSite().getSelectionProvider().getSelection(); + if (!selection.isEmpty()) { + return true; + } + } + return false; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/CutHandler.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/CutHandler.java new file mode 100644 index 0000000000..f76396d0cc --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/CutHandler.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2013 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; + +/** + * Handler for cut command in filter view + * @author Xavier Raynaud <xavier.raynaud@kalray.eu> + * @since 3.0 + */ +public class CutHandler extends CopyHandler { + + @Override + protected ISelection getSelection(FilterView tcv) { + ISelection sel = super.getSelection(tcv); + if (sel instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection) sel; + Object o = selection.getFirstElement(); + if (o instanceof ITmfFilterTreeNode) { + ITmfFilterTreeNode node = (ITmfFilterTreeNode) o; + node = node.remove(); + tcv.refresh(); + return new StructuredSelection(node); + } + } + return sel; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/DeleteHandler.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/DeleteHandler.java new file mode 100644 index 0000000000..bb01f0eb34 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/DeleteHandler.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2013 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * Handler for delete command in filter view + * @author Xavier Raynaud <xavier.raynaud@kalray.eu> + * @since 3.0 + */ +public class DeleteHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + // Check if we are closing down + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + return null; + } + IWorkbenchPage page = window.getActivePage(); + FilterView part = (FilterView) page.getActivePart(); + ISelection sel = part.getViewSite().getSelectionProvider().getSelection(); + if (sel instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection) sel; + Object o = selection.getFirstElement(); + if (o instanceof ITmfFilterTreeNode) { + ITmfFilterTreeNode node = (ITmfFilterTreeNode) o; + node = node.remove(); + part.refresh(); + } + } + return null; + } + + @Override + public boolean isEnabled() { + // Check if we are closing down + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + return false; + } + + // Get the selection + IWorkbenchPage page = window.getActivePage(); + IWorkbenchPart part = page.getActivePart(); + if (part instanceof FilterView) { + FilterView tcv = (FilterView) part; + ISelection selection = tcv.getSite().getSelectionProvider().getSelection(); + if (!selection.isEmpty()) { + return true; + } + } + return false; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDialog.java new file mode 100644 index 0000000000..43707c8d98 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDialog.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode; + +/** + * The dialog for user-defined filters. + * + * @version 1.0 + * @author Patrick Tasse + */ +public class FilterDialog extends Dialog { + + TmfFilterNode fRoot; + FilterViewer fViewer; + + /** + * Constructor. + * + * @param shell + * The shell to which this dialog is attached + */ + public FilterDialog(Shell shell) { + super(shell); + setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX); + } + + @Override + protected Control createDialogArea(Composite parent) { + getShell().setText(Messages.FilterDialog_FilterDialogTitle); + getShell().setMinimumSize(getShell().computeSize(500, 200)); + Composite composite = (Composite) super.createDialogArea(parent); + + fViewer = new FilterViewer(composite, SWT.BORDER); + fViewer.setInput(fRoot); + return composite; + } + + /** + * @param filter + * the filter to set + */ + public void setFilter(ITmfFilterTreeNode filter) { + fRoot = new TmfFilterNode(null); + if (filter != null) { + fRoot.addChild(filter.clone()); + } + if (fViewer != null) { + fViewer.setInput(fRoot); + } + } + + /** + * @return the filter + */ + public ITmfFilterTreeNode getFilter() { + if (fRoot != null && fRoot.hasChildren()) { + return fRoot.getChild(0).clone(); + } + return null; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDragSourceAdapter.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDragSourceAdapter.java new file mode 100644 index 0000000000..ef05bd431f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDragSourceAdapter.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2013 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.jface.util.LocalSelectionTransfer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSourceAdapter; +import org.eclipse.swt.dnd.DragSourceEvent; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; + +/** + * DragSourceListener for filter view + * @author Xavier Raynaud <xavier.raynaud@kalray.eu> + */ +class FilterDragSourceAdapter extends DragSourceAdapter { + + private FilterViewer fViewer; + + /** + * Constructor + * + * @param viewer + * the content of the FilterView + */ + public FilterDragSourceAdapter(FilterViewer viewer) { + super(); + this.fViewer = viewer; + } + + @Override + public void dragStart(DragSourceEvent event) { + ISelection s = fViewer.getTreeViewer().getSelection(); + LocalSelectionTransfer.getTransfer().setSelection(s); + LocalSelectionTransfer.getTransfer().setSelectionSetTime(event.time & 0xFFFFFFFFL); + } + + @Override + public void dragSetData(DragSourceEvent event) { + event.data = LocalSelectionTransfer.getTransfer().getSelection(); + } + + @Override + public void dragFinished(DragSourceEvent event) { + if (event.detail == DND.DROP_MOVE) { + IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(); + for (Object data : selection.toList()) { + if (data instanceof ITmfFilterTreeNode) { + ITmfFilterTreeNode e = (ITmfFilterTreeNode) data; + e.remove(); + fViewer.refresh(); + } + } + } + LocalSelectionTransfer.getTransfer().setSelection(null); + LocalSelectionTransfer.getTransfer().setSelectionSetTime(0); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDropTargetAdapter.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDropTargetAdapter.java new file mode 100644 index 0000000000..557f77e670 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterDropTargetAdapter.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2013 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + + +import org.eclipse.jface.util.LocalSelectionTransfer; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetAdapter; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode; + +/** + * DropTargetListener for filter view + * @author Xavier Raynaud <xavier.raynaud@kalray.eu> + */ +class FilterDropTargetAdapter extends DropTargetAdapter { + + private FilterViewer fViewer; + + /** + * Constructor + * @param viewer the content of the FilterView + */ + public FilterDropTargetAdapter(FilterViewer viewer) { + super(); + this.fViewer = viewer; + } + + /** + * Returns <code>true</code> if droppedNode is an ancestor of node. + * + * @param droppedNode + * the ITmfFilterTreeNode to drop or paste + * @param node + * the ITmfFilterTreeNode receiving a new child + * @return <code>true</code> if droppedNode is and ancestor of node, + * <code>false</code> otherwise. + */ + private static boolean isAncestor(ITmfFilterTreeNode droppedNode, ITmfFilterTreeNode node) { + ITmfFilterTreeNode tmp = node; + + while (tmp != null) { + ITmfFilterTreeNode n = tmp.getParent(); + if (n == droppedNode) { + return true; + } + tmp = n; + } + return false; + } + + @Override + public void dropAccept(DropTargetEvent event) { + ITmfFilterTreeNode treeNodeToDrop = null; + if (LocalSelectionTransfer.getTransfer().isSupportedType(event.currentDataType)) { + treeNodeToDrop = FilterEditUtils.getTransferredTreeNode(); + } + if (treeNodeToDrop == null) { + // should never occur + event.detail = DND.DROP_NONE; + return; + } + if (event.item instanceof TreeItem) { + Object data = event.item.getData(); + if (data instanceof ITmfFilterTreeNode) { + ITmfFilterTreeNode node = (ITmfFilterTreeNode) data; + if (node.getValidChildren().contains(treeNodeToDrop.getNodeName())) { + if (isAncestor(treeNodeToDrop, node) && event.detail != DND.DROP_COPY) { + // do nothing in this case + event.detail = DND.DROP_NONE; + } + return; + } + } + } else { // accept only TmfFilterNode + if (!TmfFilterNode.NODE_NAME.equals(treeNodeToDrop.getNodeName())) { + event.detail = DND.DROP_NONE; + } + return; + } + event.detail = DND.DROP_NONE; + return; + } + + @Override + public void drop(DropTargetEvent event) { + ITmfFilterTreeNode treeNodeToDrop = FilterEditUtils.getTransferredTreeNode(); + if (event.item instanceof TreeItem) { + Object data = event.item.getData(); + if (data instanceof ITmfFilterTreeNode) { + ITmfFilterTreeNode node = (ITmfFilterTreeNode) data; + if (node.getValidChildren().contains(treeNodeToDrop.getNodeName())) { + treeNodeToDrop = treeNodeToDrop.clone(); + node.addChild(treeNodeToDrop); + fViewer.refresh(); + fViewer.setSelection(treeNodeToDrop, true); + return; + } + } + } else { // accept only TmfFilterNode + if (TmfFilterNode.NODE_NAME.equals(treeNodeToDrop.getNodeName())) { + ITmfFilterTreeNode root = fViewer.getInput(); + treeNodeToDrop = treeNodeToDrop.clone(); + root.addChild(treeNodeToDrop); + fViewer.refresh(); + fViewer.setSelection(treeNodeToDrop, true); + return; + } + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterEditUtils.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterEditUtils.java new file mode 100644 index 0000000000..7766d8f63c --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterEditUtils.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2013 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.jface.util.LocalSelectionTransfer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; + +/** + * Utilities for cut/copy/paste/dnd in filter view + * @author Xavier Raynaud <xavier.raynaud@kalray.eu> + */ +class FilterEditUtils { + + /** + * Gets the ITmfFilterTreeNode in LocalSelectionTransfer, if any + * @return a ITmfFilterTreeNode or <code>null</code> + */ + public static ITmfFilterTreeNode getTransferredTreeNode() { + ITmfFilterTreeNode treeNodeToDrop = null; + ISelection sel = LocalSelectionTransfer.getTransfer().getSelection(); + if (sel instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection) sel; + for (Object data : selection.toList()) { + if (!(data instanceof ITmfFilterTreeNode)) { + return null; + } else if (treeNodeToDrop != null) { + // should never occur, since tree has SWT.SINGLE style + return null; + } else { + treeNodeToDrop = (ITmfFilterTreeNode) data; + } + } + } + return treeNodeToDrop; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterManager.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterManager.java new file mode 100644 index 0000000000..fecd173708 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterManager.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterRootNode; +import org.eclipse.tracecompass.tmf.core.filter.xml.TmfFilterXMLParser; +import org.eclipse.tracecompass.tmf.core.filter.xml.TmfFilterXMLWriter; +import org.xml.sax.SAXException; + +/** + * Central filter manager + * + * @version 1.0 + * @author Patrick Tasse + */ +public class FilterManager { + + private static final String SAVED_FILTERS_FILE_NAME = "saved_filters.xml"; //$NON-NLS-1$ + private static final String SAVED_FILTERS_PATH_NAME = + Activator.getDefault().getStateLocation().addTrailingSeparator().append(SAVED_FILTERS_FILE_NAME).toString(); + + private static ITmfFilterTreeNode fRoot = new TmfFilterRootNode(); + static { + try { + fRoot = new TmfFilterXMLParser(SAVED_FILTERS_PATH_NAME).getTree(); + } catch (FileNotFoundException e) { + } catch (SAXException e) { + Activator.getDefault().logError("Error parsing saved filter xml file: " + SAVED_FILTERS_PATH_NAME, e); //$NON-NLS-1$ + } catch (IOException e) { + Activator.getDefault().logError("Error parsing saved filter xml file: " + SAVED_FILTERS_PATH_NAME, e); //$NON-NLS-1$ + } + } + + /** + * Retrieve the currently saved filters + * + * @return The array of filters + */ + public static ITmfFilterTreeNode[] getSavedFilters() { + return fRoot.clone().getChildren(); + } + + /** + * Set the passed filters as the currently saved ones. + * + * @param filters + * The filters to save + */ + public static void setSavedFilters(ITmfFilterTreeNode[] filters) { + fRoot = new TmfFilterRootNode(); + for (ITmfFilterTreeNode filter : filters) { + fRoot.addChild(filter.clone()); + } + try { + TmfFilterXMLWriter writerXML = new TmfFilterXMLWriter(fRoot); + writerXML.saveTree(SAVED_FILTERS_PATH_NAME); + } catch (ParserConfigurationException e) { + Activator.getDefault().logError("Error saving filter xml file: " + SAVED_FILTERS_PATH_NAME, e); //$NON-NLS-1$ + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterTreeContentProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterTreeContentProvider.java new file mode 100644 index 0000000000..ec9ec29cb9 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterTreeContentProvider.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Yuriy Vashchuk - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import java.util.ArrayList; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; + +/** + * This is the Content Provider of our tree + * + * @version 1.0 + * @author Yuriy Vashchuk + */ +public class FilterTreeContentProvider implements ITreeContentProvider { + + @Override + public void dispose() { + // TODO Auto-generated method stub + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // TODO Auto-generated method stub + } + + @Override + public Object[] getElements(Object inputElement) { + if (inputElement instanceof ITmfFilterTreeNode) { + ArrayList<ITmfFilterTreeNode> result = new ArrayList<>(); + for (int i = 0; i < ((ITmfFilterTreeNode) inputElement).getChildrenCount(); i++) { + result.add(((ITmfFilterTreeNode) inputElement).getChild(i)); + } + + return result.toArray(); + } + return null; + } + + @Override + public Object[] getChildren(Object parentElement) { + ArrayList<ITmfFilterTreeNode> result = new ArrayList<>(); + for (int i = 0; i < ((ITmfFilterTreeNode) parentElement).getChildrenCount(); i++) { + result.add(((ITmfFilterTreeNode) parentElement).getChild(i)); + } + return result.toArray(); + } + + @Override + public Object getParent(Object element) { + return ((ITmfFilterTreeNode) element).getParent(); + } + + @Override + public boolean hasChildren(Object element) { + return ((ITmfFilterTreeNode) element).hasChildren(); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterTreeLabelProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterTreeLabelProvider.java new file mode 100644 index 0000000000..53bb2caef1 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterTreeLabelProvider.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Yuriy Vashchuk - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAndNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterCompareNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterContainsNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterEqualsNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterEventTypeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterOrNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterCompareNode.Type; + +/** + * This is the Label Provider for our Filter Tree + * + * @version 1.0 + * @author Yuriy Vashchuk + */ +public class FilterTreeLabelProvider implements ILabelProvider { + + @Override + public void addListener(ILabelProviderListener listener) { + // TODO Auto-generated method stub + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + } + + @Override + public boolean isLabelProperty(Object element, String property) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void removeListener(ILabelProviderListener listener) { + // TODO Auto-generated method stub + } + + @Override + public Image getImage(Object element) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getText(Object element) { + String label = null; + + if (element instanceof TmfFilterNode) { + + TmfFilterNode node = (TmfFilterNode) element; + label = node.getNodeName() + " " + node.getFilterName(); //$NON-NLS-1$ + + } else if (element instanceof TmfFilterEventTypeNode) { + + TmfFilterEventTypeNode node = (TmfFilterEventTypeNode) element; + label = "WITH " + node.getNodeName() + (node.getName() != null ? " " + node.getName() : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if (element instanceof TmfFilterAndNode) { + + TmfFilterAndNode node = (TmfFilterAndNode) element; + label = (node.isNot() ? "NOT " : "") + node.getNodeName(); //$NON-NLS-1$ //$NON-NLS-2$ + + } else if (element instanceof TmfFilterOrNode) { + + TmfFilterOrNode node = (TmfFilterOrNode) element; + label = (node.isNot() ? "NOT " : "") + node.getNodeName(); //$NON-NLS-1$ //$NON-NLS-2$ + + } else if (element instanceof TmfFilterContainsNode) { + + TmfFilterContainsNode node = (TmfFilterContainsNode) element; + label = (node.isNot() ? "NOT " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + (node.getField() != null ? node.getField() + " " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + node.getNodeName() + + (node.getValue() != null && node.getValue().length() > 0 ? " \"" + node.getValue() + "\"" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if (element instanceof TmfFilterEqualsNode) { + + TmfFilterEqualsNode node = (TmfFilterEqualsNode) element; + label = (node.isNot() ? "NOT " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + (node.getField() != null ? node.getField() + " " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + node.getNodeName() + + (node.getValue() != null && node.getValue().length() > 0 ? " \"" + node.getValue() + "\"" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if (element instanceof TmfFilterMatchesNode) { + + TmfFilterMatchesNode node = (TmfFilterMatchesNode) element; + label = (node.isNot() ? "NOT " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + (node.getField() != null ? node.getField() + " " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + node.getNodeName() + + (node.getRegex() != null && node.getRegex().length() > 0 ? " \"" + node.getRegex() + "\"" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if (element instanceof TmfFilterCompareNode) { + + TmfFilterCompareNode node = (TmfFilterCompareNode) element; + label = (node.isNot() ? "NOT " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + (node.getField() != null ? node.getField() + " " : "") + //$NON-NLS-1$ //$NON-NLS-2$ + (node.getResult() < 0 ? "<" : (node.getResult() > 0 ? ">" : "=")) + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + (node.getValue() != null && node.getValue().length() > 0 ? + (node.getType() == Type.ALPHA ? " \"" + node.getValue() + "\"" : //$NON-NLS-1$ //$NON-NLS-2$ + (node.getType() == Type.TIMESTAMP ? " [" + node.getValue() + "]" : //$NON-NLS-1$ //$NON-NLS-2$ + " " + node.getValue())) : ""); //$NON-NLS-1$//$NON-NLS-2$ + + } + return label; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterView.java new file mode 100644 index 0000000000..d3ee36c7eb --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterView.java @@ -0,0 +1,309 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Yuriy Vashchuk - Initial API and implementation + * Xavier Raynaud - add cut/copy/paste/dnd support + * based on Francois Chouinard ProjectView code. + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import java.io.IOException; + +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterRootNode; +import org.eclipse.tracecompass.tmf.core.filter.xml.TmfFilterXMLParser; +import org.eclipse.tracecompass.tmf.core.filter.xml.TmfFilterXMLWriter; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.ui.IActionBars; +import org.xml.sax.SAXException; + +/** + * View that contain UI to the TMF filter. + * + * @version 1.0 + * @author Yuriy Vashchuk + */ +public class FilterView extends TmfView { + + /** ID for the Filter view */ + public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.ui.views.filter"; //$NON-NLS-1$ + + private static final Image SAVE_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/save_button.gif"); //$NON-NLS-1$ + private static final Image ADD_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/add_button.gif"); //$NON-NLS-1$ + private static final Image IMPORT_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/import_button.gif"); //$NON-NLS-1$ + private static final Image EXPORT_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/export_button.gif"); //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Main data structures + // ------------------------------------------------------------------------ + + private FilterViewer fViewer; + private final ITmfFilterTreeNode fRoot; + + private final IWorkspace fWorkspace; + + private SaveAction fSaveAction; + private AddAction fAddAction; + private ExportAction fExportAction; + private ImportAction fImportAction; + + /** + * Getter for the Filter Tree Root + * + * @return The root of builded tree + */ + public ITmfFilterTreeNode getFilterRoot() { + return fRoot; + } + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Default Constructor + */ + public FilterView() { + super("Filter"); //$NON-NLS-1$ + + fWorkspace = ResourcesPlugin.getWorkspace(); + try { + fWorkspace.getRoot().refreshLocal(IResource.DEPTH_INFINITE, null); + } catch (CoreException e) { + Activator.getDefault().logError("Error refreshing workspace", e); //$NON-NLS-1$ + } + + fRoot = new TmfFilterRootNode(); + for (ITmfFilterTreeNode node : FilterManager.getSavedFilters()) { + fRoot.addChild(node); + } + } + + /** + * Add a filter to the FilterView. This does not modify the XML, which must + * be done manually. If the filter is already in the FilterView, this is a + * no-op. + * + * @param filter + * The filter to add. + * @since 3.1 + */ + public void addFilter(ITmfFilterTreeNode filter) { + ITmfFilterTreeNode root = fViewer.getInput(); + for (ITmfFilterTreeNode node : root.getChildren()) { + if (node.equals(filter)) { + return; + } + } + root.addChild(filter); + fViewer.setInput(root); + } + + /** + * Refresh the tree widget + */ + public void refresh() { + fViewer.refresh(); + } + + /** + * Setter for selection + * + * @param node + * The node to select + */ + public void setSelection(ITmfFilterTreeNode node) { + fViewer.setSelection(node, true); + } + + // ------------------------------------------------------------------------ + // ViewPart + // ------------------------------------------------------------------------ + + @Override + public void createPartControl(Composite parent) { + + fViewer = new FilterViewer(parent, SWT.NONE); + fViewer.setInput(fRoot); + + contributeToActionBars(); + + fViewer.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + if (!(event.getSelection().isEmpty()) && event.getSelection() instanceof IStructuredSelection) { + fExportAction.setEnabled(true); + } else { + fExportAction.setEnabled(false); + } + } + }); + this.getSite().setSelectionProvider(fViewer.getTreeViewer()); + + MenuManager menuManager = fViewer.getMenuManager(); + this.getSite().registerContextMenu(menuManager, fViewer.getTreeViewer()); + } + + /** + * @return the ITmfFilterTreeNode currently selected + */ + ITmfFilterTreeNode getSelection() { + return fViewer.getSelection(); + } + + @Override + public void setFocus() { + fViewer.setFocus(); + } + + @Override + public String toString() { + return "[FilterView]"; //$NON-NLS-1$ + } + + /** + * Builds the menu toolbar + */ + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + // fillLocalPullDown(bars.getMenuManager()); + fillLocalToolBar(bars.getToolBarManager()); + } + + /** + * Build the popup menu + * + * @param manager + * The manager to build + */ + private void fillLocalToolBar(IToolBarManager manager) { + + fSaveAction = new SaveAction(); + fSaveAction.setImageDescriptor(ImageDescriptor.createFromImage(SAVE_IMAGE)); + fSaveAction.setToolTipText(Messages.FilterView_SaveActionToolTipText); + + fAddAction = new AddAction(); + fAddAction.setImageDescriptor(ImageDescriptor.createFromImage(ADD_IMAGE)); + fAddAction.setToolTipText(Messages.FilterView_AddActionToolTipText); + + fExportAction = new ExportAction(); + fExportAction.setImageDescriptor(ImageDescriptor.createFromImage(EXPORT_IMAGE)); + fExportAction.setToolTipText(Messages.FilterView_ExportActionToolTipText); + + fImportAction = new ImportAction(); + fImportAction.setImageDescriptor(ImageDescriptor.createFromImage(IMPORT_IMAGE)); + fImportAction.setToolTipText(Messages.FilterView_ImportActionToolTipText); + + manager.add(fSaveAction); + manager.add(new Separator("add_delete")); //$NON-NLS-1$ + manager.add(fAddAction); + manager.add(new Separator("edit")); //$NON-NLS-1$ + manager.add(new Separator()); + manager.add(fExportAction); + manager.add(fImportAction); + } + + private class SaveAction extends Action { + @Override + public void run() { + FilterManager.setSavedFilters(fRoot.getChildren()); + } + } + + private class AddAction extends Action { + @Override + public void run() { + + TmfFilterNode newNode = new TmfFilterNode(fRoot, ""); //$NON-NLS-1$ + refresh(); + setSelection(newNode); + } + } + + private class ExportAction extends Action { + @Override + public void run() { + try { + FileDialog dlg = new FileDialog(new Shell(), SWT.SAVE); + dlg.setFilterNames(new String[] { Messages.FilterView_FileDialogFilterName + " (*.xml)" }); //$NON-NLS-1$ + dlg.setFilterExtensions(new String[] { "*.xml" }); //$NON-NLS-1$ + + String fn = dlg.open(); + if (fn != null) { + TmfFilterXMLWriter writerXML = new TmfFilterXMLWriter(fRoot); + writerXML.saveTree(fn); + } + + } catch (ParserConfigurationException e) { + Activator.getDefault().logError("Error parsing filter xml file", e); //$NON-NLS-1$ + } + } + } + + private class ImportAction extends Action { + @Override + public void run() { + if (fViewer != null) { + ITmfFilterTreeNode root = null; + try { + FileDialog dlg = new FileDialog(new Shell(), SWT.OPEN); + dlg.setFilterNames(new String[] { Messages.FilterView_FileDialogFilterName + " (*.xml)" }); //$NON-NLS-1$ + dlg.setFilterExtensions(new String[] { "*.xml" }); //$NON-NLS-1$ + + TmfFilterXMLParser parserXML = null; + String fn = dlg.open(); + if (fn != null) { + parserXML = new TmfFilterXMLParser(fn); + root = parserXML.getTree(); + } + + } catch (SAXException e) { + Activator.getDefault().logError("Error importing filter xml file", e); //$NON-NLS-1$ + } catch (IOException e) { + Activator.getDefault().logError("Error importing filter xml file", e); //$NON-NLS-1$ + } + + if (root != null) { + for (ITmfFilterTreeNode node : root.getChildren()) { + if (node instanceof TmfFilterNode) { + fRoot.addChild(node); + refresh(); + fViewer.setSelection(node); + } + } + } + } + } + } + +}
\ No newline at end of file diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterViewer.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterViewer.java new file mode 100644 index 0000000000..af432330ae --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/FilterViewer.java @@ -0,0 +1,1124 @@ +/******************************************************************************* + * Copyright (c) 2010, 2014 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + * Xavier Raynaud - add cut/copy/paste/dnd support + * Vincent Perot - Add subfield filtering + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.util.LocalSelectionTransfer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSource; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.event.ITmfEventType; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAndNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterCompareNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterContainsNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterEqualsNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterEventTypeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterOrNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterRootNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterCompareNode.Type; +import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTraceDefinition; +import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTxtEvent; +import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTxtTraceDefinition; +import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlEvent; +import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTraceDefinition; +import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTraceDefinition.OutputColumn; +import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType; + +class FilterViewer extends Composite { + + private static final String SEP = " : "; //$NON-NLS-1$ + + private TreeViewer fViewer; + + private Composite fComposite; + private MenuManager fMenuManager; + + public FilterViewer(Composite parent, int style) { + super(parent, style); + + setLayout(new FillLayout()); + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + setLayoutData(gd); + + final SashForm sash = new SashForm(this, SWT.HORIZONTAL); + + // Create the tree viewer to display the filter tree + fViewer = new TreeViewer(sash, SWT.NONE); + fViewer.setContentProvider(new FilterTreeContentProvider()); + fViewer.setLabelProvider(new FilterTreeLabelProvider()); + fViewer.setInput(new TmfFilterRootNode()); + + // Create the empty filter node properties panel + fComposite = new Composite(sash, SWT.NONE); + GridLayout gl = new GridLayout(); + gl.marginHeight = 0; + gl.marginWidth = 0; + fComposite.setLayout(gl); + + createContextMenu(); + + fViewer.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + if (!(event.getSelection().isEmpty()) && event.getSelection() instanceof IStructuredSelection) { + // Update the filter node properties panel to the selection + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + ITmfFilterTreeNode node = (ITmfFilterTreeNode) selection.getFirstElement(); + updateFilterNodeComposite(node); + // Highlight the selection's children + highlightTreeItems(fViewer.getTree().getSelection()[0].getItems()); + } else { + updateFilterNodeComposite(null); + } + } + }); + + fViewer.getTree().addPaintListener(new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + TmfFilterTreeNode root = (TmfFilterTreeNode) fViewer.getInput(); + if (root == null || root.getChildrenCount() == 0) { + e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); + e.gc.drawText(Messages.FilterViewer_EmptyTreeHintText, 5, 0); + } + } + }); + + int operations = DND.DROP_MOVE | DND.DROP_COPY; + DragSource dragSource = new org.eclipse.swt.dnd.DragSource(fViewer.getTree(), operations); + dragSource.setTransfer(new Transfer[] { LocalSelectionTransfer.getTransfer() }); + dragSource.addDragListener(new FilterDragSourceAdapter(this)); + DropTarget dropTarget = new DropTarget(fViewer.getTree(), operations); + dropTarget.setTransfer(new Transfer[] { LocalSelectionTransfer.getTransfer() }); + dropTarget.addDropListener(new FilterDropTargetAdapter(this)); + } + + /** + * Create the context menu for the tree viewer + */ + private void createContextMenu() { + // Adds root context menu + fMenuManager = new MenuManager(); + fMenuManager.setRemoveAllWhenShown(true); + fMenuManager.addMenuListener(new IMenuListener() { + @Override + public void menuAboutToShow(IMenuManager manager) { + fillContextMenu(manager); + } + }); + + // Context + Menu contextMenu = fMenuManager.createContextMenu(fViewer.getTree()); + + // Publish it + fViewer.getTree().setMenu(contextMenu); + } + + public MenuManager getMenuManager() { + return fMenuManager; + } + + /** + * Fill the context menu for the tree viewer. + * + * @param manager + * The menu manager + */ + protected void fillContextMenu(IMenuManager manager) { + final ISelection selection = fViewer.getSelection(); + ITmfFilterTreeNode filterTreeNode = null; + if (selection instanceof StructuredSelection) { + Object element = ((StructuredSelection) selection).getFirstElement(); + if (element instanceof ITmfFilterTreeNode) { + filterTreeNode = (ITmfFilterTreeNode) element; + } + } + + if (filterTreeNode != null) { + fillContextMenuForNode(filterTreeNode, manager); + } + manager.add(new Separator("delete")); //$NON-NLS-1$ + manager.add(new Separator("edit")); //$NON-NLS-1$ + + if (fViewer.getInput() instanceof TmfFilterRootNode || filterTreeNode == null) { + manager.add(new Separator()); + ITmfFilterTreeNode root = (ITmfFilterTreeNode) fViewer.getInput(); + fillContextMenuForNode(root, manager); + } + } + + /** + * Fill the context menu with the valid children of the provided node + * + * @param node + * The target node + * @param manager + * The menu manager + */ + protected void fillContextMenuForNode(final ITmfFilterTreeNode node, IMenuManager manager) { + for (final String child : node.getValidChildren()) { + final Action action = new Action() { + @Override + public void run() { + ITmfFilterTreeNode newNode = null; + if (TmfFilterNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterNode(node, ""); //$NON-NLS-1$ + } else if (TmfFilterEventTypeNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterEventTypeNode(node); + } else if (TmfFilterAndNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterAndNode(node); + } else if (TmfFilterOrNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterOrNode(node); + } else if (TmfFilterContainsNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterContainsNode(node); + } else if (TmfFilterEqualsNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterEqualsNode(node); + } else if (TmfFilterMatchesNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterMatchesNode(node); + } else if (TmfFilterCompareNode.NODE_NAME.equals(child)) { + newNode = new TmfFilterCompareNode(node); + } + if (newNode != null) { + fViewer.refresh(); + fViewer.setSelection(new StructuredSelection(newNode), true); + } + } + }; + if (TmfFilterNode.NODE_NAME.equals(child)) { + action.setText(Messages.FilterViewer_NewPrefix + " " + child); //$NON-NLS-1$ + } else { + action.setText(child); + } + manager.add(action); + } + } + + /** + * Create the appropriate filter node properties composite + */ + private void updateFilterNodeComposite(ITmfFilterTreeNode node) { + for (Control control : fComposite.getChildren()) { + control.dispose(); + } + + if (node instanceof TmfFilterNode) { + new FilterNodeComposite(fComposite, (TmfFilterNode) node); + } else if (node instanceof TmfFilterEventTypeNode) { + new FilterEventTypeNodeComposite(fComposite, (TmfFilterEventTypeNode) node); + } else if (node instanceof TmfFilterAndNode) { + new FilterAndNodeComposite(fComposite, (TmfFilterAndNode) node); + } else if (node instanceof TmfFilterOrNode) { + new FilterOrNodeComposite(fComposite, (TmfFilterOrNode) node); + } else if (node instanceof TmfFilterContainsNode) { + new FilterContainsNodeComposite(fComposite, (TmfFilterContainsNode) node); + } else if (node instanceof TmfFilterEqualsNode) { + new FilterEqualsNodeComposite(fComposite, (TmfFilterEqualsNode) node); + } else if (node instanceof TmfFilterMatchesNode) { + new FilterMatchesNodeComposite(fComposite, (TmfFilterMatchesNode) node); + } else if (node instanceof TmfFilterCompareNode) { + new FilterCompareNodeComposite(fComposite, (TmfFilterCompareNode) node); + } else { + new FilterBaseNodeComposite(fComposite); + } + fComposite.layout(); + } + + /** + * Highlight the provided tree items + */ + private void highlightTreeItems(TreeItem[] items) { + resetTreeItems(fViewer.getTree().getItems()); + for (TreeItem item : items) { + item.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); + } + + } + + /** + * Reset the provided tree items (remove highlight) + */ + private void resetTreeItems(TreeItem[] items) { + for (TreeItem item : items) { + item.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); + resetTreeItems(item.getItems()); + } + } + + public void setInput(ITmfFilterTreeNode root) { + fViewer.setInput(root); + fViewer.expandAll(); + + updateFilterNodeComposite(null); + } + + public ITmfFilterTreeNode getInput() { + return (ITmfFilterTreeNode) fViewer.getInput(); + } + + public void refresh() { + fViewer.refresh(); + } + + public void setSelection(ITmfFilterTreeNode node, boolean reveal) { + fViewer.setSelection(new StructuredSelection(node), reveal); + } + + public void setSelection(ITmfFilterTreeNode node) { + fViewer.setSelection(new StructuredSelection(node)); + } + + public ITmfFilterTreeNode getSelection() { + final ISelection selection = fViewer.getSelection(); + ITmfFilterTreeNode filterTreeNode = null; + if (selection instanceof StructuredSelection) { + Object element = ((StructuredSelection) selection).getFirstElement(); + if (element instanceof ITmfFilterTreeNode) { + filterTreeNode = (ITmfFilterTreeNode) element; + } + } + + final ITmfFilterTreeNode selectedNode = filterTreeNode; + return selectedNode; + } + + public void addSelectionChangedListener(ISelectionChangedListener listener) { + fViewer.addSelectionChangedListener(listener); + } + + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + fViewer.removeSelectionChangedListener(listener); + } + + /** + * Gets the TreeViewer displaying filters + * + * @return a {@link TreeViewer} + */ + TreeViewer getTreeViewer() { + return fViewer; + } + + private class FilterBaseNodeComposite extends Composite { + + FilterBaseNodeComposite(Composite parent) { + super(parent, SWT.NONE); + setLayout(new GridLayout(2, false)); + setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + } + + protected Map<String, Object> getEventsTypeMap() { + Map<String, Object> eventsTypeMap = new TreeMap<>(); + for (IConfigurationElement ce : TmfTraceType.getTypeElements()) { + String categoryPrefix = ""; //$NON-NLS-1$ + String categoryId = ce.getAttribute(TmfTraceType.CATEGORY_ATTR); + if (categoryId != null) { + categoryPrefix = TmfTraceType.getCategoryName(categoryId) + SEP; + } + String text = categoryPrefix + ce.getAttribute(TmfTraceType.NAME_ATTR); + eventsTypeMap.put(text, ce); + } + for (CustomTxtTraceDefinition def : CustomTxtTraceDefinition.loadAll()) { + String text = def.categoryName + SEP + def.definitionName; + eventsTypeMap.put(text, def); + } + for (CustomXmlTraceDefinition def : CustomXmlTraceDefinition.loadAll()) { + String text = def.categoryName + SEP + def.definitionName; + eventsTypeMap.put(text, def); + } + return eventsTypeMap; + } + + protected String[] getFieldsList(ITmfFilterTreeNode node) { + ArrayList<String> fieldsList = new ArrayList<>(); + ITmfFilterTreeNode curNode = node; + while (curNode != null) { + if (curNode instanceof TmfFilterEventTypeNode) { + TmfFilterEventTypeNode eventTypeNode = (TmfFilterEventTypeNode) curNode; + for (IConfigurationElement ce : TmfTraceType.getTypeElements()) { + if (ce.getAttribute(TmfTraceType.EVENT_TYPE_ATTR).equals(eventTypeNode.getEventType())) { + try { + ITmfEvent event = (ITmfEvent) ce.createExecutableExtension(TmfTraceType.EVENT_TYPE_ATTR); + ITmfEventType eventType = event.getType(); + if (eventType != null) { + for (String field : eventType.getRootField().getFieldNames()) { + fieldsList.add(field); + } + } + } catch (CoreException e) { + } + if (fieldsList.size() == 0) { + fieldsList.add(ITmfEvent.EVENT_FIELD_TIMESTAMP); + fieldsList.add(ITmfEvent.EVENT_FIELD_SOURCE); + fieldsList.add(ITmfEvent.EVENT_FIELD_TYPE); + fieldsList.add(ITmfEvent.EVENT_FIELD_REFERENCE); + fieldsList.add(ITmfEvent.EVENT_FIELD_CONTENT); + } + return fieldsList.toArray(new String[0]); + } + } + if (eventTypeNode.getEventType() != null && eventTypeNode.getEventType().startsWith(CustomTxtEvent.class.getCanonicalName())) { + for (CustomTxtTraceDefinition def : CustomTxtTraceDefinition.loadAll()) { + if (eventTypeNode.getEventType().equals(CustomTxtEvent.class.getCanonicalName() + ':' + def.categoryName + ':' + def.definitionName)) { + for (OutputColumn output : def.outputs) { + fieldsList.add(output.name); + } + return fieldsList.toArray(new String[0]); + } + } + } + if (eventTypeNode.getEventType() != null && eventTypeNode.getEventType().startsWith(CustomXmlEvent.class.getCanonicalName())) { + for (CustomXmlTraceDefinition def : CustomXmlTraceDefinition.loadAll()) { + if (eventTypeNode.getEventType().equals(CustomXmlEvent.class.getCanonicalName() + ':' + def.categoryName + ':' + def.definitionName)) { + for (OutputColumn output : def.outputs) { + fieldsList.add(output.name); + } + return fieldsList.toArray(new String[0]); + } + } + } + } + curNode = curNode.getParent(); + } + + fieldsList.add(Messages.FilterViewer_CommonCategory); + fieldsList.add(ITmfEvent.EVENT_FIELD_TIMESTAMP); + fieldsList.add(ITmfEvent.EVENT_FIELD_SOURCE); + fieldsList.add(ITmfEvent.EVENT_FIELD_TYPE); + fieldsList.add(ITmfEvent.EVENT_FIELD_REFERENCE); + fieldsList.add(ITmfEvent.EVENT_FIELD_CONTENT); + fieldsList.add(""); //$NON-NLS-1$ + + for (Entry<String, Object> eventTypeEntry : getEventsTypeMap().entrySet()) { + Object value = eventTypeEntry.getValue(); + if (value instanceof IConfigurationElement) { + IConfigurationElement ce = (IConfigurationElement) value; + try { + ITmfEvent event = (ITmfEvent) ce.createExecutableExtension(TmfTraceType.EVENT_TYPE_ATTR); + ITmfEventType eventType = event.getType(); + if (eventType != null && eventType.getFieldNames().size() > 0) { + String categoryId = ce.getAttribute(TmfTraceType.CATEGORY_ATTR); + if (categoryId != null) { + fieldsList.add('[' + TmfTraceType.getCategoryName(categoryId) + SEP + + ce.getAttribute(TmfTraceType.NAME_ATTR) + ']'); + } else { + fieldsList.add('[' + ce.getAttribute(TmfTraceType.NAME_ATTR) + ']'); + } + for (String field : eventType.getFieldNames()) { + fieldsList.add(field); + } + fieldsList.add(""); //$NON-NLS-1$ + } + } catch (CoreException e) { + } + } else if (value instanceof CustomTraceDefinition) { + CustomTraceDefinition def = (CustomTraceDefinition) value; + if (def.outputs.size() > 0) { + fieldsList.add('[' + def.categoryName + SEP + def.definitionName + ']'); + for (OutputColumn output : def.outputs) { + fieldsList.add(output.name); + } + fieldsList.add(""); //$NON-NLS-1$ + } + } + } + return fieldsList.toArray(new String[0]); + } + } + + private class FilterNodeComposite extends FilterBaseNodeComposite { + TmfFilterNode fNode; + Text fNameText; + + FilterNodeComposite(Composite parent, TmfFilterNode node) { + super(parent); + fNode = node; + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_NameLabel); + + fNameText = new Text(this, SWT.BORDER); + fNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + if (node.getFilterName() != null && node.getFilterName().length() > 0) { + fNameText.setText(node.getFilterName()); + } else { + fNameText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fNameText.setText(Messages.FilterViewer_FilterNameHint); + } + fNameText.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (fNode.getFilterName() == null || fNode.getFilterName().length() == 0) { + fNameText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fNameText.setText(Messages.FilterViewer_FilterNameHint); + } + } + + @Override + public void focusGained(FocusEvent e) { + if (fNameText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fNameText.setText(""); //$NON-NLS-1$ + } + fNameText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); + } + }); + fNameText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + if (!fNameText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fNode.setFilterName(fNameText.getText()); + fViewer.refresh(fNode); + } + } + }); + } + } + + private class FilterEventTypeNodeComposite extends FilterBaseNodeComposite { + TmfFilterEventTypeNode fNode; + Combo fTypeCombo; + Map<String, Object> fEventsTypeMap; + + FilterEventTypeNodeComposite(Composite parent, TmfFilterEventTypeNode node) { + super(parent); + fNode = node; + fEventsTypeMap = getEventsTypeMap(); + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_TypeLabel); + + fTypeCombo = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); + fTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + fTypeCombo.setItems(fEventsTypeMap.keySet().toArray(new String[0])); + if (fNode.getEventType() != null) { + fTypeCombo.setText(fNode.getName()); + } + fTypeCombo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + for (Entry<String, Object> eventTypeEntry : fEventsTypeMap.entrySet()) { + if (eventTypeEntry.getKey().equals(fTypeCombo.getText())) { + Object value = eventTypeEntry.getValue(); + if (value instanceof IConfigurationElement) { + IConfigurationElement ce = (IConfigurationElement) value; + fNode.setEventType(ce.getAttribute(TmfTraceType.EVENT_TYPE_ATTR)); + String categoryId = ce.getAttribute(TmfTraceType.CATEGORY_ATTR); + if (categoryId != null) { + fNode.setName(TmfTraceType.getCategoryName(categoryId) + SEP + + ce.getAttribute(TmfTraceType.NAME_ATTR)); + } else { + fNode.setName(ce.getAttribute(TmfTraceType.NAME_ATTR)); + } + } else if (value instanceof CustomTxtTraceDefinition) { + CustomTxtTraceDefinition def = (CustomTxtTraceDefinition) value; + fNode.setEventType(CustomTxtEvent.class.getCanonicalName() + ':' + def.categoryName + ':' + def.definitionName); + fNode.setName(def.categoryName + SEP + def.definitionName); + } else if (value instanceof CustomXmlTraceDefinition) { + CustomXmlTraceDefinition def = (CustomXmlTraceDefinition) value; + fNode.setEventType(CustomXmlEvent.class.getCanonicalName() + ':' + def.categoryName + ':' + def.definitionName); + fNode.setName(def.categoryName + SEP + def.definitionName); + } + fViewer.refresh(fNode); + break; + } + } + } + }); + } + } + + private class FilterAndNodeComposite extends FilterBaseNodeComposite { + TmfFilterAndNode fNode; + Button fNotButton; + + FilterAndNodeComposite(Composite parent, TmfFilterAndNode node) { + super(parent); + fNode = node; + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_NotLabel); + + fNotButton = new Button(this, SWT.CHECK); + fNotButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fNotButton.setSelection(fNode.isNot()); + fNotButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setNot(fNotButton.getSelection()); + fViewer.refresh(fNode); + } + }); + } + } + + private class FilterOrNodeComposite extends FilterBaseNodeComposite { + TmfFilterOrNode fNode; + Button fNotButton; + + FilterOrNodeComposite(Composite parent, TmfFilterOrNode node) { + super(parent); + fNode = node; + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_NotLabel); + + fNotButton = new Button(this, SWT.CHECK); + fNotButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fNotButton.setSelection(fNode.isNot()); + fNotButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setNot(fNotButton.getSelection()); + fViewer.refresh(fNode); + } + }); + } + } + + private class FilterContainsNodeComposite extends FilterBaseNodeComposite { + TmfFilterContainsNode fNode; + Button fNotButton; + Combo fFieldCombo; + Text fValueText; + Button fIgnoreCaseButton; + + FilterContainsNodeComposite(Composite parent, TmfFilterContainsNode node) { + super(parent); + fNode = node; + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_NotLabel); + + fNotButton = new Button(this, SWT.CHECK); + fNotButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fNotButton.setSelection(fNode.isNot()); + fNotButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setNot(fNotButton.getSelection()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_FieldLabel); + + fFieldCombo = new Combo(this, SWT.DROP_DOWN); + fFieldCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + fFieldCombo.setItems(getFieldsList(fNode)); + fFieldCombo.setToolTipText(Messages.FilterViewer_Subfilter_ToolTip); + if (fNode.getField() != null) { + fFieldCombo.setText(fNode.getField()); + } + fFieldCombo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + fNode.setField(fFieldCombo.getText()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_ValueLabel); + + fValueText = new Text(this, SWT.BORDER); + fValueText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + if (node.getValue() != null && node.getValue().length() > 0) { + fValueText.setText(node.getValue()); + } else { + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fValueText.setText(Messages.FilterViewer_ValueHint); + } + fValueText.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (fNode.getValue() == null || fNode.getValue().length() == 0) { + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fValueText.setText(Messages.FilterViewer_ValueHint); + } + } + + @Override + public void focusGained(FocusEvent e) { + if (fValueText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fValueText.setText(""); //$NON-NLS-1$ + } + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); + } + }); + fValueText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + if (!fValueText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fNode.setValue(fValueText.getText()); + fViewer.refresh(fNode); + } + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + + fIgnoreCaseButton = new Button(this, SWT.CHECK); + fIgnoreCaseButton.setSelection(fNode.isIgnoreCase()); + fIgnoreCaseButton.setText(Messages.FilterViewer_IgnoreCaseButtonText); + fIgnoreCaseButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fIgnoreCaseButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setIgnoreCase(fIgnoreCaseButton.getSelection()); + fViewer.refresh(fNode); + } + }); + } + } + + private class FilterEqualsNodeComposite extends FilterBaseNodeComposite { + TmfFilterEqualsNode fNode; + Button fNotButton; + Combo fFieldCombo; + Text fValueText; + Button fIgnoreCaseButton; + + FilterEqualsNodeComposite(Composite parent, TmfFilterEqualsNode node) { + super(parent); + fNode = node; + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_NotLabel); + + fNotButton = new Button(this, SWT.CHECK); + fNotButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fNotButton.setSelection(fNode.isNot()); + fNotButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setNot(fNotButton.getSelection()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_FieldLabel); + + fFieldCombo = new Combo(this, SWT.DROP_DOWN); + fFieldCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + fFieldCombo.setItems(getFieldsList(fNode)); + fFieldCombo.setToolTipText(Messages.FilterViewer_Subfilter_ToolTip); + if (fNode.getField() != null) { + fFieldCombo.setText(fNode.getField()); + } + fFieldCombo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + fNode.setField(fFieldCombo.getText()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_ValueLabel); + + fValueText = new Text(this, SWT.BORDER); + fValueText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + if (node.getValue() != null && node.getValue().length() > 0) { + fValueText.setText(node.getValue()); + } else { + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fValueText.setText(Messages.FilterViewer_ValueHint); + } + fValueText.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (fNode.getValue() == null || fNode.getValue().length() == 0) { + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fValueText.setText(Messages.FilterViewer_ValueHint); + } + } + + @Override + public void focusGained(FocusEvent e) { + if (fValueText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fValueText.setText(""); //$NON-NLS-1$ + } + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); + } + }); + fValueText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + if (!fValueText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fNode.setValue(fValueText.getText()); + fViewer.refresh(fNode); + } + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + + fIgnoreCaseButton = new Button(this, SWT.CHECK); + fIgnoreCaseButton.setSelection(fNode.isIgnoreCase()); + fIgnoreCaseButton.setText(Messages.FilterViewer_IgnoreCaseButtonText); + fIgnoreCaseButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fIgnoreCaseButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setIgnoreCase(fIgnoreCaseButton.getSelection()); + fViewer.refresh(fNode); + } + }); + } + } + + private class FilterMatchesNodeComposite extends FilterBaseNodeComposite { + TmfFilterMatchesNode fNode; + Button fNotButton; + Combo fFieldCombo; + Text fRegexText; + + FilterMatchesNodeComposite(Composite parent, TmfFilterMatchesNode node) { + super(parent); + fNode = node; + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_NotLabel); + + fNotButton = new Button(this, SWT.CHECK); + fNotButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fNotButton.setSelection(fNode.isNot()); + fNotButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setNot(fNotButton.getSelection()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_FieldLabel); + + fFieldCombo = new Combo(this, SWT.DROP_DOWN); + fFieldCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + fFieldCombo.setItems(getFieldsList(fNode)); + fFieldCombo.setToolTipText(Messages.FilterViewer_Subfilter_ToolTip); + if (fNode.getField() != null) { + fFieldCombo.setText(fNode.getField()); + } + fFieldCombo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + fNode.setField(fFieldCombo.getText()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_RegexLabel); + + fRegexText = new Text(this, SWT.BORDER); + fRegexText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + if (node.getRegex() != null && node.getRegex().length() > 0) { + fRegexText.setText(node.getRegex()); + } else { + fRegexText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fRegexText.setText(Messages.FilterViewer_RegexHint); + } + fRegexText.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (fNode.getRegex() == null || fNode.getRegex().length() == 0) { + fRegexText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fRegexText.setText(Messages.FilterViewer_RegexHint); + } + } + + @Override + public void focusGained(FocusEvent e) { + if (fRegexText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fRegexText.setText(""); //$NON-NLS-1$ + } + fRegexText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); + } + }); + fRegexText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + if (!fRegexText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fNode.setRegex(fRegexText.getText()); + fViewer.refresh(fNode); + } + } + }); + } + } + + private class FilterCompareNodeComposite extends FilterBaseNodeComposite { + TmfFilterCompareNode fNode; + Button fNotButton; + Combo fFieldCombo; + Text fValueText; + Button fLTButton; + Button fEQButton; + Button fGTButton; + Button fNumButton; + Button fAlphaButton; + Button fTimestampButton; + + FilterCompareNodeComposite(Composite parent, TmfFilterCompareNode node) { + super(parent); + fNode = node; + + Label label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_NotLabel); + + fNotButton = new Button(this, SWT.CHECK); + fNotButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fNotButton.setSelection(fNode.isNot()); + fNotButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fNode.setNot(fNotButton.getSelection()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_FieldLabel); + + fFieldCombo = new Combo(this, SWT.DROP_DOWN); + fFieldCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + fFieldCombo.setItems(getFieldsList(fNode)); + fFieldCombo.setToolTipText(Messages.FilterViewer_Subfilter_ToolTip); + if (fNode.getField() != null) { + fFieldCombo.setText(fNode.getField()); + } + fFieldCombo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + fNode.setField(fFieldCombo.getText()); + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_ResultLabel); + + Composite resultGroup = new Composite(this, SWT.NONE); + GridLayout rggl = new GridLayout(3, true); + rggl.marginHeight = 0; + rggl.marginWidth = 0; + resultGroup.setLayout(rggl); + resultGroup.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + + fLTButton = new Button(resultGroup, SWT.RADIO); + fLTButton.setSelection(fNode.getResult() < 0); + fLTButton.setText("<"); //$NON-NLS-1$ + fLTButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fLTButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (fLTButton.getSelection()) { + fNode.setResult(-1); + } + fViewer.refresh(fNode); + } + }); + + fEQButton = new Button(resultGroup, SWT.RADIO); + fEQButton.setSelection(fNode.getResult() == 0); + fEQButton.setText("="); //$NON-NLS-1$ + fEQButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fEQButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (fEQButton.getSelection()) { + fNode.setResult(0); + } + fViewer.refresh(fNode); + } + }); + + fGTButton = new Button(resultGroup, SWT.RADIO); + fGTButton.setSelection(fNode.getResult() > 0); + fGTButton.setText(">"); //$NON-NLS-1$ + fGTButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fGTButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (fGTButton.getSelection()) { + fNode.setResult(1); + } + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_TypeLabel); + + Composite typeGroup = new Composite(this, SWT.NONE); + GridLayout tggl = new GridLayout(3, false); + tggl.marginHeight = 0; + tggl.marginWidth = 0; + typeGroup.setLayout(tggl); + typeGroup.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + + fNumButton = new Button(typeGroup, SWT.RADIO); + fNumButton.setSelection(fNode.getType() == Type.NUM); + fNumButton.setText(Messages.FilterViewer_NumButtonText); + fNumButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fNumButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (fNumButton.getSelection()) { + fNode.setType(Type.NUM); + } + fViewer.refresh(fNode); + } + }); + + fAlphaButton = new Button(typeGroup, SWT.RADIO); + fAlphaButton.setSelection(fNode.getType() == Type.ALPHA); + fAlphaButton.setText(Messages.FilterViewer_AlphaButtonText); + fAlphaButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fAlphaButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (fAlphaButton.getSelection()) { + fNode.setType(Type.ALPHA); + } + fViewer.refresh(fNode); + } + }); + + fTimestampButton = new Button(typeGroup, SWT.RADIO); + fTimestampButton.setSelection(fNode.getType() == Type.TIMESTAMP); + fTimestampButton.setText(Messages.FilterViewer_TimestampButtonText); + fTimestampButton.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + fTimestampButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (fTimestampButton.getSelection()) { + fNode.setType(Type.TIMESTAMP); + } + fViewer.refresh(fNode); + } + }); + + label = new Label(this, SWT.NONE); + label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + label.setText(Messages.FilterViewer_ValueLabel); + + fValueText = new Text(this, SWT.BORDER); + fValueText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + if (node.getValue() != null && node.getValue().length() > 0) { + fValueText.setText(node.getValue()); + } else { + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fValueText.setText(Messages.FilterViewer_ValueHint); + } + fValueText.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (fNode.getValue() == null || fNode.getValue().length() == 0) { + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY)); + fValueText.setText(Messages.FilterViewer_ValueHint); + } + } + + @Override + public void focusGained(FocusEvent e) { + if (fValueText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fValueText.setText(""); //$NON-NLS-1$ + } + fValueText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); + } + }); + fValueText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + if (!fValueText.getForeground().equals(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY))) { + fNode.setValue(fValueText.getText()); + fViewer.refresh(fNode); + } + } + }); + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/PasteHandler.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/PasteHandler.java new file mode 100644 index 0000000000..f34a986659 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/filter/PasteHandler.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2013 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.filter; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode; +import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * Handler for paste command in filter view + * @author Xavier Raynaud <xavier.raynaud@kalray.eu> + * @since 3.0 + */ +public class PasteHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + // Check if we are closing down + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + return null; + } + + // Get the selection + IWorkbenchPage page = window.getActivePage(); + IWorkbenchPart part = page.getActivePart(); + if (!(part instanceof FilterView)) { + return null; + } + FilterView v = (FilterView) part; + + ITmfFilterTreeNode objectToPaste = FilterEditUtils.getTransferredTreeNode(); + objectToPaste = objectToPaste.clone(); + ITmfFilterTreeNode sel = v.getSelection(); + if (sel == null || TmfFilterNode.NODE_NAME.equals(objectToPaste.getNodeName())) { + sel = v.getFilterRoot(); + } + + sel.addChild(objectToPaste); + v.refresh(); + v.setSelection(objectToPaste); + return null; + } + + @Override + public boolean isEnabled() { + // Check if we are closing down + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + return false; + } + + // Get the selection + IWorkbenchPage page = window.getActivePage(); + IWorkbenchPart part = page.getActivePart(); + if (!(part instanceof FilterView)) { + return false; + } + FilterView v = (FilterView) part; + ITmfFilterTreeNode sel = v.getSelection(); + if (sel == null) { + sel = v.getFilterRoot(); + } + ITmfFilterTreeNode objectToPaste = FilterEditUtils.getTransferredTreeNode(); + if (objectToPaste != null && + (sel.getValidChildren().contains(objectToPaste.getNodeName()) + || TmfFilterNode.NODE_NAME.equals(objectToPaste.getNodeName()))) { + return true; + } + return false; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/FullTraceHistogram.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/FullTraceHistogram.java new file mode 100644 index 0000000000..0d75887f7a --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/FullTraceHistogram.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Bernd Hufmann - Changed to updated histogram data model + * Patrick Tasse - Update for mouse wheel zoom + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; + +/** + * A histogram widget that displays the event distribution of a whole trace. + * <p> + * It also features a selected range window that can be dragged and zoomed. + * + * @version 1.1 + * @author Francois Chouinard + */ +public class FullTraceHistogram extends Histogram { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + private final HistogramZoom fZoom; + + private long fRangeStartTime = 0L; + private long fRangeDuration; + + // ------------------------------------------------------------------------ + // Construction + // ------------------------------------------------------------------------ + + /** + * Full Constructor + * + * @param view A reference to the parent histogram view + * @param parent A reference to the parent composite + */ + public FullTraceHistogram(HistogramView view, Composite parent) { + super(view, parent); + fZoom = new HistogramZoom(this, getStartTime(), getTimeLimit()); + } + + @Override + public void dispose() { + super.dispose(); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public void clear() { + fRangeStartTime = 0L; + fRangeDuration = 0L; + if (fZoom != null) { + fZoom.setFullRange(0L, 0L); + fZoom.setNewRange(0L, 0L); + } + super.clear(); + } + + /** + * Sets the time range of the full histogram. + * + * @param startTime A start time + * @param endTime A end time + */ + public void setFullRange(long startTime, long endTime) { + fZoom.setFullRange(startTime, endTime); + fZoom.setNewRange(fRangeStartTime, fRangeDuration); + } + + /** + * Sets the selected time range. + * + * @param startTime The histogram start time + * @param duration The histogram duration + */ + public void setTimeRange(long startTime, long duration) { + fRangeStartTime = startTime; + fRangeDuration = duration; + fZoom.setNewRange(fRangeStartTime, fRangeDuration); + fDataModel.complete(); + } + + // ------------------------------------------------------------------------ + // MouseListener + // ------------------------------------------------------------------------ + + private int fStartDelta; + private boolean fMouseMoved; + + @Override + public void mouseDown(MouseEvent event) { + if (fScaledData != null && fDragState == DRAG_NONE && fDataModel.getStartTime() < fDataModel.getEndTime()) { + if (event.button == 2 || (event.button == 1 && (event.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL)) { + fDragState = DRAG_RANGE; + fDragButton = event.button; + int center = (int) (((fRangeStartTime + fRangeDuration / 2) - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration); + fStartDelta = center - event.x; + fMouseMoved = false; + return; + } else if (event.button == 3) { + fDragState = DRAG_ZOOM; + fDragButton = event.button; + long time = Math.min(getTimestamp(event.x), getEndTime()); + if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) { + if (time < fRangeStartTime + fRangeDuration / 2) { + fRangeStartTime = fRangeStartTime + fRangeDuration; + } + } else { + fRangeStartTime = time; + } + fRangeDuration = time - fRangeStartTime; + fCanvas.redraw(); + return; + } + } + super.mouseDown(event); + } + + @Override + public void mouseUp(MouseEvent event) { + if (fDragState == DRAG_RANGE && event.button == fDragButton) { + fDragState = DRAG_NONE; + fDragButton = 0; + if (!fMouseMoved) { + // if single click without move, center on the click + long startTime = getTimestamp(event.x) - fRangeDuration / 2; + fRangeStartTime = Math.max(getStartTime(), Math.min(getEndTime() - fRangeDuration, startTime)); + } + ((HistogramView) fParentView).updateTimeRange(fRangeStartTime, fRangeStartTime + fRangeDuration); + return; + } else if (fDragState == DRAG_ZOOM && event.button == fDragButton) { + fDragState = DRAG_NONE; + fDragButton = 0; + if (fRangeDuration < 0) { + fRangeStartTime = fRangeStartTime + fRangeDuration; + fRangeDuration = -fRangeDuration; + } + if (fRangeDuration > 0) { + ((HistogramView) fParentView).updateTimeRange(fRangeStartTime, fRangeStartTime + fRangeDuration); + } else { + fRangeStartTime = fZoom.getStartTime(); + fRangeDuration = fZoom.getDuration(); + fCanvas.redraw(); + } + return; + } + super.mouseUp(event); + } + + // ------------------------------------------------------------------------ + // MouseMoveListener + // ------------------------------------------------------------------------ + + @Override + public void mouseMove(MouseEvent event) { + if (fDragState == DRAG_RANGE) { + int center = event.x + fStartDelta; + long newStart = getTimestamp(center) - fRangeDuration / 2; + fRangeStartTime = Math.max(getStartTime(), Math.min(getEndTime() - fRangeDuration, newStart)); + fCanvas.redraw(); + fMouseMoved = true; + return; + } else if (fDragState == DRAG_ZOOM) { + long endTime = Math.max(getStartTime(), Math.min(getEndTime(), getTimestamp(event.x))); + fRangeDuration = endTime - fRangeStartTime; + fCanvas.redraw(); + return; + } + super.mouseMove(event); + } + + // ------------------------------------------------------------------------ + // PaintListener + // ------------------------------------------------------------------------ + + @Override + public void paintControl(PaintEvent event) { + super.paintControl(event); + + Image image = (Image) fCanvas.getData(IMAGE_KEY); + assert image != null; + + Image rangeRectangleImage = new Image(image.getDevice(), image, SWT.IMAGE_COPY); + GC rangeWindowGC = new GC(rangeRectangleImage); + + if ((fScaledData != null) && (fRangeDuration != 0 || fDragState == DRAG_ZOOM)) { + drawTimeRangeWindow(rangeWindowGC, fRangeStartTime, fRangeDuration); + } + + // Draws the buffer image onto the canvas. + event.gc.drawImage(rangeRectangleImage, 0, 0); + + rangeWindowGC.dispose(); + rangeRectangleImage.dispose(); + } + + /** + * Get the histogram zoom + * @return the histogram zoom + * @since 2.0 + */ + public HistogramZoom getZoom() { + return fZoom; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/Histogram.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/Histogram.java new file mode 100644 index 0000000000..25a778d144 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/Histogram.java @@ -0,0 +1,1045 @@ +/******************************************************************************* + * Copyright (c) 2011, 2014 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Bernd Hufmann - Changed to updated histogram data model + * Francois Chouinard - Reformat histogram labels on format change + * Patrick Tasse - Support selection range + * Xavier Raynaud - Support multi-trace coloring + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.MouseWheelListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampDelta; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; + +/** + * Re-usable histogram widget. + * + * It has the following features: + * <ul> + * <li>Y-axis labels displaying min/max count values + * <li>X-axis labels displaying time range + * <li>a histogram displaying the distribution of values over time (note that + * the histogram might not necessarily fill the whole canvas) + * </ul> + * The widget also has 2 'markers' to identify: + * <ul> + * <li>a red dashed line over the bar that contains the currently selected event + * <li>a dark red dashed line that delimits the right end of the histogram (if + * it doesn't fill the canvas) + * </ul> + * Clicking on the histogram will select the current event at the mouse + * location. + * <p> + * Once the histogram is selected, there is some limited keyboard support: + * <ul> + * <li>Home: go to the first histogram bar + * <li>End: go to the last histogram bar + * <li>Left: go to the previous histogram + * <li>Right: go to the next histogram bar + * </ul> + * Finally, when the mouse hovers over the histogram, a tool tip showing the + * following information about the corresponding histogram bar time range: + * <ul> + * <li>start of the time range + * <li>end of the time range + * <li>number of events in that time range + * </ul> + * + * @version 1.1 + * @author Francois Chouinard + */ +public abstract class Histogram implements ControlListener, PaintListener, KeyListener, MouseListener, MouseMoveListener, MouseTrackListener, IHistogramModelListener { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + // Histogram colors + + // System colors, they do not need to be disposed + private final Color fBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE); + private final Color fSelectionForegroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE); + private final Color fSelectionBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + private final Color fLastEventColor = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED); + + // Application colors, they need to be disposed + private final Color[] fHistoBarColors = new Color[] {new Color(Display.getDefault(), 90, 90, 255), // blue + new Color(Display.getDefault(), 0, 240, 0), // green + new Color(Display.getDefault(), 255, 0, 0), // red + new Color(Display.getDefault(), 0, 255, 255), // cyan + new Color(Display.getDefault(), 255, 80, 255), // magenta + new Color(Display.getDefault(), 200, 200, 0), // yellow + new Color(Display.getDefault(), 200, 150, 0), // brown + new Color(Display.getDefault(), 150, 255, 150), // light green + new Color(Display.getDefault(), 200, 80, 80), // dark red + new Color(Display.getDefault(), 30, 150, 150), // dark cyan + new Color(Display.getDefault(), 200, 200, 255), // light blue + new Color(Display.getDefault(), 0, 120, 0), // dark green + new Color(Display.getDefault(), 255, 150, 150), // lighter red + new Color(Display.getDefault(), 140, 80, 140), // dark magenta + new Color(Display.getDefault(), 150, 100, 50), // brown + new Color(Display.getDefault(), 255, 80, 80), // light red + new Color(Display.getDefault(), 200, 200, 200), // light grey + new Color(Display.getDefault(), 255, 200, 80), // orange + new Color(Display.getDefault(), 255, 255, 80), // pale yellow + new Color(Display.getDefault(), 255, 200, 200), // pale red + new Color(Display.getDefault(), 255, 200, 255), // pale magenta + new Color(Display.getDefault(), 255, 255, 200), // pale pale yellow + new Color(Display.getDefault(), 200, 255, 255), // pale pale blue + }; + private final Color fTimeRangeColor = new Color(Display.getCurrent(), 255, 128, 0); + private final Color fLostEventColor = new Color(Display.getCurrent(), 208, 62, 120); + + // Drag states + /** + * No drag in progress + * @since 2.2 + */ + protected final int DRAG_NONE = 0; + /** + * Drag the selection + * @since 2.2 + */ + protected final int DRAG_SELECTION = 1; + /** + * Drag the time range + * @since 2.2 + */ + protected final int DRAG_RANGE = 2; + /** + * Drag the zoom range + * @since 2.2 + */ + protected final int DRAG_ZOOM = 3; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The parent TMF view. + */ + protected TmfView fParentView; + + private Composite fComposite; + private Font fFont; + + // Histogram text fields + private Label fMaxNbEventsLabel; + private Label fMinNbEventsLabel; + private Label fTimeRangeStartLabel; + private Label fTimeRangeEndLabel; + + /** + * Histogram drawing area + */ + protected Canvas fCanvas; + + /** + * The histogram data model. + */ + protected final HistogramDataModel fDataModel; + + /** + * The histogram data model scaled to current resolution and screen width. + */ + protected HistogramScaledData fScaledData; + + /** + * The current event value + */ + protected long fCurrentEventTime = 0L; + + /** + * The current selection begin time + */ + private long fSelectionBegin = 0L; + + /** + * The current selection end time + */ + private long fSelectionEnd = 0L; + + /** + * The drag state + * @see #DRAG_NONE + * @see #DRAG_SELECTION + * @see #DRAG_RANGE + * @see #DRAG_ZOOM + * @since 2.2 + */ + protected int fDragState = DRAG_NONE; + + /** + * The button that started a mouse drag, or 0 if no drag in progress + * @since 2.2 + */ + protected int fDragButton = 0; + + /** + * The bucket display offset + */ + private int fOffset = 0; + + /** + * show the traces or not + * @since 3.0 + */ + static boolean showTraces = true; + + // ------------------------------------------------------------------------ + // Construction + // ------------------------------------------------------------------------ + + /** + * Full constructor. + * + * @param view A reference to the parent TMF view. + * @param parent A parent composite + */ + public Histogram(final TmfView view, final Composite parent) { + fParentView = view; + + fComposite = createWidget(parent); + fDataModel = new HistogramDataModel(); + fDataModel.addHistogramListener(this); + clear(); + + fCanvas.addControlListener(this); + fCanvas.addPaintListener(this); + fCanvas.addKeyListener(this); + fCanvas.addMouseListener(this); + fCanvas.addMouseTrackListener(this); + fCanvas.addMouseMoveListener(this); + + TmfSignalManager.register(this); + } + + /** + * Dispose resources and unregisters listeners. + */ + public void dispose() { + TmfSignalManager.deregister(this); + fLostEventColor.dispose(); + for (Color c : fHistoBarColors) { + c.dispose(); + } + fTimeRangeColor.dispose(); + fFont.dispose(); + fDataModel.removeHistogramListener(this); + fDataModel.dispose(); + } + + private Composite createWidget(final Composite parent) { + + fFont = adjustFont(parent); + + final int initalWidth = 10; + + // -------------------------------------------------------------------- + // Define the histogram + // -------------------------------------------------------------------- + + final GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 3; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + gridLayout.marginTop = 0; + gridLayout.horizontalSpacing = 0; + gridLayout.verticalSpacing = 0; + gridLayout.marginLeft = 0; + gridLayout.marginRight = 0; + final Composite composite = new Composite(parent, SWT.FILL); + composite.setLayout(gridLayout); + + // Use all the horizontal space + GridData gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.verticalAlignment = SWT.FILL; + gridData.grabExcessHorizontalSpace = true; + gridData.grabExcessVerticalSpace = true; + composite.setLayoutData(gridData); + + // Y-axis max event + gridData = new GridData(); + gridData.horizontalAlignment = SWT.RIGHT; + gridData.verticalAlignment = SWT.TOP; + fMaxNbEventsLabel = new Label(composite, SWT.RIGHT); + fMaxNbEventsLabel.setFont(fFont); + fMaxNbEventsLabel.setText("0"); //$NON-NLS-1$ + fMaxNbEventsLabel.setLayoutData(gridData); + + // Histogram itself + Composite canvasComposite = new Composite(composite, SWT.BORDER); + gridData = new GridData(); + gridData.horizontalSpan = 2; + gridData.verticalSpan = 2; + gridData.horizontalAlignment = SWT.FILL; + gridData.verticalAlignment = SWT.FILL; + gridData.heightHint = 0; + gridData.widthHint = 0; + gridData.grabExcessHorizontalSpace = true; + gridData.grabExcessVerticalSpace = true; + canvasComposite.setLayoutData(gridData); + canvasComposite.setLayout(new FillLayout()); + fCanvas = new Canvas(canvasComposite, SWT.DOUBLE_BUFFERED); + fCanvas.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + Object image = fCanvas.getData(IMAGE_KEY); + if (image instanceof Image) { + ((Image) image).dispose(); + } + } + }); + + // Y-axis min event (always 0...) + gridData = new GridData(); + gridData.horizontalAlignment = SWT.RIGHT; + gridData.verticalAlignment = SWT.BOTTOM; + fMinNbEventsLabel = new Label(composite, SWT.RIGHT); + fMinNbEventsLabel.setFont(fFont); + fMinNbEventsLabel.setText("0"); //$NON-NLS-1$ + fMinNbEventsLabel.setLayoutData(gridData); + + // Dummy cell + gridData = new GridData(initalWidth, SWT.DEFAULT); + gridData.horizontalAlignment = SWT.RIGHT; + gridData.verticalAlignment = SWT.BOTTOM; + final Label dummyLabel = new Label(composite, SWT.NONE); + dummyLabel.setLayoutData(gridData); + + // Window range start time + gridData = new GridData(); + gridData.horizontalAlignment = SWT.LEFT; + gridData.verticalAlignment = SWT.BOTTOM; + fTimeRangeStartLabel = new Label(composite, SWT.NONE); + fTimeRangeStartLabel.setFont(fFont); + fTimeRangeStartLabel.setLayoutData(gridData); + + // Window range end time + gridData = new GridData(); + gridData.horizontalAlignment = SWT.RIGHT; + gridData.verticalAlignment = SWT.BOTTOM; + fTimeRangeEndLabel = new Label(composite, SWT.NONE); + fTimeRangeEndLabel.setFont(fFont); + fTimeRangeEndLabel.setLayoutData(gridData); + + return composite; + } + + private static Font adjustFont(final Composite composite) { + // Reduce font size for a more pleasing rendering + final int fontSizeAdjustment = -2; + final Font font = composite.getFont(); + final FontData fontData = font.getFontData()[0]; + return new Font(font.getDevice(), fontData.getName(), fontData.getHeight() + fontSizeAdjustment, fontData.getStyle()); + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns the start time (equal first bucket time) + * @return the start time. + */ + public long getStartTime() { + return fDataModel.getFirstBucketTime(); + } + + /** + * Returns the end time. + * @return the end time. + */ + public long getEndTime() { + return fDataModel.getEndTime(); + } + + /** + * Returns the time limit (end of last bucket) + * @return the time limit. + */ + public long getTimeLimit() { + return fDataModel.getTimeLimit(); + } + + /** + * Returns a data model reference. + * @return data model. + */ + public HistogramDataModel getDataModel() { + return fDataModel; + } + + /** + * Set the max number events to be displayed + * + * @param maxNbEvents + * the maximum number of events + */ + void setMaxNbEvents(long maxNbEvents) { + fMaxNbEventsLabel.setText(Long.toString(maxNbEvents)); + fMaxNbEventsLabel.getParent().layout(); + fCanvas.redraw(); + } + + /** + * Return <code>true</code> if the traces must be displayed in the histogram, + * <code>false</code> otherwise. + * @return whether the traces should be displayed + * @since 3.0 + */ + public boolean showTraces() { + return showTraces && fDataModel.getNbTraces() < getMaxNbTraces(); + } + + /** + * Returns the maximum number of traces the histogram can display with separate colors. + * If there is more traces, histogram will use only one color to display them. + * @return the maximum number of traces the histogram can display. + * @since 3.0 + */ + public int getMaxNbTraces() { + return fHistoBarColors.length; + } + + /** + * Returns the color used to display the trace at the given index. + * @param traceIndex a trace index + * @return a {@link Color} + * @since 3.0 + */ + public Color getTraceColor(int traceIndex) { + return fHistoBarColors[traceIndex % fHistoBarColors.length]; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + /** + * Updates the time range. + * @param startTime A start time + * @param endTime A end time. + */ + public void updateTimeRange(long startTime, long endTime) { + if (fDragState == DRAG_NONE) { + ((HistogramView) fParentView).updateTimeRange(startTime, endTime); + } + } + + /** + * Clear the histogram and reset the data + */ + public void clear() { + fDataModel.clear(); + if (fDragState == DRAG_SELECTION) { + updateSelectionTime(); + } + fDragState = DRAG_NONE; + fDragButton = 0; + synchronized (fDataModel) { + fScaledData = null; + } + } + + /** + * Sets the current selection time range and refresh the display + * + * @param beginTime The begin time of the current selection + * @param endTime The end time of the current selection + * @since 2.1 + */ + public void setSelection(final long beginTime, final long endTime) { + fSelectionBegin = (beginTime > 0) ? beginTime : 0; + fSelectionEnd = (endTime > 0) ? endTime : 0; + fDataModel.setSelectionNotifyListeners(beginTime, endTime); + } + + /** + * Computes the timestamp of the bucket at [offset] + * + * @param offset offset from the left on the histogram + * @return the start timestamp of the corresponding bucket + */ + public synchronized long getTimestamp(final int offset) { + assert offset > 0 && offset < fScaledData.fWidth; + try { + return fScaledData.fFirstBucketTime + fScaledData.fBucketDuration * offset; + } catch (final Exception e) { + return 0; // TODO: Fix that racing condition (NPE) + } + } + + /** + * Computes the offset of the timestamp in the histogram + * + * @param timestamp the timestamp + * @return the offset of the corresponding bucket (-1 if invalid) + */ + public synchronized int getOffset(final long timestamp) { + if (timestamp < fDataModel.getFirstBucketTime() || timestamp > fDataModel.getEndTime()) { + return -1; + } + return (int) ((timestamp - fDataModel.getFirstBucketTime()) / fScaledData.fBucketDuration); + } + + /** + * Set the bucket display offset + * + * @param offset + * the bucket display offset + * @since 2.2 + */ + protected void setOffset(final int offset) { + fOffset = offset; + } + + /** + * Move the currently selected bar cursor to a non-empty bucket. + * + * @param keyCode the SWT key code + */ + protected void moveCursor(final int keyCode) { + + int index; + switch (keyCode) { + + case SWT.HOME: + index = 0; + while (index < fScaledData.fLastBucket && fScaledData.fData[index].isEmpty()) { + index++; + } + if (index < fScaledData.fLastBucket) { + fScaledData.fSelectionBeginBucket = index; + } + break; + + case SWT.ARROW_RIGHT: + index = Math.max(0, fScaledData.fSelectionBeginBucket + 1); + while (index < fScaledData.fWidth && fScaledData.fData[index].isEmpty()) { + index++; + } + if (index < fScaledData.fLastBucket) { + fScaledData.fSelectionBeginBucket = index; + } + break; + + case SWT.END: + index = fScaledData.fLastBucket; + while (index >= 0 && fScaledData.fData[index].isEmpty()) { + index--; + } + if (index >= 0) { + fScaledData.fSelectionBeginBucket = index; + } + break; + + case SWT.ARROW_LEFT: + index = Math.min(fScaledData.fLastBucket - 1, fScaledData.fSelectionBeginBucket - 1); + while (index >= 0 && fScaledData.fData[index].isEmpty()) { + index--; + } + if (index >= 0) { + fScaledData.fSelectionBeginBucket = index; + } + break; + + default: + return; + } + + fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket; + fSelectionBegin = getTimestamp(fScaledData.fSelectionBeginBucket); + fSelectionEnd = fSelectionBegin; + updateSelectionTime(); + } + + /** + * Refresh the histogram display + */ + @Override + public void modelUpdated() { + if (!fCanvas.isDisposed() && fCanvas.getDisplay() != null) { + fCanvas.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + if (!fCanvas.isDisposed()) { + // Retrieve and normalize the data + final int canvasWidth = fCanvas.getBounds().width; + final int canvasHeight = fCanvas.getBounds().height; + if (canvasWidth <= 0 || canvasHeight <= 0) { + return; + } + fDataModel.setSelection(fSelectionBegin, fSelectionEnd); + fScaledData = fDataModel.scaleTo(canvasWidth, canvasHeight, 1); + synchronized (fDataModel) { + if (fScaledData != null) { + fCanvas.redraw(); + // Display histogram and update X-,Y-axis labels + updateRangeTextControls(); + long maxNbEvents = HistogramScaledData.hideLostEvents ? fScaledData.fMaxValue : fScaledData.fMaxCombinedValue; + fMaxNbEventsLabel.setText(Long.toString(maxNbEvents)); + // The Y-axis area might need to be re-sized + GridData gd = (GridData) fMaxNbEventsLabel.getLayoutData(); + gd.widthHint = Math.max(gd.widthHint, fMaxNbEventsLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).x); + fMaxNbEventsLabel.getParent().layout(); + } + } + } + } + }); + } + } + + /** + * Add a mouse wheel listener to the histogram + * @param listener the mouse wheel listener + * @since 2.0 + */ + public void addMouseWheelListener(MouseWheelListener listener) { + fCanvas.addMouseWheelListener(listener); + } + + /** + * Remove a mouse wheel listener from the histogram + * @param listener the mouse wheel listener + * @since 2.0 + */ + public void removeMouseWheelListener(MouseWheelListener listener) { + fCanvas.removeMouseWheelListener(listener); + } + + /** + * Add a key listener to the histogram + * @param listener the key listener + * @since 3.1 + */ + public void addKeyListener(KeyListener listener) { + fCanvas.addKeyListener(listener); + } + + /** + * Remove a key listener from the histogram + * @param listener the key listener + * @since 3.1 + */ + public void removeKeyListener(KeyListener listener) { + fCanvas.removeKeyListener(listener); + } + + // ------------------------------------------------------------------------ + // Helper functions + // ------------------------------------------------------------------------ + + private void updateSelectionTime() { + if (fSelectionBegin > fSelectionEnd) { + long end = fSelectionBegin; + fSelectionBegin = fSelectionEnd; + fSelectionEnd = end; + } + ((HistogramView) fParentView).updateSelectionTime(fSelectionBegin, fSelectionEnd); + } + + /** + * Update the range text controls + */ + private void updateRangeTextControls() { + if (fDataModel.getStartTime() < fDataModel.getEndTime()) { + fTimeRangeStartLabel.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getStartTime())); + fTimeRangeEndLabel.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getEndTime())); + } else { + fTimeRangeStartLabel.setText(""); //$NON-NLS-1$ + fTimeRangeEndLabel.setText(""); //$NON-NLS-1$ + } + } + + // ------------------------------------------------------------------------ + // PaintListener + // ------------------------------------------------------------------------ + /** + * Image key string for the canvas. + */ + protected final String IMAGE_KEY = "double-buffer-image"; //$NON-NLS-1$ + + @Override + public void paintControl(final PaintEvent event) { + + // Get the geometry + final int canvasWidth = fCanvas.getBounds().width; + final int canvasHeight = fCanvas.getBounds().height; + + // Make sure we have something to draw upon + if (canvasWidth <= 0 || canvasHeight <= 0) { + return; + } + + // Retrieve image; re-create only if necessary + Image image = (Image) fCanvas.getData(IMAGE_KEY); + if (image == null || image.getBounds().width != canvasWidth || image.getBounds().height != canvasHeight) { + if (image != null) { + image.dispose(); + } + image = new Image(event.display, canvasWidth, canvasHeight); + fCanvas.setData(IMAGE_KEY, image); + } + + // Draw the histogram on its canvas + final GC imageGC = new GC(image); + formatImage(imageGC, image); + event.gc.drawImage(image, 0, 0); + imageGC.dispose(); + } + + private void formatImage(final GC imageGC, final Image image) { + + if (fScaledData == null) { + return; + } + + final HistogramScaledData scaledData = new HistogramScaledData(fScaledData); + + try { + // Get drawing boundaries + final int width = image.getBounds().width; + final int height = image.getBounds().height; + + // Clear the drawing area + imageGC.setBackground(fBackgroundColor); + imageGC.fillRectangle(0, 0, image.getBounds().width + 1, image.getBounds().height + 1); + + // Draw the histogram bars + final int limit = width < scaledData.fWidth ? width : scaledData.fWidth; + double factor = HistogramScaledData.hideLostEvents ? scaledData.fScalingFactor : scaledData.fScalingFactorCombined; + final boolean showTracesColors = showTraces(); + for (int i = 0; i < limit; i++) { + HistogramBucket hb = scaledData.fData[i]; + int totalNbEvents = hb.getNbEvents(); + int value = (int) Math.ceil(totalNbEvents * factor); + int x = i + fOffset; + + // in Linux, the last pixel in a line is not drawn, + // so draw lost events first, one pixel too far + if (!HistogramScaledData.hideLostEvents) { + imageGC.setForeground(fLostEventColor); + final int lostEventValue = (int) Math.ceil(scaledData.fLostEventsData[i] * factor); + if (lostEventValue != 0) { + // drawing a line is inclusive, so we should remove 1 from y2 + // but we don't because Linux + imageGC.drawLine(x, height - value - lostEventValue, x, height - value); + } + } + + // then draw normal events second, to overwrite that extra pixel + if (!hb.isEmpty()) { + if (showTracesColors) { + for (int traceIndex = 0; traceIndex < hb.getNbTraces(); traceIndex++) { + int nbEventsForTrace = hb.getNbEvent(traceIndex); + if (nbEventsForTrace > 0) { + Color c = fHistoBarColors[traceIndex % fHistoBarColors.length]; + imageGC.setForeground(c); + imageGC.drawLine(x, height - value, x, height); + totalNbEvents -= nbEventsForTrace; + value = (int) Math.ceil(totalNbEvents * scaledData.fScalingFactor); + } + } + } else { + Color c = fHistoBarColors[0]; + imageGC.setForeground(c); + imageGC.drawLine(x, height - value, x, height); + } + } + } + + // Draw the selection bars + int alpha = imageGC.getAlpha(); + imageGC.setAlpha(100); + imageGC.setForeground(fSelectionForegroundColor); + imageGC.setBackground(fSelectionBackgroundColor); + final int beginBucket = scaledData.fSelectionBeginBucket + fOffset; + if (beginBucket >= 0 && beginBucket < limit) { + imageGC.drawLine(beginBucket, 0, beginBucket, height); + } + final int endBucket = scaledData.fSelectionEndBucket + fOffset; + if (endBucket >= 0 && endBucket < limit && endBucket != beginBucket) { + imageGC.drawLine(endBucket, 0, endBucket, height); + } + if (Math.abs(endBucket - beginBucket) > 1) { + if (endBucket > beginBucket) { + imageGC.fillRectangle(beginBucket + 1, 0, endBucket - beginBucket - 1, height); + } else { + imageGC.fillRectangle(endBucket + 1, 0, beginBucket - endBucket - 1, height); + } + } + imageGC.setAlpha(alpha); + + // Add a dashed line as a delimiter + int delimiterIndex = (int) ((getDataModel().getEndTime() - scaledData.getFirstBucketTime()) / scaledData.fBucketDuration) + 1; + drawDelimiter(imageGC, fLastEventColor, height, delimiterIndex); + + // Fill the area to the right of delimiter with background color + imageGC.setBackground(fComposite.getBackground()); + imageGC.fillRectangle(delimiterIndex + 1, 0, width - (delimiterIndex + 1), height); + + } catch (final Exception e) { + // Do nothing + } + } + + private static void drawDelimiter(final GC imageGC, final Color color, + final int height, final int index) { + imageGC.setBackground(color); + final int dash = height / 4; + imageGC.fillRectangle(index, 0 * dash, 1, dash - 1); + imageGC.fillRectangle(index, 1 * dash, 1, dash - 1); + imageGC.fillRectangle(index, 2 * dash, 1, dash - 1); + imageGC.fillRectangle(index, 3 * dash, 1, height - 3 * dash); + } + + /** + * Draw a time range window + * + * @param imageGC + * the GC + * @param rangeStartTime + * the range start time + * @param rangeDuration + * the range duration + * @since 2.2 + */ + protected void drawTimeRangeWindow(GC imageGC, long rangeStartTime, long rangeDuration) { + + if (fScaledData == null) { + return; + } + + // Map times to histogram coordinates + long bucketSpan = Math.max(fScaledData.fBucketDuration, 1); + long startTime = Math.min(rangeStartTime, rangeStartTime + rangeDuration); + int rangeWidth = (int) (Math.abs(rangeDuration) / bucketSpan); + + int left = (int) ((startTime - fDataModel.getFirstBucketTime()) / bucketSpan); + int right = left + rangeWidth; + int center = (left + right) / 2; + int height = fCanvas.getSize().y; + int arc = Math.min(15, rangeWidth); + + // Draw the selection window + imageGC.setForeground(fTimeRangeColor); + imageGC.setLineWidth(1); + imageGC.setLineStyle(SWT.LINE_SOLID); + imageGC.drawRoundRectangle(left, 0, rangeWidth, height - 1, arc, arc); + + // Fill the selection window + imageGC.setBackground(fTimeRangeColor); + imageGC.setAlpha(35); + imageGC.fillRoundRectangle(left + 1, 1, rangeWidth - 1, height - 2, arc, arc); + imageGC.setAlpha(255); + + // Draw the cross hair + imageGC.setForeground(fTimeRangeColor); + imageGC.setLineWidth(1); + imageGC.setLineStyle(SWT.LINE_SOLID); + + int chHalfWidth = ((rangeWidth < 60) ? (rangeWidth * 2) / 3 : 40) / 2; + imageGC.drawLine(center - chHalfWidth, height / 2, center + chHalfWidth, height / 2); + imageGC.drawLine(center, (height / 2) - chHalfWidth, center, (height / 2) + chHalfWidth); + } + + // ------------------------------------------------------------------------ + // KeyListener + // ------------------------------------------------------------------------ + + @Override + public void keyPressed(final KeyEvent event) { + moveCursor(event.keyCode); + } + + @Override + public void keyReleased(final KeyEvent event) { + } + + // ------------------------------------------------------------------------ + // MouseListener + // ------------------------------------------------------------------------ + + @Override + public void mouseDoubleClick(final MouseEvent event) { + } + + @Override + public void mouseDown(final MouseEvent event) { + if (fScaledData != null && event.button == 1 && fDragState == DRAG_NONE && fDataModel.getStartTime() < fDataModel.getEndTime()) { + fDragState = DRAG_SELECTION; + fDragButton = event.button; + if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) { + if (Math.abs(event.x - fScaledData.fSelectionBeginBucket) < Math.abs(event.x - fScaledData.fSelectionEndBucket)) { + fScaledData.fSelectionBeginBucket = fScaledData.fSelectionEndBucket; + fSelectionBegin = fSelectionEnd; + } + fSelectionEnd = Math.min(getTimestamp(event.x), getEndTime()); + fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration); + } else { + fSelectionBegin = Math.min(getTimestamp(event.x), getEndTime()); + fScaledData.fSelectionBeginBucket = (int) ((fSelectionBegin - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration); + fSelectionEnd = fSelectionBegin; + fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket; + } + fCanvas.redraw(); + } + } + + @Override + public void mouseUp(final MouseEvent event) { + if (fDragState == DRAG_SELECTION && event.button == fDragButton) { + fDragState = DRAG_NONE; + fDragButton = 0; + updateSelectionTime(); + } + } + + // ------------------------------------------------------------------------ + // MouseMoveListener + // ------------------------------------------------------------------------ + + /** + * @since 2.2 + */ + @Override + public void mouseMove(MouseEvent event) { + if (fDragState == DRAG_SELECTION && fDataModel.getStartTime() < fDataModel.getEndTime()) { + fSelectionEnd = Math.max(getStartTime(), Math.min(getEndTime(), getTimestamp(event.x))); + fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration); + fCanvas.redraw(); + } + } + + // ------------------------------------------------------------------------ + // MouseTrackListener + // ------------------------------------------------------------------------ + + @Override + public void mouseEnter(final MouseEvent event) { + } + + @Override + public void mouseExit(final MouseEvent event) { + } + + @Override + public void mouseHover(final MouseEvent event) { + if (fDataModel.getStartTime() < fDataModel.getEndTime() && fScaledData != null) { + int delimiterIndex = (int) ((fDataModel.getEndTime() - fScaledData.getFirstBucketTime()) / fScaledData.fBucketDuration) + 1; + if (event.x < delimiterIndex) { + final String tooltip = formatToolTipLabel(event.x - fOffset); + fCanvas.setToolTipText(tooltip); + return; + } + } + fCanvas.setToolTipText(null); + } + + private String formatToolTipLabel(final int index) { + long startTime = fScaledData.getBucketStartTime(index); + // negative values are possible if time values came into the model in decreasing order + if (startTime < 0) { + startTime = 0; + } + final long endTime = fScaledData.getBucketEndTime(index); + final int nbEvents = (index >= 0) ? fScaledData.fData[index].getNbEvents() : 0; + final String newLine = System.getProperty("line.separator"); //$NON-NLS-1$ + final StringBuffer buffer = new StringBuffer(); + int selectionBeginBucket = Math.min(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket); + int selectionEndBucket = Math.max(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket); + if (selectionBeginBucket <= index && index <= selectionEndBucket && fSelectionBegin != fSelectionEnd) { + TmfTimestampDelta delta = new TmfTimestampDelta(Math.abs(fSelectionEnd - fSelectionBegin), ITmfTimestamp.NANOSECOND_SCALE); + buffer.append(NLS.bind(Messages.Histogram_selectionSpanToolTip, delta.toString())); + buffer.append(newLine); + } + buffer.append(NLS.bind(Messages.Histogram_bucketRangeToolTip, + new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE).toString(), + new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE).toString())); + buffer.append(newLine); + buffer.append(NLS.bind(Messages.Histogram_eventCountToolTip, nbEvents)); + if (!HistogramScaledData.hideLostEvents) { + final int nbLostEvents = (index >= 0) ? fScaledData.fLostEventsData[index] : 0; + buffer.append(newLine); + buffer.append(NLS.bind(Messages.Histogram_lostEventCountToolTip, nbLostEvents)); + } + return buffer.toString(); + } + + // ------------------------------------------------------------------------ + // ControlListener + // ------------------------------------------------------------------------ + + @Override + public void controlMoved(final ControlEvent event) { + fDataModel.complete(); + } + + @Override + public void controlResized(final ControlEvent event) { + fDataModel.complete(); + } + + // ------------------------------------------------------------------------ + // Signal Handlers + // ------------------------------------------------------------------------ + + /** + * Format the timestamp and update the display + * + * @param signal + * the incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) { + updateRangeTextControls(); + + fComposite.layout(); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramBucket.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramBucket.java new file mode 100644 index 0000000000..a6ae1625d5 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramBucket.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2014 Kalray + * + * 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: + * Xavier Raynaud - Initial API and implementation + * Patrick Tasse - Fix concurrency issue + *******************************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import java.util.Arrays; + +/** + * This class counts events for a particular time range, taking into account origin of the event. + * @author Xavier Raynaud + * @since 3.0 + */ +public class HistogramBucket { + + private int fNbEvents = 0; + private int fEvents[]; + + /** + * Constructor + * @param traceCount number of traces of the experiment. + */ + public HistogramBucket(int traceCount) { + fEvents = new int[traceCount]; + } + + /** + * Constructor + * @param values list of values + */ + public HistogramBucket(int... values) { + fEvents = values; + for (int i : fEvents) { + fNbEvents += i; + } + } + + /** + * Copy Constructor + * @param b a HistogramBucket to copy + */ + public HistogramBucket(HistogramBucket b) { + add(b); + } + + /** + * Merge Constructor + * @param b1 a HistogramBucket + * @param b2 another HistogramBucket + */ + public HistogramBucket(HistogramBucket b1, HistogramBucket b2) { + add(b1); + add(b2); + } + + /** + * @return the number of events in this bucket + */ + public int getNbEvents() { + return fNbEvents; + } + + /** + * Add an event in this bucket + * @param traceIndex a trace index - see {@link HistogramDataModel#setTrace}. + */ + public synchronized void addEvent(int traceIndex) { + ensureCapacity(traceIndex + 1); + fEvents[traceIndex]++; + fNbEvents++; + } + + private void ensureCapacity(int len) { + if (fEvents == null) { + fEvents = new int[len]; + } else if (fEvents.length < len) { + int[] oldArray = fEvents; + fEvents = new int[len]; + System.arraycopy(oldArray, 0, fEvents, 0, oldArray.length); + } + } + + /** + * Gets the number of event in this bucket belonging to given trace + * @param traceIndex a trace index + * @return the number of events in this bucket belonging to the given trace + */ + public int getNbEvent(int traceIndex) { + if (fEvents == null || fEvents.length<= traceIndex) { + return 0; + } + return fEvents[traceIndex]; + } + + /** + * @return the number of traces in this bucket + */ + public int getNbTraces() { + if (fEvents == null) { + return 0; + } + return fEvents.length; + } + + /** + * Merge the given bucket in this one. + * @param histogramBucket a bucket to merge in this one. + */ + public synchronized void add(HistogramBucket histogramBucket) { + if (histogramBucket != null && histogramBucket.fNbEvents != 0) { + int len = histogramBucket.fEvents.length; + ensureCapacity(len); + for (int i = 0; i < len; i++) { + int nbEvents = histogramBucket.fEvents[i]; + fEvents[i] += nbEvents; + fNbEvents += nbEvents; + } + } + } + + /** + * @return <code>true</code> if this bucket contains no event, <code>false</code> otherwise. + */ + public boolean isEmpty() { + return fNbEvents == 0; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(fEvents); + result = prime * result + fNbEvents; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + HistogramBucket other = (HistogramBucket) obj; + if (fNbEvents != other.fNbEvents) { + return false; + } + if (fNbEvents != 0 && !Arrays.equals(fEvents, other.fEvents)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(fNbEvents); + sb.append(": "); //$NON-NLS-1$ + sb.append(Arrays.toString(fEvents)); + return sb.toString(); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramCurrentTimeControl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramCurrentTimeControl.java new file mode 100644 index 0000000000..ed0978ed01 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramCurrentTimeControl.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Francois Chouinard - Moved from LTTng to TMF + * Francois Chouinard - Simplified constructor, handle interval format change + * Patrick Tasse - Update value handling + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import java.text.ParseException; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; + +/** + * This control provides a group containing a text control. + * + * @version 1.1 + * @author Francois Chouinard + */ +public class HistogramCurrentTimeControl extends HistogramTextControl { + + // ------------------------------------------------------------------------ + // Construction + // ------------------------------------------------------------------------ + + /** + * Standard constructor + * + * @param parentView A parent histogram view + * @param parent A parent composite to draw in + * @param label A label + * @param value A value + * @since 2.0 + */ + public HistogramCurrentTimeControl(HistogramView parentView, Composite parent, + String label, long value) + { + super(parentView, parent, label, value); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + protected void updateValue() { + if (getValue() == Long.MIN_VALUE) { + fTextValue.setText(""); //$NON-NLS-1$ + return; + } + String string = fTextValue.getText(); + long value = getValue(); + try { + value = TmfTimestampFormat.getDefaulTimeFormat().parseValue(string, getValue()); + } catch (ParseException e) { + } + if (getValue() != value) { + // Make sure that the new time is within range + ITmfTrace trace = fParentView.getTrace(); + if (trace != null) { + TmfTimeRange range = trace.getTimeRange(); + long startTime = range.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long endTime = range.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + if (value < startTime) { + value = startTime; + } else if (value > endTime) { + value = endTime; + } + } + + // Set and propagate + setValue(value); + updateSelectionTime(value); + } else { + setValue(value); + } + } + + /** + * Update the selection time + * + * @param time + * the new selected time + * @since 2.2 + */ + protected void updateSelectionTime(long time) { + fParentView.updateSelectionTime(time, time); + } + + @Override + public void setValue(long time) { + if (time != Long.MIN_VALUE) { + super.setValue(time, new TmfTimestamp(time, ITmfTimestamp.NANOSECOND_SCALE).toString()); + } else { + super.setValue(time, ""); //$NON-NLS-1$ + } + } + + // ------------------------------------------------------------------------ + // Signal Handlers + // ------------------------------------------------------------------------ + + /** + * Format the timestamp and update the display. Compute the new text size, + * adjust the text and group widgets and then refresh the view layout. + * + * @param signal the incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) { + setValue(getValue()); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramDataModel.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramDataModel.java new file mode 100644 index 0000000000..cc723a7626 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramDataModel.java @@ -0,0 +1,717 @@ +/******************************************************************************* + * Copyright (c) 2011, 2014 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Bernd Hufmann - Implementation of new interfaces/listeners and support for + * time stamp in any order + * Francois Chouinard - Moved from LTTng to TMF + * Francois Chouinard - Added support for empty initial buckets + * Patrick Tasse - Support selection range + * Jean-Christian Kouamé, Simon Delisle - Added support to manage lost events + * Xavier Raynaud - Support multi-trace coloring + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; + +/** + * Histogram-independent data model. + * + * It has the following characteristics: + * <ul> + * <li>The <i>basetime</i> is the timestamp of the first event + * <li>There is a fixed number (<i>n</i>) of buckets of uniform duration + * (<i>d</i>) + * <li>The <i>timespan</i> of the model is thus: <i>n</i> * <i>d</i> time units + * <li>Bucket <i>i</i> holds the number of events that occurred in time range: + * [<i>basetime</i> + <i>i</i> * <i>d</i>, <i>basetime</i> + (<i>i</i> + 1) * + * <i>d</i>) + * </ul> + * Initially, the bucket durations is set to 1ns. As the events are read, they + * are tallied (using <i>countEvent()</i>) in the appropriate bucket (relative + * to the <i>basetime</i>). + * <p> + * Eventually, an event will have a timestamp that exceeds the <i>timespan</i> + * high end (determined by <i>n</i>, the number of buckets, and <i>d</i>, the + * bucket duration). At this point, the histogram needs to be compacted. This is + * done by simply merging adjacent buckets by pair, in effect doubling the + * <i>timespan</i> (<i>timespan'</i> = <i>n</i> * <i>d'</i>, where <i>d'</i> = + * 2<i>d</i>). This compaction happens as needed as the trace is read. + * <p> + * The model allows for timestamps in not increasing order. The timestamps can + * be fed to the model in any order. If an event has a timestamp less than the + * <i>basetime</i>, the buckets will be moved to the right to account for the + * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket + * duration smaller then the previous <i>basetime</i>. Note that the + * <i>basetime</i> might no longer be the timestamp of an event. If necessary, + * the buckets will be compacted before moving to the right. This might be + * necessary to not lose any event counts at the end of the buckets array. + * <p> + * The mapping from the model to the UI is performed by the <i>scaleTo()</i> + * method. By keeping the number of buckets <i>n</i> relatively large with + * respect to to the number of pixels in the actual histogram, we should achieve + * a nice result when visualizing the histogram. + * <p> + * + * @version 2.0 + * @author Francois Chouinard + */ +public class HistogramDataModel implements IHistogramDataModel { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * The default number of buckets + */ + public static final int DEFAULT_NUMBER_OF_BUCKETS = 16 * 1000; + + /** + * Number of events after which listeners will be notified. + */ + public static final int REFRESH_FREQUENCY = DEFAULT_NUMBER_OF_BUCKETS; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + // Trace management + private ITmfTrace fTrace = null; + private final Map<ITmfTrace, Integer> fTraceMap = new LinkedHashMap<>(); + + // Bucket management + private final int fNbBuckets; + private final HistogramBucket[] fBuckets; + private final long[] fLostEventsBuckets; + private long fBucketDuration; + private long fNbEvents; + private int fLastBucket; + + // Timestamps + private long fFirstBucketTime; // could be negative when analyzing events with descending order!!! + private long fFirstEventTime; + private long fEndTime; + private long fSelectionBegin; + private long fSelectionEnd; + private long fTimeLimit; + + // Private listener lists + private final ListenerList fModelListeners; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor with default number of buckets. + */ + public HistogramDataModel() { + this(0, DEFAULT_NUMBER_OF_BUCKETS); + } + + /** + * Default constructor with default number of buckets. + * + * @param startTime + * The histogram start time + * @since 2.0 + */ + public HistogramDataModel(long startTime) { + this(startTime, DEFAULT_NUMBER_OF_BUCKETS); + } + + /** + * Constructor with non-default number of buckets. + * + * @param nbBuckets + * A number of buckets. + */ + public HistogramDataModel(int nbBuckets) { + this(0, nbBuckets); + } + + /** + * Constructor with non-default number of buckets. + * + * @param startTime + * the histogram start time + * @param nbBuckets + * A number of buckets. + * @since 2.0 + */ + public HistogramDataModel(long startTime, int nbBuckets) { + fFirstBucketTime = fFirstEventTime = fEndTime = startTime; + fNbBuckets = nbBuckets; + fBuckets = new HistogramBucket[nbBuckets]; + fLostEventsBuckets = new long[nbBuckets]; + fModelListeners = new ListenerList(); + clear(); + } + + /** + * Copy constructor. + * + * @param other + * A model to copy. + */ + public HistogramDataModel(HistogramDataModel other) { + fNbBuckets = other.fNbBuckets; + fBuckets = new HistogramBucket[fNbBuckets]; + for (int i = 0; i < fNbBuckets; i++) { + fBuckets[i] = new HistogramBucket(other.fBuckets[i]); + } + fLostEventsBuckets = Arrays.copyOf(other.fLostEventsBuckets, fNbBuckets); + fBucketDuration = Math.max(other.fBucketDuration, 1); + fNbEvents = other.fNbEvents; + fLastBucket = other.fLastBucket; + fFirstBucketTime = other.fFirstBucketTime; + fFirstEventTime = other.fFirstEventTime; + fEndTime = other.fEndTime; + fSelectionBegin = other.fSelectionBegin; + fSelectionEnd = other.fSelectionEnd; + fTimeLimit = other.fTimeLimit; + fModelListeners = new ListenerList(); + Object[] listeners = other.fModelListeners.getListeners(); + for (Object listener : listeners) { + fModelListeners.add(listener); + } + } + + + /** + * Disposes the data model + * @since 3.0 + */ + public void dispose() { + fTraceMap.clear(); + fTrace = null; + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns the number of events in the data model. + * + * @return number of events. + */ + public long getNbEvents() { + return fNbEvents; + } + + /** + * Returns the number of buckets in the model. + * + * @return number of buckets. + */ + public int getNbBuckets() { + return fNbBuckets; + } + + /** + * Returns the current bucket duration. + * + * @return bucket duration + */ + public long getBucketDuration() { + return fBucketDuration; + } + + /** + * Returns the time value of the first bucket in the model. + * + * @return time of first bucket. + */ + public long getFirstBucketTime() { + return fFirstBucketTime; + } + + /** + * Returns the time of the first event in the model. + * + * @return time of first event. + */ + public long getStartTime() { + return fFirstEventTime; + } + + /** + * Sets the trace of this model. + * @param trace - a {@link ITmfTrace} + * @since 3.0 + */ + public void setTrace(ITmfTrace trace) { + this.fTrace = trace; + fTraceMap.clear(); + ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace); + if (traces != null) { + int i = 0; + for (ITmfTrace tr : traces) { + fTraceMap.put(tr, i); + i++; + } + } + } + + /** + * Gets the trace of this model. + * @return a {@link ITmfTrace} + * @since 3.0 + */ + public ITmfTrace getTrace() { + return this.fTrace; + } + + /** + * Gets the traces names of this model. + * @return an array of trace names + * @since 3.0 + */ + public String[] getTraceNames() { + ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace); + if (traces == null) { + return new String[0]; + } + String[] traceNames = new String[traces.length]; + int i = 0; + for (ITmfTrace tr : traces) { + traceNames[i] = tr.getName(); + i++; + } + return traceNames; + } + + /** + * Gets the number of traces of this model. + * @return the number of traces of this model. + * @since 3.0 + */ + public int getNbTraces() { + ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace); + if (traces == null) { + return 1; // + } + return traces.length; + } + + /** + * Sets the model start time + * + * @param startTime + * the histogram range start time + * @param endTime + * the histogram range end time + * @since 2.0 + */ + public void setTimeRange(long startTime, long endTime) { + fFirstBucketTime = fFirstEventTime = fEndTime = startTime; + fBucketDuration = 1; + updateEndTime(); + while (endTime >= fTimeLimit) { + mergeBuckets(); + } + } + + /** + * Set the end time. Setting this ensures that the corresponding bucket is + * displayed regardless of the event counts. + * + * @param endTime + * the time of the last used bucket + * @since 2.2 + */ + public void setEndTime(long endTime) { + fEndTime = endTime; + fLastBucket = (int) ((endTime - fFirstBucketTime) / fBucketDuration); + } + + /** + * Returns the end time. + * + * @return the time of the last used bucket + */ + public long getEndTime() { + return fEndTime; + } + + /** + * Returns the begin time of the current selection in the model. + * + * @return the begin time of the current selection. + * @since 2.1 + */ + public long getSelectionBegin() { + return fSelectionBegin; + } + + /** + * Returns the end time of the current selection in the model. + * + * @return the end time of the current selection. + * @since 2.1 + */ + public long getSelectionEnd() { + return fSelectionEnd; + } + + /** + * Returns the time limit with is: start time + nbBuckets * bucketDuration + * + * @return the time limit. + */ + public long getTimeLimit() { + return fTimeLimit; + } + + // ------------------------------------------------------------------------ + // Listener handling + // ------------------------------------------------------------------------ + + /** + * Add a listener to the model to be informed about model changes. + * + * @param listener + * A listener to add. + */ + public void addHistogramListener(IHistogramModelListener listener) { + fModelListeners.add(listener); + } + + /** + * Remove a given model listener. + * + * @param listener + * A listener to remove. + */ + public void removeHistogramListener(IHistogramModelListener listener) { + fModelListeners.remove(listener); + } + + // Notify listeners (always) + private void fireModelUpdateNotification() { + fireModelUpdateNotification(0); + } + + // Notify listener on boundary + private void fireModelUpdateNotification(long count) { + if ((count % REFRESH_FREQUENCY) == 0) { + Object[] listeners = fModelListeners.getListeners(); + for (Object listener2 : listeners) { + IHistogramModelListener listener = (IHistogramModelListener) listener2; + listener.modelUpdated(); + } + } + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public void complete() { + fireModelUpdateNotification(); + } + + /** + * Clear the histogram model. + * + * @see org.eclipse.tracecompass.tmf.ui.views.distribution.model.IBaseDistributionModel#clear() + */ + @Override + public void clear() { + Arrays.fill(fBuckets, null); + Arrays.fill(fLostEventsBuckets, 0); + fNbEvents = 0; + fFirstBucketTime = 0; + fEndTime = 0; + fSelectionBegin = 0; + fSelectionEnd = 0; + fLastBucket = 0; + fBucketDuration = 1; + updateEndTime(); + fireModelUpdateNotification(); + } + + /** + * Sets the current selection time range (no notification of listeners) + * + * @param beginTime + * The selection begin time. + * @param endTime + * The selection end time. + * @since 2.1 + */ + public void setSelection(long beginTime, long endTime) { + fSelectionBegin = beginTime; + fSelectionEnd = endTime; + } + + /** + * Sets the current selection time range with notification of listeners + * + * @param beginTime + * The selection begin time. + * @param endTime + * The selection end time. + * @since 2.1 + */ + public void setSelectionNotifyListeners(long beginTime, long endTime) { + fSelectionBegin = beginTime; + fSelectionEnd = endTime; + fireModelUpdateNotification(); + } + + /** + * Add event to the correct bucket, compacting the if needed. + * + * @param eventCount + * The current event Count (for notification purposes) + * @param timestamp + * The timestamp of the event to count + * @param trace + * The event trace + * @since 3.0 + */ + @Override + public void countEvent(long eventCount, long timestamp, ITmfTrace trace) { + + // Validate + if (timestamp < 0) { + return; + } + + // Set the start/end time if not already done + if ((fFirstBucketTime == 0) && (fLastBucket == 0) && (fBuckets[0] == null) && (timestamp > 0)) { + fFirstBucketTime = timestamp; + fFirstEventTime = timestamp; + updateEndTime(); + } + + if (timestamp < fFirstEventTime) { + fFirstEventTime = timestamp; + } + + if (fEndTime < timestamp) { + fEndTime = timestamp; + } + + if (timestamp >= fFirstBucketTime) { + + // Compact as needed + while (timestamp >= fTimeLimit) { + mergeBuckets(); + } + + } else { + + // get offset for adjustment + int offset = getOffset(timestamp); + + // Compact as needed + while ((fLastBucket + offset) >= fNbBuckets) { + mergeBuckets(); + offset = getOffset(timestamp); + } + + moveBuckets(offset); + + fLastBucket = fLastBucket + offset; + + fFirstBucketTime = fFirstBucketTime - (offset * fBucketDuration); + updateEndTime(); + } + + // Increment the right bucket + int index = (int) ((timestamp - fFirstBucketTime) / fBucketDuration); + if (fBuckets[index] == null) { + fBuckets[index] = new HistogramBucket(getNbTraces()); + } + Integer traceIndex = fTraceMap.get(trace); + if (traceIndex == null) { + traceIndex = 0; + } + fBuckets[index].addEvent(traceIndex); + fNbEvents++; + if (fLastBucket < index) { + fLastBucket = index; + } + + fireModelUpdateNotification(eventCount); + } + + /** + * Add lost event to the correct bucket, compacting the if needed. + * + * @param timeRange + * time range of a lost event + * @param nbLostEvents + * the number of lost events + * @param fullRange + * Full range or time range for histogram request + * @since 2.2 + */ + public void countLostEvent(TmfTimeRange timeRange, long nbLostEvents, boolean fullRange) { + + // Validate + if (timeRange.getStartTime().getValue() < 0 || timeRange.getEndTime().getValue() < 0) { + return; + } + + // Compact as needed + if (fullRange) { + while (timeRange.getEndTime().getValue() >= fTimeLimit) { + mergeBuckets(); + } + } + + int indexStart = (int) ((timeRange.getStartTime().getValue() - fFirstBucketTime) / fBucketDuration); + int indexEnd = (int) ((timeRange.getEndTime().getValue() - fFirstBucketTime) / fBucketDuration); + int nbBucketRange = (indexEnd - indexStart) + 1; + + int lostEventPerBucket = (int) Math.ceil((double) nbLostEvents / nbBucketRange); + long lastLostCol = Math.max(1, nbLostEvents - lostEventPerBucket * (nbBucketRange - 1)); + + // Increment the right bucket, bear in mind that ranges make it almost certain that some lost events are out of range + for (int index = indexStart; index <= indexEnd && index < fLostEventsBuckets.length; index++) { + if (index == (indexStart + nbBucketRange - 1)) { + fLostEventsBuckets[index] += lastLostCol; + } else { + fLostEventsBuckets[index] += lostEventPerBucket; + } + } + + fNbEvents++; + + fireModelUpdateNotification(nbLostEvents); + } + + /** + * Scale the model data to the width, height and bar width requested. + * + * @param width + * A width of the histogram canvas + * @param height + * A height of the histogram canvas + * @param barWidth + * A width (in pixel) of a histogram bar + * @return the result array of size [width] and where the highest value + * doesn't exceed [height] + * + * @see org.eclipse.tracecompass.tmf.ui.views.histogram.IHistogramDataModel#scaleTo(int, + * int, int) + */ + @Override + public HistogramScaledData scaleTo(int width, int height, int barWidth) { + // Basic validation + if ((width <= 0) || (height <= 0) || (barWidth <= 0)) + { + throw new AssertionError("Invalid histogram dimensions (" + width + "x" + height + ", barWidth=" + barWidth + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + // The result structure + HistogramScaledData result = new HistogramScaledData(width, height, barWidth); + + // Scale horizontally + result.fMaxValue = 0; + + int nbBars = width / barWidth; + int bucketsPerBar = (fLastBucket / nbBars) + 1; + result.fBucketDuration = Math.max(bucketsPerBar * fBucketDuration, 1); + for (int i = 0; i < nbBars; i++) { + int count = 0; + int countLostEvent = 0; + result.fData[i] = new HistogramBucket(getNbTraces()); + for (int j = i * bucketsPerBar; j < ((i + 1) * bucketsPerBar); j++) { + if (fNbBuckets <= j) { + break; + } + if (fBuckets[j] != null) { + count += fBuckets[j].getNbEvents(); + result.fData[i].add(fBuckets[j]); + } + countLostEvent += fLostEventsBuckets[j]; + } + result.fLostEventsData[i] = countLostEvent; + result.fLastBucket = i; + if (result.fMaxValue < count) { + result.fMaxValue = count; + } + if (result.fMaxCombinedValue < count + countLostEvent) { + result.fMaxCombinedValue = count + countLostEvent; + } + } + + // Scale vertically + if (result.fMaxValue > 0) { + result.fScalingFactor = (double) height / result.fMaxValue; + } + if (result.fMaxCombinedValue > 0) { + result.fScalingFactorCombined = (double) height / result.fMaxCombinedValue; + } + + fBucketDuration = Math.max(fBucketDuration, 1); + // Set selection begin and end index in the scaled histogram + result.fSelectionBeginBucket = (int) ((fSelectionBegin - fFirstBucketTime) / fBucketDuration) / bucketsPerBar; + result.fSelectionEndBucket = (int) ((fSelectionEnd - fFirstBucketTime) / fBucketDuration) / bucketsPerBar; + + result.fFirstBucketTime = fFirstBucketTime; + result.fFirstEventTime = fFirstEventTime; + return result; + } + + // ------------------------------------------------------------------------ + // Helper functions + // ------------------------------------------------------------------------ + + private void updateEndTime() { + fTimeLimit = fFirstBucketTime + (fNbBuckets * fBucketDuration); + } + + private void mergeBuckets() { + for (int i = 0; i < (fNbBuckets / 2); i++) { + fBuckets[i] = new HistogramBucket(fBuckets[2 * i], fBuckets[(2 * i) + 1]); + fLostEventsBuckets[i] = fLostEventsBuckets[2 * i] + fLostEventsBuckets[(2 * i) + 1]; + } + Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, null); + Arrays.fill(fLostEventsBuckets, fNbBuckets / 2, fNbBuckets, 0); + fBucketDuration *= 2; + updateEndTime(); + fLastBucket = (fNbBuckets / 2) - 1; + } + + private void moveBuckets(int offset) { + for (int i = fNbBuckets - 1; i >= offset; i--) { + fBuckets[i] = new HistogramBucket(fBuckets[i - offset]); + fLostEventsBuckets[i] = fLostEventsBuckets[i - offset]; + } + + for (int i = 0; i < offset; i++) { + fBuckets[i] = null; + fLostEventsBuckets[i] = 0; + } + } + + private int getOffset(long timestamp) { + int offset = (int) ((fFirstBucketTime - timestamp) / fBucketDuration); + if (((fFirstBucketTime - timestamp) % fBucketDuration) != 0) { + offset++; + } + return offset; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramRequest.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramRequest.java new file mode 100644 index 0000000000..b91ca655a0 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramRequest.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2009, 2014 Ericsson + * + * 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 Bourque - Initial API and implementation + * Yuriy Vashchuk - Heritage correction. + * Francois Chouinard - Cleanup and refactoring + * Francois Chouinard - Moved from LTTng to TMF + * Simon Delisle - Added a new parameter to the constructor + * Xavier Raynaud - Support multi-trace coloring + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.event.ITmfLostEvent; +import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest; +import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; + +/** + * Class to request events for given time range from a trace to fill a + * HistogramDataModel and HistogramView. + * + * @version 1.0 + * @author Francois Chouinard + * <p> + */ +public class HistogramRequest extends TmfEventRequest { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The histogram data model to fill. + */ + protected final HistogramDataModel fHistogram; + + private final boolean fFullRange; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructor + * + * @param histogram + * The histogram data model + * @param range + * The time range to request data + * @param rank + * The index of the first event to retrieve + * @param nbEvents + * The number of events requested + * @param blockSize + * The number of events per block + * @param execType + * The requested execution priority + * @param fullRange + * Full range or time range for histogram request + * @since 3.0 + */ + public HistogramRequest(HistogramDataModel histogram, TmfTimeRange range, + int rank, int nbEvents, int blockSize, + ITmfEventRequest.ExecutionType execType, boolean fullRange) { + super(ITmfEvent.class, range, rank, nbEvents, execType); + fHistogram = histogram; + fFullRange = fullRange; + } + + // ------------------------------------------------------------------------ + // TmfEventRequest + // ------------------------------------------------------------------------ + + /** + * Handle the event from the trace by updating the histogram data model. + * + * @param event + * a event from the trace + * @see org.eclipse.tracecompass.tmf.core.request.TmfEventRequest#handleData(org.eclipse.tracecompass.tmf.core.event.ITmfEvent) + */ + @Override + public void handleData(ITmfEvent event) { + super.handleData(event); + if (event instanceof ITmfLostEvent) { + ITmfLostEvent lostEvents = (ITmfLostEvent) event; + /* clear the old data when it is a new request */ + fHistogram.countLostEvent(lostEvents.getTimeRange(), lostEvents.getNbLostEvents(), fFullRange); + + } else { /* handle lost event */ + long timestamp = event.getTimestamp().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + fHistogram.countEvent(getNbRead(), timestamp, event.getTrace()); + } + } + + /** + * Complete the request. It also notifies the histogram model about the + * completion. + * + * @see org.eclipse.tracecompass.tmf.core.request.TmfEventRequest#handleCompleted() + */ + @Override + public void handleCompleted() { + fHistogram.complete(); + super.handleCompleted(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramScaledData.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramScaledData.java new file mode 100644 index 0000000000..389f814e0d --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramScaledData.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 2011, 2014 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Bernd Hufmann - Added setter and getter and bar width support + * Francois Chouinard - Moved from LTTng to TMF + * Patrick Tasse - Support selection range + * Jean-Christian Kouamé - Support to manage lost events + * Xavier Raynaud - Support multi-trace coloring + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import java.util.Arrays; + +/** + * Convenience class/struct for scaled histogram data. + * + * @version 1.0 + * @author Francois Chouinard + */ +public class HistogramScaledData { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * Indicator value that bucket is out of range (not filled). + */ + public static final int OUT_OF_RANGE_BUCKET = -1; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * Width of histogram canvas (number of pixels). + */ + public int fWidth; + /** + * Height of histogram canvas (number of pixels). + */ + public int fHeight; + /** + * Width of one histogram bar (number of pixels). + */ + public int fBarWidth; + /** + * Array of scaled values + */ + public HistogramBucket[] fData; + /** + * Array of scaled values combined including the lost events. + * This array contains the number of lost events for each bar in the histogram + * @since 2.2 + */ + public final int[] fLostEventsData; + /** + * The bucket duration of a scaled data bucket. + */ + public long fBucketDuration; + /** + * The maximum number of events of all buckets. + */ + public long fMaxValue; + /** + * the maximum of events of all buckets including the lost events + * @since 2.2 + */ + public long fMaxCombinedValue; + /** + * The index of the selection begin bucket. + * @since 2.1 + */ + public int fSelectionBeginBucket; + /** + * The index of the selection end bucket. + * @since 2.1 + */ + public int fSelectionEndBucket; + /** + * The index of the last bucket. + */ + public int fLastBucket; + /** + * The scaling factor used to fill the scaled data. + */ + public double fScalingFactor; + /** + * The scaling factor used to fill the scaled data including the lost events. + * @since 2.2 + */ + public double fScalingFactorCombined; + /** + * The scaling factor used to fill the combining scaled data including lost events + */ + /** + * Time of first bucket. + */ + public long fFirstBucketTime; + /** + * The time of the first event. + */ + public long fFirstEventTime; + /** + * show the lost events or not + * @since 2.2 + */ + public static volatile boolean hideLostEvents = false; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructor. + * @param width the canvas width + * @param height the canvas height + * @param barWidth the required bar width + */ + public HistogramScaledData(int width, int height, int barWidth) { + fWidth = width; + fHeight = height; + fBarWidth = barWidth; + fData = new HistogramBucket[width / fBarWidth]; + fLostEventsData = new int[width / fBarWidth]; + fBucketDuration = 1; + fMaxValue = 0; + fMaxCombinedValue = 0; + fSelectionBeginBucket = 0; + fSelectionEndBucket = 0; + fLastBucket = 0; + fScalingFactor = 1; + fScalingFactorCombined = 1; + fFirstBucketTime = 0; + } + + /** + * Copy constructor + * @param other another scaled data. + */ + public HistogramScaledData(HistogramScaledData other) { + fWidth = other.fWidth; + fHeight = other.fHeight; + fBarWidth = other.fBarWidth; + fData = Arrays.copyOf(other.fData, other.fData.length); + fLostEventsData = Arrays.copyOf(other.fLostEventsData, other.fLostEventsData.length); + fBucketDuration = other.fBucketDuration; + fMaxValue = other.fMaxValue; + fMaxCombinedValue = other.fMaxCombinedValue; + fSelectionBeginBucket = other.fSelectionBeginBucket; + fSelectionEndBucket = other.fSelectionEndBucket; + fLastBucket = other.fLastBucket; + fScalingFactor = other.fScalingFactor; + fScalingFactorCombined = other.fScalingFactorCombined; + fFirstBucketTime = other.fFirstBucketTime; + } + + // ------------------------------------------------------------------------ + // Setter and Getter + // ------------------------------------------------------------------------ + + /** + * Returns the time of the first bucket of the scaled data. + * @return the time of the first bucket. + */ + public long getFirstBucketTime() { + return fFirstBucketTime; + } + + /** + * Set the first event time. + * @param firstEventTime The time to set + */ + public void setFirstBucketTime(long firstEventTime) { + fFirstBucketTime = firstEventTime; + } + + /** + * Returns the time of the last bucket. + * @return last bucket time + */ + public long getLastBucketTime() { + return getBucketStartTime(fLastBucket); + } + + /** + * Returns the time of the bucket start time for given index. + * @param index A bucket index. + * @return the time of the bucket start time + */ + public long getBucketStartTime(int index) { + return fFirstBucketTime + index * fBucketDuration; + } + + /** + * Returns the time of the bucket end time for given index. + * @param index A bucket index. + * @return the time of the bucket end time + */ + public long getBucketEndTime(int index) { + return getBucketStartTime(index) + fBucketDuration; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramSelectionEndControl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramSelectionEndControl.java new file mode 100644 index 0000000000..c87cb403ca --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramSelectionEndControl.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.swt.widgets.Composite; + +/** + * Text control for selection end time + * + * @since 2.2 + */ +public class HistogramSelectionEndControl extends HistogramCurrentTimeControl { + + /** + * Standard constructor + * + * @param parentView A parent histogram view + * @param parent A parent composite to draw in + * @param label A label + * @param value A value + */ + public HistogramSelectionEndControl(HistogramView parentView, Composite parent, String label, long value) { + super(parentView, parent, label, value); + } + + @Override + protected void updateSelectionTime(long time) { + long begin = Math.min(fParentView.getSelectionBegin(), time); + long end = Math.max(fParentView.getSelectionBegin(), time); + fParentView.updateSelectionTime(begin, end); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramSelectionStartControl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramSelectionStartControl.java new file mode 100644 index 0000000000..14cc546143 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramSelectionStartControl.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.swt.widgets.Composite; + +/** + * Text control for selection start time + * + * @since 2.2 + */ +public class HistogramSelectionStartControl extends HistogramCurrentTimeControl { + + /** + * Standard constructor + * + * @param parentView A parent histogram view + * @param parent A parent composite to draw in + * @param label A label + * @param value A value + */ + public HistogramSelectionStartControl(HistogramView parentView, Composite parent, String label, long value) { + super(parentView, parent, label, value); + } + + @Override + protected void updateSelectionTime(long time) { + if (fParentView.getLinkState()) { + fParentView.updateSelectionTime(time, time); + } else { + long begin = Math.min(time, fParentView.getSelectionEnd()); + long end = Math.max(time, fParentView.getSelectionEnd()); + fParentView.updateSelectionTime(begin, end); + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramTextControl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramTextControl.java new file mode 100644 index 0000000000..5e8c588011 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramTextControl.java @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Ericsson + * + * 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: + * Wiliam Bourque - Adapted from SpinnerGroup (in TimeFrameView) + * Francois Chouinard - Cleanup and refactoring + * Francois Chouinard - Moved from LTTng to TMF + * Francois Chouinard - Better handling of control display, support for signals + * Patrick Tasse - Update for mouse wheel zoom + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseWheelListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; + +/** + * This control provides a group containing a text control. + * + * @version 1.1 + * @author Francois Chouinard + */ +public abstract class HistogramTextControl implements FocusListener, KeyListener { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The parent histogram view. + */ + protected final HistogramView fParentView; + + // Controls data + private final Composite fParent; + private Font fFont; + private final Composite fComposite; + private final Label fLabel; + + /** + * The text value field. + */ + protected final Text fTextValue; + + private long fValue; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructor with given group and text values. + * + * @param parentView The parent histogram view. + * @param parent The parent composite + * @param label The text label + * @param value The initial value + * @since 2.0 + */ + public HistogramTextControl(HistogramView parentView, Composite parent, String label, long value) + { + fParentView = parentView; + fParent = parent; + + // -------------------------------------------------------------------- + // Reduce font size for a more pleasing rendering + // -------------------------------------------------------------------- + + final int fontSizeAdjustment = -1; + final Font font = parent.getFont(); + final FontData fontData = font.getFontData()[0]; + fFont = new Font(font.getDevice(), fontData.getName(), fontData.getHeight() + fontSizeAdjustment, fontData.getStyle()); + + // -------------------------------------------------------------------- + // Create the group + // -------------------------------------------------------------------- + + // Re-used layout variables + GridLayout gridLayout; + GridData gridData; + + // Composite + gridLayout = new GridLayout(3, false); + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + fComposite = new Composite(fParent, SWT.NONE); + fComposite.setLayout(gridLayout); + + gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); + Label filler = new Label(fComposite, SWT.NONE); + filler.setLayoutData(gridData); + + // Label + gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); + fLabel = new Label(fComposite, SWT.NONE); + fLabel.setText(label); + fLabel.setFont(fFont); + + // Text control + gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); + fTextValue = new Text(fComposite, SWT.BORDER); + fTextValue.setFont(fFont); + fTextValue.setLayoutData(gridData); + + // -------------------------------------------------------------------- + // Add listeners + // -------------------------------------------------------------------- + + fTextValue.addFocusListener(this); + fTextValue.addKeyListener(this); + + TmfSignalManager.register(this); + } + + /** + * Dispose of the widget + * @since 2.0 + */ + public void dispose() { + fFont.dispose(); + TmfSignalManager.deregister(this); + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns if widget isDisposed or not + * @return <code>true</code> if widget is disposed else <code>false</code> + */ + public boolean isDisposed() { + return fComposite.isDisposed(); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * Updates the text value. + */ + protected abstract void updateValue(); + + /** + * Set the Grid Layout Data for the group. + * @param layoutData A GridData to set. + */ + public void setLayoutData(GridData layoutData) { + fComposite.setLayoutData(layoutData); + } + + /** + * Enables the receiver if the argument is <code>true</code>, + * and disables it otherwise. A disabled control is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @param enabled the new enabled state + * @since 2.2 + */ + public void setEnabled(boolean enabled) { + fTextValue.setEnabled(enabled); + } + + /** + * The time value in to set in the text field. + * + * @param time the time to set + * @param displayTime the display value + * @since 2.0 + */ + protected void setValue(final long time, final String displayTime) { + // If this is the UI thread, process now + Display display = Display.getCurrent(); + if (display != null) { + if (!isDisposed()) { + fValue = time; + fTextValue.setText(displayTime); + fComposite.layout(); + fParent.getParent().layout(); + } + return; + } + + // Call self from the UI thread + if (!isDisposed()) { + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (!isDisposed()) { + setValue(time, displayTime); + } + } + }); + } + } + + /** + * @param time the time value to display + */ + public abstract void setValue(long time); + + /** + * Returns the time value. + * @return time value. + */ + public long getValue() { + return fValue; + } + + /** + * Add a mouse wheel listener to the text control + * @param listener the mouse wheel listener + * @since 2.0 + */ + public void addMouseWheelListener(MouseWheelListener listener) { + fTextValue.addMouseWheelListener(listener); + } + + /** + * Remove a mouse wheel listener from the text control + * @param listener the mouse wheel listener + * @since 2.0 + */ + public void removeMouseWheelListener(MouseWheelListener listener) { + fTextValue.removeMouseWheelListener(listener); + } + + // ------------------------------------------------------------------------ + // FocusListener + // ------------------------------------------------------------------------ + + @Override + public void focusGained(FocusEvent event) { + } + + @Override + public void focusLost(FocusEvent event) { + updateValue(); + } + + // ------------------------------------------------------------------------ + // KeyListener + // ------------------------------------------------------------------------ + + @Override + public void keyPressed(KeyEvent event) { + switch (event.character) { + case SWT.CR: + updateValue(); + break; + default: + break; + } + } + + @Override + public void keyReleased(KeyEvent e) { + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramTimeRangeControl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramTimeRangeControl.java new file mode 100644 index 0000000000..0eaa50fb7a --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramTimeRangeControl.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Francois Chouinard - Moved from LTTng to TMF + * Francois Chouinard - Simplified constructor, handle interval format change + * Patrick Tasse - Update value handling + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import java.text.ParseException; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat; + +/** + * This control provides a group containing a text control. + * + * @version 2.0 + * @author Francois Chouinard + */ +public class HistogramTimeRangeControl extends HistogramTextControl { + + // ------------------------------------------------------------------------ + // Construction + // ------------------------------------------------------------------------ + + /** + * Constructor with given group and text values. + * + * @param parentView The parent histogram view. + * @param parent The parent composite + * @param groupLabel A group value + * @param value A text value + * @since 2.0 + */ + public HistogramTimeRangeControl(HistogramView parentView, Composite parent, + String groupLabel, long value) + { + super(parentView, parent, groupLabel, value); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + protected void updateValue() { + if (getValue() == Long.MIN_VALUE) { + fTextValue.setText(""); //$NON-NLS-1$ + return; + } + String string = fTextValue.getText(); + long value = getValue(); + try { + value = TmfTimestampFormat.getDefaulIntervalFormat().parseValue(string); + if (value < 1) { + value = getValue(); + } + } catch (ParseException e) { + } + if (getValue() != value) { + fParentView.updateTimeRange(value); + } else { + setValue(value); + } + } + + @Override + public void setValue(long time) { + if (time != Long.MIN_VALUE) { + ITmfTimestamp ts = new TmfTimestamp(time, ITmfTimestamp.NANOSECOND_SCALE); + super.setValue(time, ts.toString(TmfTimestampFormat.getDefaulIntervalFormat())); + } else { + super.setValue(time, ""); //$NON-NLS-1$ + } + } + + // ------------------------------------------------------------------------ + // Signal Handlers + // ------------------------------------------------------------------------ + + /** + * Format the interval and update the display. Compute the new text size, + * adjust the text and group widgets and then refresh the view layout. + * + * @param signal the incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void intervalFormatUpdated(TmfTimestampFormatUpdateSignal signal) { + setValue(getValue()); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramView.java new file mode 100644 index 0000000000..a575619a58 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramView.java @@ -0,0 +1,887 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Ericsson + * + * 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 Bourque - Initial API and implementation + * Yuriy Vashchuk - GUI reorganisation, simplification and some related code improvements. + * Yuriy Vashchuk - Histograms optimisation. + * Yuriy Vashchuk - Histogram Canvas Heritage correction + * Francois Chouinard - Cleanup and refactoring + * Francois Chouinard - Moved from LTTng to TMF + * Patrick Tasse - Update for mouse wheel zoom + * Xavier Raynaud - Support multi-trace coloring + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.Separator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseWheelListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest; +import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType; +import org.eclipse.tracecompass.tmf.core.signal.TmfRangeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalThrottler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.ui.IActionBars; + +/** + * The purpose of this view is to provide graphical time distribution statistics about the trace events. + * <p> + * The view is composed of two histograms and two controls: + * <ul> + * <li>an event distribution histogram for the whole trace; + * <li>an event distribution histogram for current time window (window span); + * <li>the timestamp of the currently selected event; + * <li>the window span (size of the time window of the smaller histogram). + * </ul> + * The histograms x-axis show their respective time range. + * + * @version 2.0 + * @author Francois Chouinard + */ +public class HistogramView extends TmfView { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * The view ID as defined in plugin.xml + */ + public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.ui.views.histogram"; //$NON-NLS-1$ + + private static final Image LINK_IMG = Activator.getDefault().getImageFromPath("/icons/etool16/link.gif"); //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + // Parent widget + private Composite fParent; + + // The current trace + private ITmfTrace fTrace; + + // Current timestamp/time window - everything in the TIME_SCALE + private long fTraceStartTime; + private long fTraceEndTime; + private long fWindowStartTime; + private long fWindowEndTime; + private long fWindowSpan; + private long fSelectionBeginTime; + private long fSelectionEndTime; + + // Time controls + private HistogramTextControl fSelectionStartControl; + private HistogramTextControl fSelectionEndControl; + private HistogramTextControl fTimeSpanControl; + + // Link + private Label fLinkButton; + private boolean fLinkState; + + // Histogram/request for the full trace range + private static FullTraceHistogram fFullTraceHistogram; + private HistogramRequest fFullTraceRequest; + + // Histogram/request for the selected time range + private static TimeRangeHistogram fTimeRangeHistogram; + private HistogramRequest fTimeRangeRequest; + + // Legend area + private Composite fLegendArea; + private Image[] fLegendImages; + + // Throttlers for the time sync and time-range sync signals + private final TmfSignalThrottler fTimeSyncThrottle; + private final TmfSignalThrottler fTimeRangeSyncThrottle; + + // Action for toggle showing the lost events + private Action hideLostEventsAction; + // Action for toggle showing the traces + private Action showTraceAction; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Default constructor + */ + public HistogramView() { + super(ID); + fTimeSyncThrottle = new TmfSignalThrottler(this, 200); + fTimeRangeSyncThrottle = new TmfSignalThrottler(this, 200); + } + + @Override + public void dispose() { + if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { + fTimeRangeRequest.cancel(); + } + if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { + fFullTraceRequest.cancel(); + } + fFullTraceHistogram.dispose(); + fTimeRangeHistogram.dispose(); + fSelectionStartControl.dispose(); + fSelectionEndControl.dispose(); + fTimeSpanControl.dispose(); + super.dispose(); + } + + // ------------------------------------------------------------------------ + // TmfView + // ------------------------------------------------------------------------ + + @Override + public void createPartControl(Composite parent) { + + fParent = parent; + + // Control labels + final String selectionStartLabel = Messages.HistogramView_selectionStartLabel; + final String selectionEndLabel = Messages.HistogramView_selectionEndLabel; + final String windowSpanLabel = Messages.HistogramView_windowSpanLabel; + + // -------------------------------------------------------------------- + // Set the HistogramView layout + // -------------------------------------------------------------------- + + Composite viewComposite = new Composite(fParent, SWT.FILL); + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 2; + gridLayout.horizontalSpacing = 5; + gridLayout.verticalSpacing = 0; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + viewComposite.setLayout(gridLayout); + + // -------------------------------------------------------------------- + // Time controls + // -------------------------------------------------------------------- + + Composite controlsComposite = new Composite(viewComposite, SWT.NONE); + gridLayout = new GridLayout(); + gridLayout.numColumns = 2; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + gridLayout.horizontalSpacing = 5; + gridLayout.verticalSpacing = 1; + gridLayout.makeColumnsEqualWidth = false; + controlsComposite.setLayout(gridLayout); + GridData gridData = new GridData(SWT.FILL, SWT.CENTER, false, false); + controlsComposite.setLayoutData(gridData); + + Composite selectionGroup = new Composite(controlsComposite, SWT.BORDER); + gridLayout = new GridLayout(); + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + gridLayout.horizontalSpacing = 0; + gridLayout.verticalSpacing = 0; + selectionGroup.setLayout(gridLayout); + + // Selection start control + gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.verticalAlignment = SWT.CENTER; + fSelectionStartControl = new HistogramSelectionStartControl(this, selectionGroup, selectionStartLabel, 0L); + fSelectionStartControl.setLayoutData(gridData); + fSelectionStartControl.setValue(Long.MIN_VALUE); + + // Selection end control + gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.verticalAlignment = SWT.CENTER; + fSelectionEndControl = new HistogramSelectionEndControl(this, selectionGroup, selectionEndLabel, 0L); + fSelectionEndControl.setLayoutData(gridData); + fSelectionEndControl.setValue(Long.MIN_VALUE); + + // Link button + gridData = new GridData(); + fLinkButton = new Label(controlsComposite, SWT.NONE); + fLinkButton.setImage(LINK_IMG); + fLinkButton.setLayoutData(gridData); + addLinkButtonListeners(); + + // Window span time control + gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.verticalAlignment = SWT.CENTER; + fTimeSpanControl = new HistogramTimeRangeControl(this, controlsComposite, windowSpanLabel, 0L); + fTimeSpanControl.setLayoutData(gridData); + fTimeSpanControl.setValue(Long.MIN_VALUE); + + // -------------------------------------------------------------------- + // Time range histogram + // -------------------------------------------------------------------- + + Composite timeRangeComposite = new Composite(viewComposite, SWT.NONE); + gridLayout = new GridLayout(); + gridLayout.numColumns = 1; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + gridLayout.marginTop = 5; + gridLayout.horizontalSpacing = 0; + gridLayout.verticalSpacing = 0; + gridLayout.marginLeft = 5; + gridLayout.marginRight = 5; + timeRangeComposite.setLayout(gridLayout); + + // Use remaining horizontal space + gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.verticalAlignment = SWT.FILL; + gridData.grabExcessHorizontalSpace = true; + gridData.grabExcessVerticalSpace = true; + timeRangeComposite.setLayoutData(gridData); + + // Histogram + fTimeRangeHistogram = new TimeRangeHistogram(this, timeRangeComposite); + + // -------------------------------------------------------------------- + // Full range histogram + // -------------------------------------------------------------------- + + Composite fullRangeComposite = new Composite(viewComposite, SWT.FILL); + gridLayout = new GridLayout(); + gridLayout.numColumns = 1; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + gridLayout.marginTop = 5; + gridLayout.horizontalSpacing = 0; + gridLayout.verticalSpacing = 0; + gridLayout.marginLeft = 5; + gridLayout.marginRight = 5; + fullRangeComposite.setLayout(gridLayout); + + // Use remaining horizontal space + gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.verticalAlignment = SWT.FILL; + gridData.horizontalSpan = 2; + gridData.grabExcessHorizontalSpace = true; + gridData.grabExcessVerticalSpace = true; + fullRangeComposite.setLayoutData(gridData); + + // Histogram + fFullTraceHistogram = new FullTraceHistogram(this, fullRangeComposite); + + fLegendArea = new Composite(viewComposite, SWT.FILL); + fLegendArea.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, true, false, 2, 1)); + fLegendArea.setLayout(new RowLayout()); + + // Add mouse wheel listener to time span control + MouseWheelListener listener = fFullTraceHistogram.getZoom(); + fTimeSpanControl.addMouseWheelListener(listener); + + + // View Action Handling + contributeToActionBars(); + + ITmfTrace trace = getActiveTrace(); + if (trace != null) { + traceSelected(new TmfTraceSelectedSignal(this, trace)); + } + } + + @Override + public void setFocus() { + fFullTraceHistogram.fCanvas.setFocus(); + } + + void refresh() { + fParent.layout(true); + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns the current trace handled by the view + * + * @return the current trace + * @since 2.0 + */ + public ITmfTrace getTrace() { + return fTrace; + } + + /** + * Returns the time range of the current selected window (base on default time scale). + * + * @return the time range of current selected window. + * @since 2.0 + */ + public TmfTimeRange getTimeRange() { + return new TmfTimeRange( + new TmfTimestamp(fWindowStartTime, ITmfTimestamp.NANOSECOND_SCALE), + new TmfTimestamp(fWindowEndTime, ITmfTimestamp.NANOSECOND_SCALE)); + } + + /** + * get the show lost events action + * + * @return The action object + * @since 2.2 + */ + public Action getShowLostEventsAction() { + if (hideLostEventsAction == null) { + /* show lost events */ + hideLostEventsAction = new Action(Messages.HistogramView_hideLostEvents, IAction.AS_CHECK_BOX) { + @Override + public void run() { + HistogramScaledData.hideLostEvents = hideLostEventsAction.isChecked(); + long maxNbEvents = HistogramScaledData.hideLostEvents ? fFullTraceHistogram.fScaledData.fMaxValue : fFullTraceHistogram.fScaledData.fMaxCombinedValue; + fFullTraceHistogram.setMaxNbEvents(maxNbEvents); + maxNbEvents = HistogramScaledData.hideLostEvents ? fTimeRangeHistogram.fScaledData.fMaxValue : fTimeRangeHistogram.fScaledData.fMaxCombinedValue; + fTimeRangeHistogram.setMaxNbEvents(maxNbEvents); + } + }; + hideLostEventsAction.setText(Messages.HistogramView_hideLostEvents); + hideLostEventsAction.setToolTipText(Messages.HistogramView_hideLostEvents); + hideLostEventsAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_LOST_EVENTS)); + } + return hideLostEventsAction; + } + + /** + * get the show trace action + * + * @return The action object + * @since 3.0 + */ + public Action getShowTraceAction() { + if (showTraceAction == null) { + /* show lost events */ + showTraceAction = new Action(Messages.HistogramView_showTraces, IAction.AS_CHECK_BOX) { + @Override + public void run() { + Histogram.showTraces = showTraceAction.isChecked(); + fFullTraceHistogram.fCanvas.redraw(); + fTimeRangeHistogram.fCanvas.redraw(); + updateLegendArea(); + } + }; + showTraceAction.setChecked(true); + showTraceAction.setText(Messages.HistogramView_showTraces); + showTraceAction.setToolTipText(Messages.HistogramView_showTraces); + showTraceAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_HIST_TRACES)); + } + return showTraceAction; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * Broadcast TmfSignal about new current selection time range. + * @param beginTime the begin time of current selection. + * @param endTime the end time of current selection. + */ + void updateSelectionTime(long beginTime, long endTime) { + updateDisplayedSelectionTime(beginTime, endTime); + TmfTimestamp beginTs = new TmfTimestamp(beginTime, ITmfTimestamp.NANOSECOND_SCALE); + TmfTimestamp endTs = new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE); + TmfTimeSynchSignal signal = new TmfTimeSynchSignal(this, beginTs, endTs); + fTimeSyncThrottle.queue(signal); + } + + /** + * Get selection begin time + * @return the begin time of current selection + */ + long getSelectionBegin() { + return fSelectionBeginTime; + } + + /** + * Get selection end time + * @return the end time of current selection + */ + long getSelectionEnd() { + return fSelectionEndTime; + } + + /** + * Get the link state + * @return true if begin and end selection time should be linked + */ + boolean getLinkState() { + return fLinkState; + } + + /** + * Broadcast TmfSignal about new selection time range. + * @param startTime the new start time + * @param endTime the new end time + */ + void updateTimeRange(long startTime, long endTime) { + if (fTrace != null) { + // Build the new time range; keep the current time + TmfTimeRange timeRange = new TmfTimeRange( + new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE), + new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE)); + fTimeSpanControl.setValue(endTime - startTime); + + updateDisplayedTimeRange(startTime, endTime); + + // Send the FW signal + TmfRangeSynchSignal signal = new TmfRangeSynchSignal(this, timeRange); + fTimeRangeSyncThrottle.queue(signal); + } + } + + /** + * Broadcast TmfSignal about new selected time range. + * @param newDuration new duration (relative to current start time) + */ + public synchronized void updateTimeRange(long newDuration) { + if (fTrace != null) { + long delta = newDuration - fWindowSpan; + long newStartTime = fWindowStartTime - (delta / 2); + setNewRange(newStartTime, newDuration); + } + } + + private void setNewRange(long startTime, long duration) { + long realStart = startTime; + + if (realStart < fTraceStartTime) { + realStart = fTraceStartTime; + } + + long endTime = realStart + duration; + if (endTime > fTraceEndTime) { + endTime = fTraceEndTime; + if ((endTime - duration) > fTraceStartTime) { + realStart = endTime - duration; + } else { + realStart = fTraceStartTime; + } + } + updateTimeRange(realStart, endTime); + } + + // ------------------------------------------------------------------------ + // Signal handlers + // ------------------------------------------------------------------------ + + /** + * Handles trace opened signal. Loads histogram if new trace time range is not + * equal <code>TmfTimeRange.NULL_RANGE</code> + * @param signal the trace opened signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceOpened(TmfTraceOpenedSignal signal) { + assert (signal != null); + fTrace = signal.getTrace(); + loadTrace(); + } + + /** + * Handles trace selected signal. Loads histogram if new trace time range is not + * equal <code>TmfTimeRange.NULL_RANGE</code> + * @param signal the trace selected signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceSelected(TmfTraceSelectedSignal signal) { + assert (signal != null); + if (fTrace != signal.getTrace()) { + fTrace = signal.getTrace(); + loadTrace(); + } + } + + private void loadTrace() { + initializeHistograms(); + fParent.redraw(); + } + + /** + * Handles trace closed signal. Clears the view and data model and cancels requests. + * @param signal the trace closed signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceClosed(TmfTraceClosedSignal signal) { + + if (signal.getTrace() != fTrace) { + return; + } + + // Kill any running request + if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { + fTimeRangeRequest.cancel(); + } + if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { + fFullTraceRequest.cancel(); + } + + // Initialize the internal data + fTrace = null; + fTraceStartTime = 0L; + fTraceEndTime = 0L; + fWindowStartTime = 0L; + fWindowEndTime = 0L; + fWindowSpan = 0L; + fSelectionBeginTime = 0L; + fSelectionEndTime = 0L; + + // Clear the UI widgets + fFullTraceHistogram.clear(); + fTimeRangeHistogram.clear(); + fSelectionStartControl.setValue(Long.MIN_VALUE); + fSelectionEndControl.setValue(Long.MIN_VALUE); + + fTimeSpanControl.setValue(Long.MIN_VALUE); + + for (Control c: fLegendArea.getChildren()) { + c.dispose(); + } + if (fLegendImages != null) { + for (Image i: fLegendImages) { + i.dispose(); + } + } + fLegendImages = null; + fLegendArea.layout(); + fLegendArea.getParent().layout(); + } + + /** + * Handles trace range updated signal. Extends histogram according to the new time range. If a + * HistogramRequest is already ongoing, it will be cancelled and a new request with the new range + * will be issued. + * + * @param signal the trace range updated signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) { + + if (signal.getTrace() != fTrace) { + return; + } + + TmfTimeRange fullRange = signal.getRange(); + + fTraceStartTime = fullRange.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + fTraceEndTime = fullRange.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + + fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime); + fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); + + sendFullRangeRequest(fullRange); + } + + /** + * Handles the trace updated signal. Used to update time limits (start and end time) + * @param signal the trace updated signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceUpdated(TmfTraceUpdatedSignal signal) { + if (signal.getTrace() != fTrace) { + return; + } + TmfTimeRange fullRange = signal.getTrace().getTimeRange(); + fTraceStartTime = fullRange.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + fTraceEndTime = fullRange.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + + fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime); + fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); + + if ((fFullTraceRequest != null) && fFullTraceRequest.getRange().getEndTime().compareTo(signal.getRange().getEndTime()) < 0) { + sendFullRangeRequest(fullRange); + } +} + + /** + * Handles the current time updated signal. Sets the current time in the time range + * histogram as well as the full histogram. + * + * @param signal the signal to process + */ + @TmfSignalHandler + public void currentTimeUpdated(final TmfTimeSynchSignal signal) { + if (Display.getCurrent() == null) { + // Make sure the signal is handled in the UI thread + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fParent.isDisposed()) { + return; + } + currentTimeUpdated(signal); + } + }); + return; + } + + // Update the selected time range + ITmfTimestamp beginTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE); + ITmfTimestamp endTime = signal.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE); + updateDisplayedSelectionTime(beginTime.getValue(), endTime.getValue()); + } + + /** + * Updates the current time range in the time range histogram and full range histogram. + * @param signal the signal to process + */ + @TmfSignalHandler + public void timeRangeUpdated(final TmfRangeSynchSignal signal) { + if (Display.getCurrent() == null) { + // Make sure the signal is handled in the UI thread + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fParent.isDisposed()) { + return; + } + timeRangeUpdated(signal); + } + }); + return; + } + + if (fTrace != null) { + // Validate the time range + TmfTimeRange range = signal.getCurrentRange().getIntersection(fTrace.getTimeRange()); + if (range == null) { + return; + } + + updateDisplayedTimeRange( + range.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(), + range.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue()); + + // Send the event request to populate the small histogram + sendTimeRangeRequest(fWindowStartTime, fWindowEndTime); + + fTimeSpanControl.setValue(fWindowSpan); + } + } + + // ------------------------------------------------------------------------ + // Helper functions + // ------------------------------------------------------------------------ + + private void initializeHistograms() { + TmfTimeRange fullRange = updateTraceTimeRange(); + long selectionBeginTime = fTraceManager.getSelectionBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long selectionEndTime = fTraceManager.getSelectionEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long startTime = fTraceManager.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long duration = fTraceManager.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue() - startTime; + + if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { + fTimeRangeRequest.cancel(); + } + fTimeRangeHistogram.clear(); + fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); + fTimeRangeHistogram.setTimeRange(startTime, duration); + fTimeRangeHistogram.setSelection(selectionBeginTime, selectionEndTime); + fTimeRangeHistogram.fDataModel.setTrace(fTrace); + + if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { + fFullTraceRequest.cancel(); + } + fFullTraceHistogram.clear(); + fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime); + fFullTraceHistogram.setTimeRange(startTime, duration); + fFullTraceHistogram.setSelection(selectionBeginTime, selectionEndTime); + fFullTraceHistogram.fDataModel.setTrace(fTrace); + + fWindowStartTime = startTime; + fWindowSpan = duration; + fWindowEndTime = startTime + duration; + + fSelectionBeginTime = selectionBeginTime; + fSelectionEndTime = selectionEndTime; + fSelectionStartControl.setValue(fSelectionBeginTime); + fSelectionEndControl.setValue(fSelectionEndTime); + + fTimeSpanControl.setValue(duration); + + ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace); + if (traces != null) { + this.showTraceAction.setEnabled(traces.length < fFullTraceHistogram.getMaxNbTraces()); + } + updateLegendArea(); + + if (!fullRange.equals(TmfTimeRange.NULL_RANGE)) { + sendTimeRangeRequest(startTime, startTime + duration); + sendFullRangeRequest(fullRange); + } + } + + private void updateLegendArea() { + for (Control c: fLegendArea.getChildren()) { + c.dispose(); + } + if (fLegendImages != null) { + for (Image i: fLegendImages) { + i.dispose(); + } + } + fLegendImages = null; + if (fFullTraceHistogram.showTraces()) { + ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace); + fLegendImages = new Image[traces.length]; + int traceIndex = 0; + for (ITmfTrace trace : traces) { + fLegendImages[traceIndex] = new Image(fLegendArea.getDisplay(), 16, 16); + GC gc = new GC(fLegendImages[traceIndex]); + gc.setBackground(fFullTraceHistogram.getTraceColor(traceIndex)); + gc.fillRectangle(0, 0, 15, 15); + gc.setForeground(fLegendArea.getDisplay().getSystemColor(SWT.COLOR_BLACK)); + gc.drawRectangle(0, 0, 15, 15); + gc.dispose(); + + CLabel label = new CLabel(fLegendArea, SWT.NONE); + label.setText(trace.getName()); + label.setImage(fLegendImages[traceIndex]); + traceIndex++; + } + } + fLegendArea.layout(); + fLegendArea.getParent().layout(); + } + + private void updateDisplayedSelectionTime(long beginTime, long endTime) { + fSelectionBeginTime = beginTime; + fSelectionEndTime = endTime; + + fFullTraceHistogram.setSelection(fSelectionBeginTime, fSelectionEndTime); + fTimeRangeHistogram.setSelection(fSelectionBeginTime, fSelectionEndTime); + fSelectionStartControl.setValue(fSelectionBeginTime); + fSelectionEndControl.setValue(fSelectionEndTime); + } + + private void updateDisplayedTimeRange(long start, long end) { + fWindowStartTime = start; + fWindowEndTime = end; + fWindowSpan = fWindowEndTime - fWindowStartTime; + fFullTraceHistogram.setTimeRange(fWindowStartTime, fWindowSpan); + } + + private TmfTimeRange updateTraceTimeRange() { + fTraceStartTime = 0L; + fTraceEndTime = 0L; + + TmfTimeRange timeRange = fTrace.getTimeRange(); + if (!timeRange.equals(TmfTimeRange.NULL_RANGE)) { + fTraceStartTime = timeRange.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + fTraceEndTime = timeRange.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + } + return timeRange; + } + + private void sendTimeRangeRequest(long startTime, long endTime) { + if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { + fTimeRangeRequest.cancel(); + } + TmfTimestamp startTS = new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE); + TmfTimestamp endTS = new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE); + TmfTimeRange timeRange = new TmfTimeRange(startTS, endTS); + + fTimeRangeHistogram.clear(); + fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); + fTimeRangeHistogram.setTimeRange(startTime, endTime - startTime); + + int cacheSize = fTrace.getCacheSize(); + fTimeRangeRequest = new HistogramRequest(fTimeRangeHistogram.getDataModel(), + timeRange, 0, ITmfEventRequest.ALL_DATA, cacheSize, ExecutionType.FOREGROUND, false); + fTrace.sendRequest(fTimeRangeRequest); + } + + private void sendFullRangeRequest(TmfTimeRange fullRange) { + if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { + fFullTraceRequest.cancel(); + } + int cacheSize = fTrace.getCacheSize(); + fFullTraceRequest = new HistogramRequest(fFullTraceHistogram.getDataModel(), + fullRange, + (int) fFullTraceHistogram.fDataModel.getNbEvents(), + ITmfEventRequest.ALL_DATA, + cacheSize, + ExecutionType.BACKGROUND, true); + fTrace.sendRequest(fFullTraceRequest); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + bars.getToolBarManager().add(getShowLostEventsAction()); + bars.getToolBarManager().add(getShowTraceAction()); + bars.getToolBarManager().add(new Separator()); + } + + private void addLinkButtonListeners() { + fLinkButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseDown(MouseEvent e) { + fSelectionEndControl.setEnabled(fLinkState); + fLinkState = !fLinkState; + fLinkButton.redraw(); + } + }); + + fLinkButton.addPaintListener(new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + if (fLinkState) { + Rectangle r = fLinkButton.getBounds(); + r.x = -1; + r.y = -1; + e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); + e.gc.drawRectangle(r); + r.x = 0; + r.y = 0; + e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_DARK_GRAY)); + e.gc.drawRectangle(r); + } + } + }); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramZoom.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramZoom.java new file mode 100644 index 0000000000..296b2c79fd --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/HistogramZoom.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Francois Chouinard - Moved from LTTng to TMF + * Patrick Tasse - Update for mouse wheel zoom + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseWheelListener; + +/** + * Class to handle zooming within histogram windows.. + * + * @version 1.0 + * @author Francois Chouinard + * <p> + */ +public class HistogramZoom implements MouseWheelListener, KeyListener { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + private final static double ZOOM_FACTOR = 0.8; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + private final Histogram fHistogram; + + private long fAbsoluteStartTime; + private long fAbsoluteEndTime; + private final long fMinWindowSize; + + private long fRangeStartTime; + private long fRangeDuration; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Standard constructor. + * + * @param histogram + * The parent histogram object + * @param start + * The start time of the zoom area + * @param end + * The end time of the zoom area + * @since 2.0 + */ + public HistogramZoom(Histogram histogram, long start, long end) { + fHistogram = histogram; + fAbsoluteStartTime = start; + fAbsoluteEndTime = end; + fMinWindowSize = 0; + + fRangeStartTime = fAbsoluteStartTime; + fRangeDuration = fAbsoluteStartTime + fMinWindowSize; + + histogram.addMouseWheelListener(this); + histogram.addKeyListener(this); + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Get start time of the zoom window. + * @return the start time. + */ + public synchronized long getStartTime() { + return fRangeStartTime; + } + + /** + * Get the end time of the zoom window. + * @return the end time + */ + public synchronized long getEndTime() { + return fRangeStartTime + fRangeDuration; + } + + /** + * Get the duration of the zoom window. + * @return the duration of the zoom window. + */ + public synchronized long getDuration() { + return fRangeDuration; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * The the full time range of the histogram + * + * @param startTime the start time the histogram + * @param endTime the end time of the histogram + */ + public synchronized void setFullRange(long startTime, long endTime) { + fAbsoluteStartTime = startTime; + fAbsoluteEndTime = endTime; + } + + /** + * Sets the new zoom window + * @param startTime the start time + * @param duration the duration + */ + public synchronized void setNewRange(long startTime, long duration) { + long realStart = startTime; + + if (realStart < fAbsoluteStartTime) { + realStart = fAbsoluteStartTime; + } + + long endTime = realStart + duration; + if (endTime > fAbsoluteEndTime) { + endTime = fAbsoluteEndTime; + if (endTime - duration > fAbsoluteStartTime) { + realStart = endTime - duration; + } else { + realStart = fAbsoluteStartTime; + } + } + + fRangeStartTime = realStart; + fRangeDuration = endTime - realStart; + } + + // ------------------------------------------------------------------------ + // MouseWheelListener + // ------------------------------------------------------------------------ + + @Override + public void mouseScrolled(MouseEvent event) { + zoom(event.count); + } + + /** + * @since 3.1 + */ + @Override + public void keyPressed(KeyEvent e) { + if (e.character == '+') { + zoom(1); + } else if (e.character == '-') { + zoom(-1); + } + } + + /** + * @since 3.1 + */ + @Override + public void keyReleased(KeyEvent e) { + } + + private synchronized void zoom(int nbClicks) { + // Compute the new time range + long requestedRange = (nbClicks > 0) ? Math.round(ZOOM_FACTOR * fRangeDuration) : (long) Math.ceil(fRangeDuration * (1.0 / ZOOM_FACTOR)); + + // Distribute delta and adjust for boundaries + long requestedStart = validateStart(fRangeStartTime + (fRangeDuration - requestedRange) / 2); + long requestedEnd = validateEnd(requestedStart, requestedStart + requestedRange); + requestedStart = validateStart(requestedEnd - requestedRange); + + fHistogram.updateTimeRange(requestedStart, requestedEnd); + } + + private long validateStart(long start) { + long realStart = start; + + if (realStart < fAbsoluteStartTime) { + realStart = fAbsoluteStartTime; + } + if (realStart > fAbsoluteEndTime) { + realStart = fAbsoluteEndTime - fMinWindowSize; + } + return realStart; + } + + private long validateEnd(long start, long end) { + long realEnd = end; + + if (realEnd > fAbsoluteEndTime) { + realEnd = fAbsoluteEndTime; + } + if (realEnd < start + fMinWindowSize) { + realEnd = start + fMinWindowSize; + } + return realEnd; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/IHistogramDataModel.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/IHistogramDataModel.java new file mode 100644 index 0000000000..56f1e89747 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/IHistogramDataModel.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + * Francois Chouinard - Moved from LTTng to TMF + * Xavier Raynaud - Support multi-trace coloring + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.ui.views.distribution.model.IBaseDistributionModel; + +/** + * Histogram data model interface. + * + * @version 1.0 + * @author Bernd Hufmann + */ +public interface IHistogramDataModel extends IBaseDistributionModel { + /** + * Add event to the correct bucket, compacting the if needed. + * + * @param eventCount the event to count + * @param timestamp the timestamp of the event to count + * @param trace the trace corresponding to given events + * @since 3.0 + */ + void countEvent(long eventCount, long timestamp, ITmfTrace trace); + + /** + * Scale the model data to the width, height and bar width requested. + * + * @param width A width of the histogram canvas + * @param height A height of the histogram canvas + * @param barWidth A width (in pixel) of a histogram bar + * @return the result array of size [width] and where the highest value doesn't exceed [height] + * while considering the bar width [barWidth] + */ + HistogramScaledData scaleTo(int width, int height, int barWidth); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/IHistogramModelListener.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/IHistogramModelListener.java new file mode 100644 index 0000000000..10c6a07a94 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/IHistogramModelListener.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + * Francois Chouinard - Moved from LTTng to TMF + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +/** + * Listener interface for receiving histogram data model notifications. + * + * @version 1.0 + * @author Bernd Hufmann + */ +public interface IHistogramModelListener { + /** + * Method to implement to receive notification about model updates. + */ + void modelUpdated(); +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/Messages.java new file mode 100644 index 0000000000..9141c11fb0 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/Messages.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2009, 2014 Ericsson + * + * 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 Bourque - Initial API and implementation + * Francois Chouinard - Cleanup and refactoring + * Francois Chouinard - Moved from LTTng to TMF + * Patrick Tasse - Update for histogram selection range and tool tip + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.osgi.util.NLS; + +/** + * Messages file for the histogram widgets. + * <p> + * + * @version 1.0 + * @author Francois Chouinard + */ +public class Messages extends NLS { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.tmf.ui.views.histogram.messages"; //$NON-NLS-1$ + + /** + * @since 3.0 + */ + public static String HistogramView_showTraces; + + /** + * @since 2.2 + */ + public static String HistogramView_hideLostEvents; + /** + * The label for the selection start time + * @since 2.2 + */ + public static String HistogramView_selectionStartLabel; + /** + * The label for the selection end time + * @since 2.2 + */ + public static String HistogramView_selectionEndLabel; + /** + * The label for the window span. + */ + public static String HistogramView_windowSpanLabel; + /** + * The tool tip text for the selection span. + * @since 2.2 + */ + public static String Histogram_selectionSpanToolTip; + /** + * The tool tip text for the bucket range. + * @since 2.2 + */ + public static String Histogram_bucketRangeToolTip; + /** + * The tool tip text for the event count. + * @since 2.2 + */ + public static String Histogram_eventCountToolTip; + /** + * The tool tip text for the lost event count. + * @since 2.2 + */ + public static String Histogram_lostEventCountToolTip; + + // ------------------------------------------------------------------------ + // Initializer + // ------------------------------------------------------------------------ + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + private Messages() { + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/TimeRangeHistogram.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/TimeRangeHistogram.java new file mode 100644 index 0000000000..caf56e03b6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/TimeRangeHistogram.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Francois Chouinard - Initial API and implementation + * Bernd Hufmann - Changed to updated histogram data model + * Francois Chouinard - Moved from LTTng to TMF + * Patrick Tasse - Update for mouse wheel zoom + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.histogram; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; + +/** + * A basic histogram widget that displays the event distribution of a specific time range of a trace. + * It has the following additional features: + * <ul> + * <li>zoom in: mouse wheel up (or forward) + * <li>zoom out: mouse wheel down (or backward) + * </ul> + * + * @version 1.1 + * @author Francois Chouinard + */ +public class TimeRangeHistogram extends Histogram { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + private HistogramZoom fZoom = null; + + private long fRangeStartTime = 0L; + private long fRangeDuration; + private long fFullRangeStartTime = 0L; + private long fFullRangeEndTime = 0L; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + /** + * Constructor + * @param view The parent histogram view + * @param parent The parent composite + */ + public TimeRangeHistogram(HistogramView view, Composite parent) { + super(view, parent); + fZoom = new HistogramZoom(this, getStartTime(), getTimeLimit()); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public synchronized void clear() { + fRangeStartTime = 0L; + fRangeDuration = 0L; + fFullRangeStartTime = 0L; + fFullRangeEndTime = 0L; + setOffset(0); + if (fZoom != null) { + fZoom.setFullRange(0L, 0L); + fZoom.setNewRange(0L, 0L); + } + super.clear(); + } + + /** + * Sets the time range of the histogram + * @param startTime The start time + * @param duration The duration of the time range + */ + public synchronized void setTimeRange(long startTime, long duration) { + fRangeStartTime = startTime; + fRangeDuration = duration; + fZoom.setNewRange(startTime, duration); + if (getDataModel().getNbEvents() == 0) { + getDataModel().setTimeRange(startTime, startTime + duration); + getDataModel().setEndTime(startTime + duration); + } + } + + /** + * Sets the full time range of the whole trace. + * @param startTime The start time + * @param endTime The end time + */ + public void setFullRange(long startTime, long endTime) { + fFullRangeStartTime = startTime; + fFullRangeEndTime = endTime; + fZoom.setFullRange(startTime, endTime); + fZoom.setNewRange(fRangeStartTime, fRangeDuration); + } + + // ------------------------------------------------------------------------ + // MouseListener + // ------------------------------------------------------------------------ + + private int fStartPosition; + private int fMinOffset; + private int fMaxOffset; + + @Override + public void mouseDown(MouseEvent event) { + if (fScaledData != null && fDragState == DRAG_NONE && fDataModel.getStartTime() < fDataModel.getEndTime()) { + if (event.button == 2 || (event.button == 1 && (event.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL)) { + fDragState = DRAG_RANGE; + fDragButton = event.button; + fStartPosition = event.x; + long maxOffset = (fRangeStartTime - fFullRangeStartTime) / fScaledData.fBucketDuration; + long minOffset = (fRangeStartTime + fRangeDuration - fFullRangeEndTime) / fScaledData.fBucketDuration; + fMaxOffset = (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, maxOffset)); + fMinOffset = (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, minOffset)); + return; + } else if (event.button == 3) { + fDragState = DRAG_ZOOM; + fDragButton = event.button; + fRangeStartTime = Math.min(getTimestamp(event.x), getEndTime()); + fRangeDuration = 0; + fCanvas.redraw(); + return; + } + } + super.mouseDown(event); + } + + @Override + public void mouseUp(MouseEvent event) { + if (fDragState == DRAG_RANGE && event.button == fDragButton) { + fDragState = DRAG_NONE; + fDragButton = 0; + if (event.x != fStartPosition) { + int nbBuckets = event.x - fStartPosition; + long delta = nbBuckets * fScaledData.fBucketDuration; + long startTime = fRangeStartTime - delta; + fRangeStartTime = Math.max(fFullRangeStartTime, Math.min(fFullRangeEndTime - fRangeDuration, startTime)); + ((HistogramView) fParentView).updateTimeRange(fRangeStartTime, fRangeStartTime + fRangeDuration); + setOffset(0); + } + return; + } else if (fDragState == DRAG_ZOOM && event.button == fDragButton) { + fDragState = DRAG_NONE; + fDragButton = 0; + if (fRangeDuration < 0) { + fRangeStartTime = fRangeStartTime + fRangeDuration; + fRangeDuration = -fRangeDuration; + } + if (fRangeDuration > 0) { + ((HistogramView) fParentView).updateTimeRange(fRangeStartTime, fRangeStartTime + fRangeDuration); + } else { + fRangeStartTime = fZoom.getStartTime(); + fRangeDuration = fZoom.getDuration(); + fCanvas.redraw(); + } + return; + } + super.mouseUp(event); + } + + // ------------------------------------------------------------------------ + // MouseMoveListener + // ------------------------------------------------------------------------ + + @Override + public void mouseMove(MouseEvent event) { + if (fDragState == DRAG_RANGE) { + int offset = Math.max(fMinOffset, Math.min(fMaxOffset, event.x - fStartPosition)); + setOffset(offset); + fCanvas.redraw(); + return; + } else if (fDragState == DRAG_ZOOM) { + long endTime = Math.max(getStartTime(), Math.min(getEndTime(), getTimestamp(event.x))); + fRangeDuration = endTime - fRangeStartTime; + fCanvas.redraw(); + return; + } + super.mouseMove(event); + } + + // ------------------------------------------------------------------------ + // PaintListener + // ------------------------------------------------------------------------ + + @Override + public void paintControl(PaintEvent event) { + super.paintControl(event); + + if (fDragState == DRAG_ZOOM) { + Image image = (Image) fCanvas.getData(IMAGE_KEY); + assert image != null; + + Image rangeRectangleImage = new Image(image.getDevice(), image, SWT.IMAGE_COPY); + GC rangeWindowGC = new GC(rangeRectangleImage); + + drawTimeRangeWindow(rangeWindowGC, fRangeStartTime, fRangeDuration); + + // Draws the buffer image onto the canvas. + event.gc.drawImage(rangeRectangleImage, 0, 0); + + rangeWindowGC.dispose(); + rangeRectangleImage.dispose(); + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/messages.properties new file mode 100644 index 0000000000..116ec9acc3 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/histogram/messages.properties @@ -0,0 +1,21 @@ +############################################################################### +# Copyright (c) 2013, 2014 Ericsson +# +# 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: +# Ericsson - Initial API and implementation +############################################################################### + +HistogramView_hideLostEvents=Hide Lost Events +HistogramView_showTraces=Activate Trace Coloring +HistogramView_selectionStartLabel=Selection Start +HistogramView_selectionEndLabel=Selection End +HistogramView_windowSpanLabel=Window Span +Histogram_selectionSpanToolTip=Selection Span = {0} +Histogram_bucketRangeToolTip=Bucket Range = [{0},{1}) +Histogram_eventCountToolTip=Event count = {0} +Histogram_lostEventCountToolTip=Lost event count = {0} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/Messages.java new file mode 100644 index 0000000000..10da8eef3a --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/Messages.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * 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: + * Alexandre Montplaisir - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.statesystem; + +import org.eclipse.osgi.util.NLS; + +/** + * Localizable strings in the State System Visualizer. + * + * @author Alexandre Montplaisir + * @since 2.0 + */ +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.tmf.ui.views.statesystem.messages"; //$NON-NLS-1$ + + /** + * Initializer + */ + static { + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + /** + * Private constructor (static class) + */ + private Messages() {} + + /** Label for the first column */ + public static String TreeNodeColumnLabel; + + /** Label for the "quark" column" */ + public static String QuarkColumnLabel; + + /** Label for the "value" column */ + public static String ValueColumnLabel; + + /** Label for the "type" column + * @since 2.1*/ + public static String TypeColumnLabel; + + /** Label for the "start time" column */ + public static String StartTimeColumLabel; + + /** Label for the "end time" column */ + public static String EndTimeColumLabel; + + /** Label for the "attribute path" column */ + public static String AttributePathColumnLabel; + + /** + * Printing "out of range" in the value column when the current timestamp is + * outside of the SS's range. + */ + public static String OutOfRangeMsg; + + /** Label for the Filter button + * @since 2.1*/ + public static String FilterButton; + + /** Label for the type Interger + * @since 2.1*/ + public static String TypeInteger; + + /** Label for the type Long + * @since 2.1*/ + public static String TypeLong; + + /** Label for type Double + * @since 3.0*/ + public static String TypeDouble; + + /** Label for the type String + * @since 2.1*/ + public static String TypeString; +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/TmfStateSystemExplorer.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/TmfStateSystemExplorer.java new file mode 100644 index 0000000000..fb658c2370 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/TmfStateSystemExplorer.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2013, 2014 École Polytechnique de Montréal, Ericsson + * + * 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: + * Florian Wininger - Initial API and implementation + * Alexandre Montplaisir - Refactoring, performance tweaks + * Bernd Hufmann - Updated signal handling + * Marc-Andre Laperle - Add time zone preference + * Geneviève Bastien - Use a tree viewer instead of a tree + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.statesystem; + +import java.io.File; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.ui.IActionBars; + +/** + * Displays the State System at a current time. + * + * @author Florian Wininger + * @author Alexandre Montplaisir + * @since 2.0 + */ +public class TmfStateSystemExplorer extends TmfView { + + /** The Environment View's ID */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$ + + private static final Image FILTER_IMAGE = + Activator.getDefault().getImageFromPath( File.separator + "icons" + File.separator + "elcl16" + File.separator + "filter_items.gif"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + private TmfStateSystemViewer fViewer; + + /** + * Default constructor + */ + public TmfStateSystemExplorer() { + super(ID); + } + + // ------------------------------------------------------------------------ + // ViewPart + // ------------------------------------------------------------------------ + + @Override + public void createPartControl(Composite parent) { + + fViewer = new TmfStateSystemViewer(parent); + + fillToolBar() ; + + ITmfTrace trace = getActiveTrace(); + if (trace != null) { + fViewer.traceSelected(new TmfTraceSelectedSignal(this, trace)); + } + + } + + // ------------------------------------------------------------------------ + // Part For Button Action + // ------------------------------------------------------------------------ + + private void fillToolBar() { + Action fFilterAction = new FilterAction(); + fFilterAction.setImageDescriptor(ImageDescriptor.createFromImage(FILTER_IMAGE)); + fFilterAction.setToolTipText(Messages.FilterButton) ; + fFilterAction.setChecked(false); + + IActionBars bars = getViewSite().getActionBars(); + IToolBarManager manager = bars.getToolBarManager(); + manager.add(fFilterAction); + } + + private class FilterAction extends Action { + @Override + public void run() { + fViewer.changeFilterStatus(); + } + } + + @Override + public void setFocus() { + } + + @Override + public void dispose() { + super.dispose(); + if (fViewer != null) { + fViewer.dispose(); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/TmfStateSystemViewer.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/TmfStateSystemViewer.java new file mode 100644 index 0000000000..1c732c1e44 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/TmfStateSystemViewer.java @@ -0,0 +1,463 @@ +/******************************************************************************* + * Copyright (c) 2014 École Polytechnique de Montréal + * + * 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: + * Florian Wininger - Initial API and implementation + * Alexandre Montplaisir - Refactoring, performance tweaks + * Bernd Hufmann - Updated signal handling + * Marc-Andre Laperle - Add time zone preference + * Geneviève Bastien - Moved state system explorer to use the abstract tree viewer + * Patrick Tasse - Refactoring + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.statesystem; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.linuxtools.statesystem.core.ITmfStateSystem; +import org.eclipse.linuxtools.statesystem.core.exceptions.AttributeNotFoundException; +import org.eclipse.linuxtools.statesystem.core.exceptions.StateSystemDisposedException; +import org.eclipse.linuxtools.statesystem.core.exceptions.TimeRangeException; +import org.eclipse.linuxtools.statesystem.core.interval.ITmfStateInterval; +import org.eclipse.linuxtools.statesystem.core.statevalue.ITmfStateValue; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal; +import org.eclipse.tracecompass.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems; +import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.viewers.tree.AbstractTmfTreeViewer; +import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider; +import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeViewerEntry; +import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData; +import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeViewerEntry; + +/** + * Displays the content of the state systems at the current time + * + * @author Florian Wininger + * @author Alexandre Montplaisir + * @author Geneviève Bastien + * @since 3.0 + */ +public class TmfStateSystemViewer extends AbstractTmfTreeViewer { + + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ + private static final int DEFAULT_AUTOEXPAND = 2; + private boolean fFilterStatus = false; + private long fSelection = 0; + + /* Order of columns */ + private static final int ATTRIBUTE_NAME_COL = 0; + private static final int QUARK_COL = 1; + private static final int VALUE_COL = 2; + private static final int TYPE_COL = 3; + private static final int START_TIME_COL = 4; + private static final int END_TIME_COL = 5; + private static final int ATTRIBUTE_FULLPATH_COL = 6; + + /** + * Base class to provide the labels for the tree viewer. Views extending + * this class typically need to override the getColumnText method if they + * have more than one column to display + */ + protected static class StateSystemTreeLabelProvider extends TreeLabelProvider { + + @Override + public String getColumnText(Object element, int columnIndex) { + if (element instanceof StateEntry) { + StateEntry entry = (StateEntry) element; + switch (columnIndex) { + case ATTRIBUTE_NAME_COL: + return entry.getName(); + case QUARK_COL: + return String.valueOf(entry.getQuark()); + case VALUE_COL: + return entry.getValue(); + case TYPE_COL: + return entry.getType(); + case START_TIME_COL: + return entry.getStartTime(); + case END_TIME_COL: + return entry.getEndTime(); + case ATTRIBUTE_FULLPATH_COL: + return entry.getFullPath(); + default: + return EMPTY_STRING; + } + } + return super.getColumnText(element, columnIndex); + } + + @Override + public Color getBackground(Object element, int columnIndex) { + if (element instanceof StateEntry) { + if (((StateEntry) element).isModified()) { + return Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW); + } + } + return super.getBackground(element, columnIndex); + } + } + + /** + * Constructor + * + * @param parent + * The parent containing this viewer + */ + public TmfStateSystemViewer(Composite parent) { + super(parent, false); + this.setLabelProvider(new StateSystemTreeLabelProvider()); + getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND); + } + + @Override + protected ITmfTreeColumnDataProvider getColumnDataProvider() { + return new ITmfTreeColumnDataProvider() { + + @Override + public List<TmfTreeColumnData> getColumnData() { + List<TmfTreeColumnData> columns = new ArrayList<>(); + TmfTreeColumnData column = new TmfTreeColumnData(Messages.TreeNodeColumnLabel); + columns.add(column); + column.setComparator(new ViewerComparator() { + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + TmfTreeViewerEntry n1 = (TmfTreeViewerEntry) e1; + TmfTreeViewerEntry n2 = (TmfTreeViewerEntry) e2; + + return n1.getName().compareTo(n2.getName()); + } + }); + columns.add(new TmfTreeColumnData(Messages.QuarkColumnLabel)); + columns.add(new TmfTreeColumnData(Messages.ValueColumnLabel)); + columns.add(new TmfTreeColumnData(Messages.TypeColumnLabel)); + columns.add(new TmfTreeColumnData(Messages.StartTimeColumLabel)); + columns.add(new TmfTreeColumnData(Messages.EndTimeColumLabel)); + columns.add(new TmfTreeColumnData(Messages.AttributePathColumnLabel)); + return columns; + } + + }; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + protected ITmfTreeViewerEntry updateElements(long start, long end, boolean selection) { + + if (selection) { + fSelection = start; + } else { + fSelection = TmfTraceManager.getInstance().getSelectionBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + } + + if (getTrace() == null) { + return null; + } + + ITmfTreeViewerEntry root = getInput(); + + if (root == null) { + root = createRoot(); + } else if (fFilterStatus) { + clearStateSystemEntries(root); + } + + /* + * Update the values of the elements of the state systems at the + * selection start time + */ + boolean changed = updateStateSystemEntries(root, fSelection); + + return selection || changed ? root : null; + } + + private ITmfTreeViewerEntry createRoot() { + // 'Fake' root node + TmfTreeViewerEntry rootEntry = new TmfTreeViewerEntry("root"); //$NON-NLS-1$ + + for (final ITmfTrace trace : TmfTraceManager.getTraceSetWithExperiment(getTrace())) { + if (trace != null) { + rootEntry.addChild(createTraceEntry(trace)); + } + } + return rootEntry; + } + + private static TmfTreeViewerEntry createTraceEntry(ITmfTrace trace) { + TmfTreeViewerEntry traceEntry = new TmfTreeViewerEntry(trace.getName()); + Iterable<ITmfAnalysisModuleWithStateSystems> modules = trace.getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems.class); + for (ITmfAnalysisModuleWithStateSystems module : modules) { + /* Just schedule the module, the data will be filled when available */ + module.schedule(); + if (module instanceof TmfStateSystemAnalysisModule) { + // TODO: add this method to ITmfAnalysisModuleWithStateSystems + ((TmfStateSystemAnalysisModule) module).waitForInitialization(); + } + for (ITmfStateSystem ss : module.getStateSystems()) { + if (ss != null) { + traceEntry.addChild(new StateSystemEntry(ss)); + } + } + } + return traceEntry; + } + + private static void clearStateSystemEntries(ITmfTreeViewerEntry root) { + for (ITmfTreeViewerEntry traceEntry : root.getChildren()) { + for (ITmfTreeViewerEntry ssEntry : traceEntry.getChildren()) { + ssEntry.getChildren().clear(); + } + } + } + + private boolean updateStateSystemEntries(ITmfTreeViewerEntry root, long timestamp) { + boolean changed = false; + for (ITmfTreeViewerEntry traceEntry : root.getChildren()) { + for (ITmfTreeViewerEntry ssEntry : traceEntry.getChildren()) { + StateSystemEntry stateSystemEntry = (StateSystemEntry) ssEntry; + ITmfStateSystem ss = stateSystemEntry.getSS(); + try { + List<ITmfStateInterval> fullState = ss.queryFullState(timestamp); + changed |= updateStateEntries(ss, fullState, stateSystemEntry, -1, timestamp); + } catch (TimeRangeException e) { + markOutOfRange(stateSystemEntry); + changed = true; + } catch (StateSystemDisposedException e) { + /* Ignored */ + } + } + } + return changed; + } + + private boolean updateStateEntries(ITmfStateSystem ss, List<ITmfStateInterval> fullState, TmfTreeViewerEntry parent, int parentQuark, long timestamp) { + boolean changed = false; + try { + for (int quark : ss.getSubAttributes(parentQuark, false)) { + if (quark >= fullState.size()) { + // attribute was created after the full state query + continue; + } + ITmfStateInterval interval = fullState.get(quark); + StateEntry stateEntry = findStateEntry(parent, quark); + if (stateEntry == null) { + boolean modified = fFilterStatus ? + interval.getStartTime() == timestamp : + !interval.getStateValue().isNull(); + stateEntry = new StateEntry(ss.getAttributeName(quark), quark, ss.getFullAttributePath(quark), + interval.getStateValue(), + new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE), + new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE), + modified); + + // update children first to know if parent is really needed + updateStateEntries(ss, fullState, stateEntry, quark, timestamp); + + /* + * Add this entry to parent if filtering is off, or + * if the entry has children to display, or + * if there is a state change at the current timestamp + */ + if (!fFilterStatus || stateEntry.hasChildren() || interval.getStartTime() == timestamp) { + parent.addChild(stateEntry); + changed = true; + } + } else { + stateEntry.update(interval.getStateValue(), + new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE), + new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE)); + + // update children recursively + updateStateEntries(ss, fullState, stateEntry, quark, timestamp); + } + + } + } catch (AttributeNotFoundException e) { + /* Should not happen, we're iterating on known attributes */ + } + return changed; + } + + private static StateEntry findStateEntry(TmfTreeViewerEntry parent, int quark) { + for (ITmfTreeViewerEntry child : parent.getChildren()) { + StateEntry stateEntry = (StateEntry) child; + if (stateEntry.getQuark() == quark) { + return stateEntry; + } + } + return null; + } + /** + * Set the entries as out of range + */ + private static void markOutOfRange(ITmfTreeViewerEntry parent) { + for (ITmfTreeViewerEntry entry : parent.getChildren()) { + if (entry instanceof StateEntry) { + ((StateEntry) entry).setOutOfRange(); + + /* Update this node's children recursively */ + markOutOfRange(entry); + } + } + } + + /** + * Set the filter status of the viewer. By default, all entries of all state + * system are present, and the values that changed since last refresh are + * shown in yellow. When the filter status is true, only the entries with + * values modified at current time are displayed. + */ + public void changeFilterStatus() { + fFilterStatus = !fFilterStatus; + if (fFilterStatus) { + getTreeViewer().setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS); + } else { + getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND); + clearContent(); + } + updateContent(getSelectionBeginTime(), getSelectionEndTime(), true); + } + + /** + * Update the display to use the updated timestamp format + * + * @param signal + * the incoming signal + */ + @TmfSignalHandler + public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) { + updateContent(getSelectionBeginTime(), getSelectionEndTime(), true); + } + + private static class StateSystemEntry extends TmfTreeViewerEntry { + private final @NonNull ITmfStateSystem fSS; + + public StateSystemEntry(@NonNull ITmfStateSystem ss) { + super(ss.getSSID()); + fSS = ss; + } + + public @NonNull ITmfStateSystem getSS() { + return fSS; + } + } + + private class StateEntry extends TmfTreeViewerEntry { + + private final int fQuark; + private final String fFullPath; + private @NonNull TmfTimestamp fStart; + private @NonNull TmfTimestamp fEnd; + private ITmfStateValue fValue; + private boolean fModified; + private boolean fOutOfRange = false; + + public StateEntry(String name, int quark, String fullPath, ITmfStateValue value, @NonNull TmfTimestamp start, @NonNull TmfTimestamp end, boolean modified) { + super(name); + fQuark = quark; + fFullPath = fullPath; + fStart = start; + fEnd = end; + fValue = value; + fModified = modified; + } + + public int getQuark() { + return fQuark; + } + + public String getFullPath() { + return fFullPath; + } + + public String getStartTime() { + if (fOutOfRange) { + return EMPTY_STRING; + } + return fStart.toString(); + } + + public String getEndTime() { + if (fOutOfRange) { + return EMPTY_STRING; + } + return fEnd.toString(); + } + + public String getValue() { + if (fOutOfRange) { + return Messages.OutOfRangeMsg; + } + switch (fValue.getType()) { + case INTEGER: + case LONG: + case DOUBLE: + case STRING: + return fValue.toString(); + case NULL: + default: + return EMPTY_STRING; + } + } + + public String getType() { + if (fOutOfRange) { + return EMPTY_STRING; + } + switch (fValue.getType()) { + case INTEGER: + return Messages.TypeInteger; + case LONG: + return Messages.TypeLong; + case DOUBLE: + return Messages.TypeDouble; + case STRING: + return Messages.TypeString; + case NULL: + default: + return EMPTY_STRING; + } + } + + public boolean isModified() { + return fModified; + } + + public void update(ITmfStateValue value, @NonNull TmfTimestamp start, @NonNull TmfTimestamp end) { + fModified = false; + fOutOfRange = false; + if (!start.equals(fStart)) { + fModified = true; + fStart = start; + fEnd = end; + fValue = value; + } + } + + public void setOutOfRange() { + fModified = false; + fOutOfRange = true; + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/messages.properties new file mode 100644 index 0000000000..33a5a3d741 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statesystem/messages.properties @@ -0,0 +1,28 @@ +############################################################################### +# Copyright (c) 2013 Ericsson +# +# 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: +# Ericsson - Initial API and implementation +############################################################################### + +# Column names +TreeNodeColumnLabel=State System / Attribute +QuarkColumnLabel=Quark +ValueColumnLabel=Value at timestamp +TypeColumnLabel=Type +StartTimeColumLabel=Start time +EndTimeColumLabel=End time +AttributePathColumnLabel=Full attribute path + +# Other messages +OutOfRangeMsg=Out of Range +FilterButton=Only Display Changes at Selected Timestamp +TypeInteger=Int +TypeLong=Long +TypeDouble=Double +TypeString=String diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/Messages.java new file mode 100755 index 0000000000..3824dc61f5 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/Messages.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Mathieu Denis <mathieu.denis@polymtl.ca> - Initial API and Implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.statistics; + +import org.eclipse.osgi.util.NLS; + +/** + * Messages file for statistics view strings. + * + * @version 2.0 + * @author Mathieu Denis + * @since 2.0 + */ +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.tmf.ui.views.statistics.messages"; //$NON-NLS-1$ + + /** + * String for the global tab name + * @since 2.0 + */ + public static String TmfStatisticsView_GlobalTabName; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/TmfStatisticsView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/TmfStatisticsView.java new file mode 100755 index 0000000000..01e772e1e0 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/TmfStatisticsView.java @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2011, 2014 Ericsson + * + * 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: + * Mathieu Denis <mathieu.denis@polymtl.ca> - Generalized version based on LTTng + * Bernd Hufmann - Updated to use trace reference in TmfEvent and streaming + * Mathieu Denis - New request added to update the statistics from the selected time range + * Mathieu Denis - Generalization of the view to instantiate a viewer specific to a trace type + * + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.statistics; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.ui.viewers.ITmfViewer; +import org.eclipse.tracecompass.tmf.ui.viewers.statistics.TmfStatisticsViewer; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.tracecompass.tmf.ui.widgets.tabsview.TmfViewerFolder; + +/** + * The generic Statistics View displays statistics for any kind of traces. + * + * It is implemented according to the MVC pattern. - The model is a + * TmfStatisticsTreeNode built by the State Manager. - The view is built with a + * TreeViewer. - The controller that keeps model and view synchronized is an + * observer of the model. + * + * @version 2.0 + * @author Mathieu Denis + */ +public class TmfStatisticsView extends TmfView { + + /** + * The ID corresponds to the package in which this class is embedded. + */ + public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.ui.views.statistics"; //$NON-NLS-1$ + + /** + * The view name. + */ + public static final String TMF_STATISTICS_VIEW = "StatisticsView"; //$NON-NLS-1$ + + /** + * The viewer that builds the columns to show the statistics. + * + * @since 2.0 + */ + protected final TmfViewerFolder fStatsViewers; + + /** + * Stores a reference to the selected trace. + */ + private ITmfTrace fTrace; + + /** + * Constructor of a statistics view. + * + * @param viewName The name to give to the view. + */ + public TmfStatisticsView(String viewName) { + super(viewName); + /* + * Create a fake parent for initialization purpose, than set the parent + * as soon as createPartControl is called. + */ + Composite temporaryParent = new Shell(); + fStatsViewers = new TmfViewerFolder(temporaryParent); + } + + /** + * Default constructor. + */ + public TmfStatisticsView() { + this(TMF_STATISTICS_VIEW); + } + + @Override + public void createPartControl(Composite parent) { + fStatsViewers.setParent(parent); + createStatisticsViewers(); + + ITmfTrace trace = getActiveTrace(); + if (trace != null) { + traceSelected(new TmfTraceSelectedSignal(this, trace)); + } + } + + @Override + public void dispose() { + super.dispose(); + fStatsViewers.dispose(); + } + + /** + * Handler called when an trace is opened. + * + * @param signal + * Contains the information about the selection. + * @since 2.0 + */ + @TmfSignalHandler + public void traceOpened(TmfTraceOpenedSignal signal) { + /* + * Dispose the current viewer and adapt the new one to the trace + * type of the trace opened + */ + fStatsViewers.clear(); + // Update the current trace + fTrace = signal.getTrace(); + createStatisticsViewers(); + fStatsViewers.layout(); + } + + /** + * Handler called when an trace is selected. Checks if the trace + * has changed and requests the selected trace if it has not yet been + * cached. + * + * @param signal + * Contains the information about the selection. + * @since 2.0 + */ + @TmfSignalHandler + public void traceSelected(TmfTraceSelectedSignal signal) { + // Does not reload the same trace if already opened + if (signal.getTrace() != fTrace) { + /* + * Dispose the current viewer and adapt the new one to the trace + * type of the trace selected + */ + fStatsViewers.clear(); + // Update the current trace + fTrace = signal.getTrace(); + createStatisticsViewers(); + fStatsViewers.layout(); + + TmfTraceRangeUpdatedSignal updateSignal = new TmfTraceRangeUpdatedSignal(this, fTrace, fTrace.getTimeRange()); + + for (ITmfViewer viewer : fStatsViewers.getViewers()) { + TmfStatisticsViewer statsViewer = (TmfStatisticsViewer) viewer; + statsViewer.sendPartialRequestOnNextUpdate(); + statsViewer.traceRangeUpdated(updateSignal); + } + } else { + /* + * If the same trace is reselected, sends a notification to + * the viewers to make sure they reload correctly their partial + * event count. + */ + for (ITmfViewer viewer : fStatsViewers.getViewers()) { + TmfStatisticsViewer statsViewer = (TmfStatisticsViewer) viewer; + // Will update the partial event count if needed. + statsViewer.sendPartialRequestOnNextUpdate(); + } + } + } + + /** + * @param signal the incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceClosed(TmfTraceClosedSignal signal) { + if (signal.getTrace() != fTrace) { + return; + } + + // Clear the internal data + fTrace = null; + + // Clear the UI widgets + fStatsViewers.clear(); // Also cancels ongoing requests + createStatisticsViewers(); + fStatsViewers.layout(); + } + + @Override + public void setFocus() { + fStatsViewers.setFocus(); + } + + /** + * Creates the statistics viewers for all traces in an experiment and + * populates a viewer folder. Each viewer is placed in a different tab and + * the first one is selected automatically. + * + * It uses the extension point that defines the statistics viewer to build + * from the trace type. If no viewer is defined, another tab won't be + * created, since the global viewer already contains all the basic + * statistics. If there is no trace selected, a global statistics viewer will + * still be created. + * + * @since 2.0 + */ + protected void createStatisticsViewers() { + // Default style for the tabs that will be created + int defaultStyle = SWT.NONE; + + // The folder composite that will contain the tabs + Composite folder = fStatsViewers.getParentFolder(); + + // Instantiation of the global viewer + if (fTrace != null) { + // Shows the name of the trace in the global tab + TmfStatisticsViewer globalViewer = new TmfStatisticsViewer(folder, Messages.TmfStatisticsView_GlobalTabName + " - " + fTrace.getName(), fTrace); //$NON-NLS-1$ + fStatsViewers.addTab(globalViewer, Messages.TmfStatisticsView_GlobalTabName, defaultStyle); + + } else { + // There is no trace selected. Shows an empty global tab + TmfStatisticsViewer globalViewer = new TmfStatisticsViewer(folder, Messages.TmfStatisticsView_GlobalTabName, fTrace); + fStatsViewers.addTab(globalViewer, Messages.TmfStatisticsView_GlobalTabName, defaultStyle); + } + // Makes the global viewer visible + fStatsViewers.setSelection(0); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/messages.properties new file mode 100755 index 0000000000..1a7d06c1e2 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/statistics/messages.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2013 Ericsson +# +# 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: +# Ericsson - Initial API and implementation +############################################################################### + +TmfStatisticsView_GlobalTabName=Global diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/Messages.java new file mode 100644 index 0000000000..e2e6f03b73 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/Messages.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2013 École Polytechnique de Montréal + * + * 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: + * Geneviève Bastien - Initial implementation and API + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.synchronization; + +import org.eclipse.osgi.util.NLS; + +/** + * Message file for the synchronization view + * @since 3.0 + */ +@SuppressWarnings("javadoc") +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.tmf.ui.views.synchronization.messages"; //$NON-NLS-1$ + public static String TmfSynchronizationView_NameColumn; + public static String TmfSynchronizationView_ValueColumn; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/TmfSynchronizationView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/TmfSynchronizationView.java new file mode 100644 index 0000000000..8021e1dabb --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/TmfSynchronizationView.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2013 École Polytechnique de Montréal + * + * 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: + * Geneviève Bastien - Initial implementation and API + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.synchronization; + +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSynchronizedSignal; +import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationAlgorithm; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfExperiment; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; + +/** + * Small view to display statistics about a synchronization + * + * @author Geneviève Bastien + * @since 3.0 + */ +public class TmfSynchronizationView extends TmfView { + + /** + * The ID corresponds to the package in which this class is embedded. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.synchronization"; //$NON-NLS-1$ + + /** + * The view name. + */ + public static final String TMF_SYNCHRONIZATION_VIEW = "SynchronizationView"; //$NON-NLS-1$ + + /** + * The synchronization algorithm to display stats for + */ + private SynchronizationAlgorithm fAlgoSync; + + private Tree fTree; + + /** + * Default constructor + */ + public TmfSynchronizationView() { + super(TMF_SYNCHRONIZATION_VIEW); + } + + @Override + public void createPartControl(Composite parent) { + fTree = new Tree(parent, SWT.NONE); + TreeColumn nameCol = new TreeColumn(fTree, SWT.NONE, 0); + TreeColumn valueCol = new TreeColumn(fTree, SWT.NONE, 1); + nameCol.setText(Messages.TmfSynchronizationView_NameColumn); + valueCol.setText(Messages.TmfSynchronizationView_ValueColumn); + + fTree.setItemCount(0); + + fTree.setHeaderVisible(true); + nameCol.pack(); + valueCol.pack(); + + ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace(); + if (trace != null) { + traceSelected(new TmfTraceSelectedSignal(this, trace)); + } + } + + private void updateTable() { + fTree.setItemCount(0); + if (fAlgoSync == null) { + return; + } + + for (Map.Entry<String, Map<String, Object>> entry : fAlgoSync.getStats().entrySet()) { + TreeItem item = new TreeItem(fTree, SWT.NONE); + item.setText(0, entry.getKey().toString()); + item.setText(1, entry.getValue().toString()); + + for (Map.Entry<String, Object> subentry : entry.getValue().entrySet()) { + TreeItem subitem = new TreeItem(item, SWT.NONE); + subitem.setText(0, subentry.getKey().toString()); + subitem.setText(1, subentry.getValue().toString()); + } + } + + /* Expand the tree items */ + for (int i = 0; i < fTree.getItemCount(); i++) { + fTree.getItem(i).setExpanded(true); + } + + for (TreeColumn column : fTree.getColumns()) { + column.pack(); + } + } + + @Override + public void setFocus() { + fTree.setFocus(); + } + + /** + * Handler called when a trace is selected + * + * @param signal + * Contains information about the selected trace + * @since 3.1 + */ + @TmfSignalHandler + public void traceSelected(TmfTraceSelectedSignal signal) { + fAlgoSync = null; + if (signal.getTrace() instanceof TmfExperiment) { + fAlgoSync = ((TmfExperiment) signal.getTrace()).synchronizeTraces(); + } + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + updateTable(); + } + }); + } + + /** + * Handler called when traces are synchronized + * + * @param signal + * Contains the information about the selection. + */ + @TmfSignalHandler + public void traceSynchronized(TmfTraceSynchronizedSignal signal) { + if (signal.getSyncAlgo() != fAlgoSync) { + fAlgoSync = signal.getSyncAlgo(); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + updateTable(); + } + }); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/messages.properties new file mode 100644 index 0000000000..86c84fe49e --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/synchronization/messages.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2014 Ericsson +# +# 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: +# Ericsson - Initial API and implementation +############################################################################### +TmfSynchronizationView_NameColumn=Synchronization Information +TmfSynchronizationView_ValueColumn=Value diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartAnalysisEntry.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartAnalysisEntry.java new file mode 100644 index 0000000000..512b0e223f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartAnalysisEntry.java @@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.timechart; + +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Vector; + +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; + +/** + * An entry (row) in the time chart analysis view + * + * @version 1.0 + * @author Patrick Tasse + */ +public class TimeChartAnalysisEntry implements ITimeGraphEntry { + + private final ITmfTrace fTrace; + private final Vector<TimeChartEvent> fTraceEvents; + private int fPower = 0; // 2^fPower nanoseconds per vector position + private long fReferenceTime = -1; // time corresponding to beginning of index 0 + private long fStartTime = -1; // time of first event + private long fStopTime = -1; // time of last event + private long fLastRank = -1; // rank of last processed trace event + + TimeChartAnalysisEntry(ITmfTrace trace, int modelSize) { + fTrace = trace; + fTraceEvents = new Vector<>(modelSize); + } + + /** + * @since 2.0 + */ + @Override + public List<ITimeGraphEntry> getChildren() { + return null; + } + + @Override + public ITimeGraphEntry getParent() { + return null; + } + + @Override + public boolean hasChildren() { + return false; + } + + @Override + public String getName() { + return fTrace.getName(); + } + + @Override + public long getStartTime() { + return fStartTime; + } + + @Override + public long getEndTime() { + return fStopTime; + } + + @Override + public boolean hasTimeEvents() { + return true; + } + + @Override + public Iterator<ITimeEvent> getTimeEventsIterator() { + return new EntryIterator(0, Long.MAX_VALUE, 0); + } + + @Override + public Iterator<ITimeEvent> getTimeEventsIterator(long startTime, long stopTime, long maxDuration) { + return new EntryIterator(startTime, stopTime, maxDuration); + } + + private class EntryIterator implements Iterator<ITimeEvent> { + private final long fIteratorStartTime; + private final long fIteratorStopTime; + private final long fIteratorMaxDuration; + private long lastTime = -1; + private TimeChartEvent next = null; + private Iterator<ITimeEvent> nestedIterator = null; + + public EntryIterator(long startTime, long stopTime, long maxDuration) { + fIteratorStartTime = startTime; + fIteratorStopTime = stopTime; + fIteratorMaxDuration = maxDuration; + } + + @Override + public boolean hasNext() { + synchronized (fTraceEvents) { + if (next != null) { + return true; + } + if (nestedIterator != null) { + if (nestedIterator.hasNext()) { + return true; + } + nestedIterator = null; + } + long time = (lastTime == -1) ? fStartTime : lastTime; + int index = (fReferenceTime == -1) ? 0 : (int) ((time - fReferenceTime) >> fPower); + while (index < fTraceEvents.size()) { + TimeChartEvent event = fTraceEvents.get(index++); + if (event != null && (lastTime == -1 || event.getTime() > time)) { + if (event.getTime() + event.getDuration() >= fIteratorStartTime && event.getTime() <= fIteratorStopTime) { + if (event.getItemizedEntry() == null || event.getDuration() <= fIteratorMaxDuration) { + lastTime = event.getTime() + event.getDuration(); + next = event; + return true; + } + nestedIterator = event.getItemizedEntry().getTimeEventsIterator(fIteratorStartTime, fIteratorStopTime, fIteratorMaxDuration); + return nestedIterator.hasNext(); + } + } + } + return false; + } + } + + @Override + public TimeChartEvent next() { + synchronized (fTraceEvents) { + if (nestedIterator != null) { + TimeChartEvent event = (TimeChartEvent) nestedIterator.next(); + lastTime = event.getTime() + event.getDuration(); + return event; + } + if (hasNext()) { + TimeChartEvent event = next; + next = null; + return event; + } + throw new NoSuchElementException(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + /** + * Add a time event to the time chart entry + * + * @param timeEvent + * The event to add + */ + public void addTraceEvent(ITimeEvent timeEvent) { + long time = timeEvent.getTime(); + synchronized (fTraceEvents) { + long index = (fReferenceTime == -1) ? 0 : (time - fReferenceTime) >> fPower; + if (index < 0) { + if (fTraceEvents.capacity() - fTraceEvents.size() < -index) { + int powershift = (-index + fTraceEvents.size() <= 2 * fTraceEvents.capacity()) ? 1 : + (int) Math.ceil(Math.log((double) (-index + fTraceEvents.size()) / fTraceEvents.capacity()) / Math.log(2)); + merge(powershift); + index = (int) ((time - fReferenceTime) >> fPower); + } + shift((int) -index); + index = 0; + fTraceEvents.set(0, (TimeChartEvent) timeEvent); + } else if (index < fTraceEvents.capacity()) { + if (index >= fTraceEvents.size()) { + fTraceEvents.setSize((int) index + 1); + } + } else { + int powershift = (index < 2 * fTraceEvents.capacity()) ? 1 : + (int) Math.ceil(Math.log((double) (index + 1) / fTraceEvents.capacity()) / Math.log(2)); + merge(powershift); + index = (int) ((time - fReferenceTime) >> fPower); + fTraceEvents.setSize((int) index + 1); + } + TimeChartEvent event = fTraceEvents.get((int) index); + if (event == null) { + fTraceEvents.set((int) index, (TimeChartEvent) timeEvent); + } else { + if (event.getItemizedEntry() == null) { + event.merge((TimeChartEvent) timeEvent); + } else { + event.mergeDecorations((TimeChartEvent) timeEvent); + event.getItemizedEntry().addTraceEvent(timeEvent); + } + } + if (fReferenceTime == -1 || time < fReferenceTime) { + fReferenceTime = (time >> fPower) << fPower; + } + if (fStartTime == -1 || time < fStartTime) { + fStartTime = time; + } + if (fStopTime == -1 || time > fStopTime) { + fStopTime = time; + } + } + } + + private void merge(int powershift) { + fPower += powershift; + fReferenceTime = (fReferenceTime >> fPower) << fPower; + int index = 0; + for (int i = 0; i < fTraceEvents.size(); i++) { + TimeChartEvent event = fTraceEvents.get(i); + if (event != null) { + index = (int) ((event.getTime() - fReferenceTime) >> fPower); + TimeChartEvent mergedEvent = fTraceEvents.get(index); + if (mergedEvent == null) { + fTraceEvents.set(index, event); + } else { + mergedEvent.merge(event); + } + if (i != index) { + fTraceEvents.set(i, null); + } + } + } + fTraceEvents.setSize(index + 1); + } + + private void shift(int indexshift) { + int oldSize = fTraceEvents.size(); + fTraceEvents.setSize(oldSize + indexshift); + for (int i = oldSize - 1; i >= 0; i--) { + fTraceEvents.set(i + indexshift, fTraceEvents.get(i)); + } + for (int i = 0; i < indexshift; i++) { + fTraceEvents.set(i, null); + } + } + + /** + * Retrieve the trace associated with this entry + * + * @return The trace object + */ + public ITmfTrace getTrace() { + return fTrace; + } + + /** + * Set the last rank of the entry + * + * @param rank + * The rank to set + */ + public void setLastRank(long rank) { + fLastRank = rank; + } + + /** + * Retrieve the last rank of the entry + * + * @return The last rank + */ + public long getLastRank() { + return fLastRank; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartAnalysisProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartAnalysisProvider.java new file mode 100644 index 0000000000..ba935bdd87 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartAnalysisProvider.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.timechart; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSetting; +import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSettingsManager; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; + +/** + * Provider for a time chart analysis view + * + * @version 1.0 + * @author Patrick Tasse + */ +public class TimeChartAnalysisProvider extends TimeGraphPresentationProvider { + + private static final Color BOOKMARK_INNER_COLOR = new Color(Display.getDefault(), 115, 165, 224); + private static final Color BOOKMARK_OUTER_COLOR = new Color(Display.getDefault(), 2, 70, 140); + private static final Color SEARCH_MATCH_COLOR = new Color(Display.getDefault(), 177, 118, 14); + + private int lastX = Integer.MIN_VALUE; + private int currX = Integer.MIN_VALUE; + private int lastPriority; + private int lastBookmarkX = Integer.MIN_VALUE; + + @Override + public StateItem[] getStateTable() { + + ColorSetting[] settings = ColorSettingsManager.getColorSettings(); + StateItem[] stateItems = new StateItem[settings.length]; + for (int i = 0; i < settings.length; i++) { + stateItems[i] = new StateItem(settings[i].getTickColorRGB()); + } + return stateItems; + } + + @Override + public int getStateTableIndex(ITimeEvent event) { + if (! ((TimeChartEvent) event).isVisible()) { + return ITimeGraphPresentationProvider.INVISIBLE; + } + int priority = ((TimeChartEvent) event).getColorSettingPriority(); + if (currX == lastX) { + priority = Math.min(priority, lastPriority); + } + lastPriority = priority; + return priority; + } + + @Override + public void postDrawEvent(ITimeEvent event, Rectangle rect, GC gc) { + if (! ((TimeChartEvent) event).isVisible()) { + return; + } + lastX = currX; + currX = rect.x; + if (lastBookmarkX == rect.x || ((TimeChartEvent) event).isBookmarked()) { + drawBookmark(rect, gc); + lastBookmarkX = rect.x; + } else if (lastBookmarkX == rect.x - 1) { + Rectangle r = new Rectangle(lastBookmarkX, rect.y, rect.width, rect.height); + drawBookmark(r, gc); + } else { + lastBookmarkX = Integer.MIN_VALUE; + } + if (((TimeChartEvent) event).isSearchMatch()) { + drawSearchMatch(rect, gc); + } + } + + private static void drawBookmark(Rectangle r, GC gc) { + gc.setForeground(BOOKMARK_OUTER_COLOR); + gc.drawLine(r.x - 1, r.y - 2, r.x - 1, r.y + 2); + gc.drawLine(r.x + 1, r.y - 2, r.x + 1, r.y + 2); + gc.drawPoint(r.x, r.y - 2); + gc.setForeground(BOOKMARK_INNER_COLOR); + gc.drawLine(r.x, r.y - 1, r.x, r.y + 1); + gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE)); + gc.drawPoint(r.x - 1, r.y + 3); + gc.drawPoint(r.x, r.y + 2); + gc.drawPoint(r.x + 1, r.y + 3); + } + + private static void drawSearchMatch(Rectangle r, GC gc) { + gc.setForeground(SEARCH_MATCH_COLOR); + gc.drawPoint(r.x, r.y + r.height); + gc.drawLine(r.x - 1, r.y + r.height + 1, r.x + 1, r.y + r.height + 1); + gc.drawLine(r.x - 2, r.y + r.height + 2, r.x + 2, r.y + r.height + 2); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartDecorationProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartDecorationProvider.java new file mode 100644 index 0000000000..1dbd59dd0f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartDecorationProvider.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.timechart; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter; + +/** + * Provider for decorations in the time chart view + * + * @version 1.0 + * @author Patrick Tasse + */ +public class TimeChartDecorationProvider { + + private final IFile fBookmarksFile; + private final Set<Long> fBookmarksSet = new HashSet<>(); + private ITmfFilter fFilterFilter; + private ITmfFilter fSearchFilter; + + /** + * Constructor + * + * @param bookmarksFile + * Bookmark file associated with the trace + */ + public TimeChartDecorationProvider(IFile bookmarksFile) { + fBookmarksFile = bookmarksFile; + refreshBookmarks(); + } + + /** + * Retrieve the bookmark file that was assigned to this provider + * + * @return The bookmark file + */ + public IFile getBookmarksFile() { + return fBookmarksFile; + } + + /** + * Verify if the selected rank has a bookmark assigned to it. + * + * @param rank + * The rank to check for + * @return If there is a bookmark there + */ + public boolean isBookmark(long rank) { + return fBookmarksSet.contains(rank); + } + + /** + * Refresh the bookmark display. + */ + public void refreshBookmarks() { + try { + fBookmarksSet.clear(); + if (fBookmarksFile == null) { + return; + } + for (IMarker bookmark : fBookmarksFile.findMarkers( + IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) { + int location = bookmark.getAttribute(IMarker.LOCATION, -1); + if (location != -1) { + Long rank = (long) location; + fBookmarksSet.add(rank); + } + } + } catch (CoreException e) { + Activator.getDefault().logError("Error refreshing bookmarks", e); //$NON-NLS-1$ + } + } + + /** + * Notify that a filter is now applied on the view. + * + * @param filter + * The filter that was applied + */ + public void filterApplied(ITmfFilter filter) { + fFilterFilter = filter; + } + + /** + * Check if an event is currently visible in the view or not. + * + * @param event + * The event to check for + * @return If the event is visible or not + */ + public boolean isVisible(ITmfEvent event) { + if (fFilterFilter != null) { + return fFilterFilter.matches(event); + } + return true; + } + + /** + * Notify that a search is applied on the view. + * + * @param filter + * The search filter that was applied + */ + public void searchApplied(ITmfFilter filter) { + fSearchFilter = filter; + } + + /** + * Verify if the currently active search filter applies to the given event + * or not. + * + * @param event + * The event to check for + * @return If the event matches + */ + public boolean isSearchMatch(ITmfEvent event) { + if (fSearchFilter != null) { + return fSearchFilter.matches(event); + } + return false; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartEvent.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartEvent.java new file mode 100644 index 0000000000..9a5850cebc --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartEvent.java @@ -0,0 +1,375 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.timechart; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSettingsManager; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; + +/** + * Event in the time chart view + * + * @version 1.0 + * @author Patrick Tasse + */ +public class TimeChartEvent implements ITimeEvent { + + private static final byte TIMESTAMP_SCALE = -9; + + private final TimeChartAnalysisEntry fParentEntry; + private long fTime; + private long fDuration; + private long fFirstRank; + private long fLastRank; + private final RankRangeList fRankRangeList; + private long fNbEvents; + private int fColorSettingPriority; + private boolean fIsBookmark; + private boolean fIsVisible; + private boolean fIsSearchMatch; + private TimeChartAnalysisEntry fItemizedEntry; + private boolean fItemizing; + + /** + * Standard constructor + * + * @param parentEntry + * The parent entry + * @param event + * The event from which this time chart event originates + * @param rank + * The rank of the event in the trace + * @param decorationProvider + * The decoration provider to use + */ + public TimeChartEvent(TimeChartAnalysisEntry parentEntry, ITmfEvent event, + long rank, TimeChartDecorationProvider decorationProvider) { + fParentEntry = parentEntry; + fTime = event.getTimestamp().normalize(0, TIMESTAMP_SCALE).getValue(); + fDuration = 0; + fFirstRank = fLastRank = rank; + fRankRangeList = new RankRangeList(rank); + fNbEvents = 1; + fColorSettingPriority = ColorSettingsManager.getColorSettingPriority(event); + fIsBookmark = decorationProvider.isBookmark(rank); + fIsVisible = decorationProvider.isVisible(event); + fIsSearchMatch = decorationProvider.isSearchMatch(event); + } + + @Override + public ITimeGraphEntry getEntry() { + return fParentEntry; + } + + @Override + public long getTime() { + return fTime; + } + + @Override + public long getDuration() { + return fDuration; + } + + /** + * Retrieve the rank of the trace event which started this time event. + * + * @return The rank of the beginning + */ + public long getFirstRank() { + return fFirstRank; + } + + /** + * Retrieve the rank of the trace event which *finished* this time event. + * + * @return The rank of the end + */ + public long getLastRank() { + return fLastRank; + } + + /** + * Get the list of rank ranges corresponding to this time event. + * + * @return The rank range list + */ + public RankRangeList getRankRangeList() { + return fRankRangeList; + } + + /** + * Merge another time event with this one. + * + * @param event + * The other event + */ + public void merge(TimeChartEvent event) { + mergeDecorations(event); + if (fTime == event.getTime() && fDuration == event.getDuration()) { + return; + } + long endTime = Math.max(fTime + fDuration, event.getTime() + event.getDuration()); + fTime = Math.min(fTime, event.getTime()); + fDuration = endTime - fTime; + fFirstRank = Math.min(fFirstRank, event.fFirstRank); + fLastRank = Math.max(fLastRank, event.fLastRank); + fNbEvents += event.fNbEvents; + fItemizedEntry = null; + synchronized (fRankRangeList) { + fRankRangeList.merge(event.getRankRangeList()); + } + } + + /** + * Merge the decorations of another time event with the decorations of this + * one. + * + * @param event + * The other event + */ + public void mergeDecorations(TimeChartEvent event) { + fColorSettingPriority = Math.min(fColorSettingPriority, event.getColorSettingPriority()); + fIsBookmark |= event.fIsBookmark; + fIsVisible |= event.fIsVisible; + fIsSearchMatch |= event.fIsSearchMatch; + } + + /** + * Get the number of time events that have been merged with this one (starts + * counting at 1 if no merge happened). + * + * @return The current number of events in the bath + */ + public long getNbEvents() { + return fNbEvents; + } + + /** + * Retrieve the color setting priority. + * + * @return The priority + */ + public int getColorSettingPriority() { + return fColorSettingPriority; + } + + /** + * Set the color setting priority. + * + * @param priority + * The priority to set + */ + public void setColorSettingPriority(int priority) { + fColorSettingPriority = priority; + } + + /** + * Check if this time event is bookmarked + * + * @return Y/N + */ + public boolean isBookmarked() { + return fIsBookmark; + } + + /** + * Set this time event to be bookmarked or not. + * + * @param isBookmarked + * Should time time event become a bookmark, or not + */ + public void setIsBookmarked(boolean isBookmarked) { + fIsBookmark = isBookmarked; + } + + /** + * Check if this time is currently visible or not. + * + * @return If the event is visible + */ + public boolean isVisible() { + return fIsVisible; + } + + /** + * Set this time event to visible (or to non-visible). + * + * @param isVisible The new status + */ + public void setIsVisible(boolean isVisible) { + fIsVisible = isVisible; + } + + /** + * Check if the time event matches the current search. + * + * @return If it matches, Y/N + */ + public boolean isSearchMatch() { + return fIsSearchMatch; + } + + /** + * Mark this event as matching (or non-matching) the current search. + * + * @param isSearchMatch + * The new matching status + */ + public void setIsSearchMatch(boolean isSearchMatch) { + fIsSearchMatch = isSearchMatch; + } + + /** + * Set this event's itemized entry. + * + * @param timeAnalysisEntry + * The entry to set + */ + public void setItemizedEntry(TimeChartAnalysisEntry timeAnalysisEntry) { + fItemizedEntry = timeAnalysisEntry; + } + + /** + * Retrieve this event's itemized entry. + * + * @return The itemized entry that was previously set + */ + public TimeChartAnalysisEntry getItemizedEntry() { + return fItemizedEntry; + } + + /** + * @return Has this time event been set to itemizing? + */ + public boolean isItemizing() { + return fItemizing; + } + + /** + * Set this event's itemizing flag to true or false. + * + * @param itemizing + * The new value + */ + public void setItemizing(boolean itemizing) { + fItemizing = itemizing; + } + + /** + * Inner class to define a range in terms of ranks in the trace. + * + * @version 1.0 + * @author Patrick Tasse + */ + public class RankRange { + private long firstRank; + private long lastRank; + + /** + * Standard constructor + * + * @param firstRank + * The first (earliest) rank of the range + * @param lastRank + * The last (latest) rank of the range + */ + public RankRange(long firstRank, long lastRank) { + this.firstRank = firstRank; + this.lastRank = lastRank; + } + + /** + * Retrieve the start rank of this range. + * + * @return The first rank + */ + public long getFirstRank() { + return firstRank; + } + + /** + * Retrieve the end rank of this range + * + * @return The end rank + */ + public long getLastRank() { + return lastRank; + } + + /** + * Calculate the minimal distance between two RankRange's + * + * @param range + * The other range + * @return The distance, in "number of events" between the two ranges + */ + public long distanceFrom(RankRange range) { + if (range.lastRank < fFirstRank) { + return fFirstRank - range.lastRank; + } else if (range.firstRank > fLastRank) { + return range.firstRank - fLastRank; + } else { + return 0; + } + } + + @Override + public String toString() { + return "["+firstRank+","+lastRank+"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + private class RankRangeList extends ArrayList<RankRange> { + + private static final long serialVersionUID = 6060485531208535986L; + + public RankRangeList(long rank) { + super(1); + add(new RankRange(rank, rank)); + } + + public void merge(RankRangeList rankRangeList) { + long threshold = fParentEntry.getTrace().getCacheSize(); + for (RankRange newRange : rankRangeList) { + boolean merged = false; + for (RankRange oldRange : fRankRangeList) { + if (newRange.distanceFrom(oldRange) <= threshold) { + oldRange.firstRank = Math.min(oldRange.firstRank, newRange.firstRank); + oldRange.lastRank = Math.max(oldRange.lastRank, newRange.lastRank); + merged = true; + break; + } + } + if (!merged) { + add(newRange); + } + } + Iterator<RankRange> iterator = fRankRangeList.iterator(); + RankRange previous = null; + while (iterator.hasNext()) { + RankRange range = iterator.next(); + if (previous != null && range.distanceFrom(previous) <= threshold) { + previous.firstRank = Math.min(previous.firstRank, range.firstRank); + previous.lastRank = Math.max(previous.lastRank, range.lastRank); + iterator.remove(); + } + previous = range; + } + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartView.java new file mode 100644 index 0000000000..73dd074a18 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartView.java @@ -0,0 +1,754 @@ +/******************************************************************************* + * Copyright (c) 2010, 2014 Ericsson + * + * 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: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.timechart; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.signal.TmfEventFilterAppliedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfEventSearchAppliedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfRangeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSetting; +import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSettingsManager; +import org.eclipse.tracecompass.tmf.ui.views.colors.IColorSettingsListener; +import org.eclipse.tracecompass.tmf.ui.views.timechart.TimeChartEvent.RankRange; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphRangeListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphSelectionListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphRangeUpdateEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphSelectionEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat; + +/** + * Generic Time Chart view, which is similar to a Gantt chart for trace analysis + * + * @version 1.0 + * @author Patrick Tasse + */ +public class TimeChartView extends TmfView implements ITimeGraphRangeListener, ITimeGraphSelectionListener, ITimeGraphTimeListener, IColorSettingsListener, IResourceChangeListener { + + /** TimeChartView's ID */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.timechart"; //$NON-NLS-1$ + + private static final int TIMESTAMP_SCALE = -9; + + private final int fDisplayWidth; + private TimeGraphViewer fViewer; + private final ArrayList<TimeChartAnalysisEntry> fTimeAnalysisEntries = new ArrayList<>(); + private final Map<ITmfTrace, TimeChartDecorationProvider> fDecorationProviders = new HashMap<>(); + private final ArrayList<DecorateThread> fDecorateThreads = new ArrayList<>(); + private long fStartTime = 0; + private long fStopTime = Long.MAX_VALUE; + private boolean fRefreshBusy = false; + private boolean fRefreshPending = false; + private boolean fRedrawBusy = false; + private boolean fRedrawPending = false; + private final Object fSyncObj = new Object(); + private ITimeGraphPresentationProvider fPresentationProvider; + + /** + * Default constructor + */ + public TimeChartView() { + super("Time Chart"); //$NON-NLS-1$ + fDisplayWidth = Display.getDefault().getBounds().width; + } + + @Override + public void createPartControl(Composite parent) { + fViewer = new TimeGraphViewer(parent, SWT.NONE); + fPresentationProvider = new TimeChartAnalysisProvider(); + fViewer.setTimeGraphProvider(fPresentationProvider); + fViewer.setTimeFormat(TimeFormat.CALENDAR); + fViewer.addTimeListener(this); + fViewer.addRangeListener(this); + fViewer.addSelectionListener(this); + fViewer.setMinimumItemWidth(1); + + IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager(); + fViewer.getTimeGraphControl().setStatusLineManager(statusLineManager); + + for (ITmfTrace trace : TmfTraceManager.getInstance().getOpenedTraces()) { + IFile bookmarksFile = TmfTraceManager.getInstance().getTraceEditorFile(trace); + TimeChartAnalysisEntry timeAnalysisEntry = new TimeChartAnalysisEntry(trace, fDisplayWidth * 2); + fTimeAnalysisEntries.add(timeAnalysisEntry); + fDecorationProviders.put(trace, new TimeChartDecorationProvider(bookmarksFile)); + Thread thread = new ProcessTraceThread(timeAnalysisEntry); + thread.start(); + } + fViewer.setInput(fTimeAnalysisEntries.toArray(new TimeChartAnalysisEntry[0])); + + ColorSettingsManager.addColorSettingsListener(this); + ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); + } + + @Override + public void dispose() { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + for (DecorateThread thread : fDecorateThreads) { + thread.cancel(); + } + ColorSettingsManager.removeColorSettingsListener(this); + super.dispose(); + } + + @Override + public void setFocus() { + fViewer.setFocus(); + } + + private class ProcessTraceThread extends Thread { + + private final TimeChartAnalysisEntry fTimeAnalysisEntry; + + public ProcessTraceThread(TimeChartAnalysisEntry timeAnalysisEntry) { + super("ProcessTraceJob:" + timeAnalysisEntry.getName()); //$NON-NLS-1$ + fTimeAnalysisEntry = timeAnalysisEntry; + } + + @Override + public void run() { + updateTraceEntry(fTimeAnalysisEntry, Long.MAX_VALUE, 0, Long.MAX_VALUE); + } + } + + private void updateTraceEntry(TimeChartAnalysisEntry timeAnalysisEntry, long stopRank, long startTime, long stopTime) { + ITmfTrace trace = timeAnalysisEntry.getTrace(); + TimeChartDecorationProvider decorationProvider = fDecorationProviders.get(trace); + if (decorationProvider == null) { + return; // the trace has been closed + } + ITmfContext context = null; + // TmfTimestamp lastTimestamp = null; + boolean done = false; + while (!done) { + synchronized (timeAnalysisEntry) { + if (timeAnalysisEntry.getLastRank() >= trace.getNbEvents()) { + done = true; + break; + } + if (context == null || context.getRank() != timeAnalysisEntry.getLastRank()) { + if (context != null) { + context.dispose(); + } + if (timeAnalysisEntry.getLastRank() != -1) { + context = trace.seekEvent(timeAnalysisEntry.getLastRank()); + } else { + // context = trace.seekLocation(null); + context = trace.seekEvent(0); + } + } + while (true) { + long rank = context.getRank(); + ITmfEvent event = trace.getNext(context); + if (event == null) { + done = true; + break; + } + // if (!event.getTimestamp().equals(lastTimestamp)) { + TimeChartEvent timeEvent = new TimeChartEvent(timeAnalysisEntry, event, rank, decorationProvider); + if (timeEvent.getTime() >= startTime && timeEvent.getTime() <= stopTime) { + timeAnalysisEntry.addTraceEvent(timeEvent); + } + // lastTimestamp = event.getTimestamp(); + // } *** commented out so that color setting priority gets + // set even if the event has same time + if (context.getRank() == trace.getNbEvents() || context.getRank() == stopRank) { + done = true; + break; + } + if (context.getRank() % trace.getCacheSize() == 1) { + // break for UI refresh + break; + } + } + // timeAnalysisEntry.setLastRank(Math.min(trace.getNbEvents(), + // stopRank)); + timeAnalysisEntry.setLastRank(context.getRank()); + } + redrawViewer(true); + } + if (context != null) { + context.dispose(); + } + } + + private void refreshViewer() { + synchronized (fSyncObj) { + if (fRefreshBusy) { + fRefreshPending = true; + return; + } + fRefreshBusy = true; + } + // Perform the refresh on the UI thread + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fViewer.getControl().isDisposed()) { + return; + } + fViewer.setInput(fTimeAnalysisEntries.toArray(new TimeChartAnalysisEntry[0])); + fViewer.resetStartFinishTime(); + synchronized (fSyncObj) { + fRefreshBusy = false; + if (fRefreshPending) { + fRefreshPending = false; + refreshViewer(); + } + } + } + }); + } + + private void redrawViewer(boolean resetTimeIntervals) { + synchronized (fSyncObj) { + if (fRedrawBusy) { + fRedrawPending = true; + return; + } + fRedrawBusy = true; + } + final boolean reset = resetTimeIntervals; + // Perform the refresh on the UI thread + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fViewer.getControl().isDisposed()) { + return; + } + if (reset) { + fViewer.setTimeRange(fTimeAnalysisEntries.toArray(new TimeChartAnalysisEntry[0])); + fViewer.setTimeBounds(); + } + fViewer.getControl().redraw(); + fViewer.getControl().update(); + synchronized (fSyncObj) { + fRedrawBusy = false; + if (fRedrawPending) { + fRedrawPending = false; + redrawViewer(reset); + } + } + } + }); + } + + private void itemize(long startTime, long stopTime) { + for (int i = 0; i < fTimeAnalysisEntries.size(); i++) { + Thread thread = new ItemizeThread(fTimeAnalysisEntries.get(i), startTime, stopTime); + thread.start(); + } + } + + private class ItemizeThread extends Thread { + + private final TimeChartAnalysisEntry fTimeAnalysisEntry; + private final long startTime; + private final long stopTime; + private final long fMaxDuration; + + private ItemizeThread(TimeChartAnalysisEntry timeAnalysisEntry, long startTime, long stopTime) { + super("Itemize Thread:" + timeAnalysisEntry.getName()); //$NON-NLS-1$ + fTimeAnalysisEntry = timeAnalysisEntry; + this.startTime = startTime; + this.stopTime = stopTime; + fMaxDuration = 3 * (stopTime - startTime) / fDisplayWidth; + } + + @Override + public void run() { + itemizeTraceEntry(fTimeAnalysisEntry); + } + + public void itemizeTraceEntry(TimeChartAnalysisEntry timeAnalysisEntry) { + Iterator<ITimeEvent> iterator = timeAnalysisEntry.getTimeEventsIterator(); + TimeChartEvent event = null; + boolean hasNext = true; + while (hasNext) { + synchronized (timeAnalysisEntry) { + while ((hasNext = iterator.hasNext()) == true) { + event = (TimeChartEvent) iterator.next(); + if (event.getTime() + event.getDuration() > startTime && event.getTime() < stopTime && event.getDuration() > fMaxDuration + && event.getNbEvents() > 1) { + break; + } + } + } + if (hasNext && event != null) { + if (event.getItemizedEntry() == null) { + itemizeEvent(event); + } else { + itemizeTraceEntry(event.getItemizedEntry()); + } + } + } + } + + public void itemizeEvent(TimeChartEvent event) { + synchronized (event) { + if (event.isItemizing()) { + return; + } + event.setItemizing(true); + } + TimeChartAnalysisEntry timeAnalysisEntry = new TimeChartAnalysisEntry(fTimeAnalysisEntry.getTrace(), (int) Math.min( + event.getNbEvents() + 1, fDisplayWidth * 2)); + synchronized (event.getRankRangeList()) { + for (RankRange range : event.getRankRangeList()) { + timeAnalysisEntry.setLastRank(range.getFirstRank()); + updateTraceEntry(timeAnalysisEntry, range.getLastRank() + 1, event.getTime(), event.getTime() + event.getDuration()); + } + } + event.setItemizedEntry(timeAnalysisEntry); + redrawViewer(false); + itemizeTraceEntry(timeAnalysisEntry); + synchronized (event) { + event.setItemizing(false); + } + } + } + + private void redecorate() { + synchronized (fDecorateThreads) { + for (DecorateThread thread : fDecorateThreads) { + thread.cancel(); + } + fDecorateThreads.clear(); + for (int i = 0; i < fTimeAnalysisEntries.size(); i++) { + DecorateThread thread = new DecorateThread(fTimeAnalysisEntries.get(i)); + thread.start(); + fDecorateThreads.add(thread); + } + } + } + + private class DecorateThread extends Thread { + private volatile boolean interrupted = false; + private final TimeChartAnalysisEntry fTimeAnalysisEntry; + private final TimeChartDecorationProvider fDecorationProvider; + private ITmfContext fContext; + private int fCount = 0; + + private DecorateThread(TimeChartAnalysisEntry timeAnalysisEntry) { + super("Decorate Thread:" + timeAnalysisEntry.getName()); //$NON-NLS-1$ + fTimeAnalysisEntry = timeAnalysisEntry; + fDecorationProvider = fDecorationProviders.get(timeAnalysisEntry.getTrace()); + } + + @Override + public void run() { + resetTraceEntry(fTimeAnalysisEntry); + redrawViewer(false); + decorateTraceEntry(fTimeAnalysisEntry, null); + redrawViewer(false); + synchronized (fDecorateThreads) { + fDecorateThreads.remove(this); + } + if (fContext != null) { + fContext.dispose(); + } + } + + public void resetTraceEntry(TimeChartAnalysisEntry timeAnalysisEntry) { + Iterator<ITimeEvent> iterator = timeAnalysisEntry.getTimeEventsIterator(); + TimeChartEvent event = null; + boolean hasNext = true; + while (!interrupted && hasNext) { + synchronized (timeAnalysisEntry) { + while ((hasNext = iterator.hasNext()) == true) { + event = (TimeChartEvent) iterator.next(); + break; + } + } + if (hasNext && event != null) { + // TODO possible concurrency problem here with ItemizeJob + event.setColorSettingPriority(ColorSettingsManager.PRIORITY_NONE); + if (event.getItemizedEntry() != null) { + resetTraceEntry(event.getItemizedEntry()); + } + } + } + } + + public void decorateTraceEntry(TimeChartAnalysisEntry timeAnalysisEntry, TimeChartEvent parentEvent) { + // Set max duration high to ensure iterator does not consider + // itemized events + Iterator<ITimeEvent> iterator = timeAnalysisEntry.getTimeEventsIterator(0, Long.MAX_VALUE, Long.MAX_VALUE); + TimeChartEvent event = null; + int entryPriority = ColorSettingsManager.PRIORITY_NONE; + boolean entryIsBookmarked = false; + boolean entryIsVisible = false; + boolean entryIsSearchMatch = false; + boolean hasNext = true; + while (!interrupted && hasNext) { + synchronized (timeAnalysisEntry) { + while ((hasNext = iterator.hasNext()) == true) { + event = (TimeChartEvent) iterator.next(); + break; + } + } + if (hasNext && event != null) { + // TODO possible concurrency problem here with ItemizeJob + if (event.getItemizedEntry() == null) { + decorateEvent(event); + } else { + decorateTraceEntry(event.getItemizedEntry(), event); + } + entryPriority = Math.min(entryPriority, event.getColorSettingPriority()); + entryIsBookmarked |= event.isBookmarked(); + entryIsVisible |= event.isVisible(); + entryIsSearchMatch |= event.isSearchMatch(); + if (++fCount % timeAnalysisEntry.getTrace().getCacheSize() == 0) { + redrawViewer(false); + } + } + } + if (parentEvent != null) { + parentEvent.setColorSettingPriority(entryPriority); + parentEvent.setIsBookmarked(entryIsBookmarked); + parentEvent.setIsVisible(entryIsVisible); + parentEvent.setIsSearchMatch(entryIsSearchMatch); + } + } + + public void decorateEvent(TimeChartEvent timeChartEvent) { + // TODO possible concurrency problem here with ItemizeJob + TimeChartAnalysisEntry timeAnalysisEntry = (TimeChartAnalysisEntry) timeChartEvent.getEntry(); + ITmfTrace trace = timeAnalysisEntry.getTrace(); + int priority = ColorSettingsManager.PRIORITY_NONE; + boolean isBookmarked = false; + boolean isVisible = false; + boolean isSearchMatch = false; + synchronized (timeChartEvent.getRankRangeList()) { + for (RankRange range : timeChartEvent.getRankRangeList()) { + if (interrupted) { + return; + } + if (fContext == null || fContext.getRank() != range.getFirstRank()) { + if (fContext != null) { + fContext.dispose(); + } + fContext = trace.seekEvent(range.getFirstRank()); + fContext.setRank(range.getFirstRank()); + } + while (true) { + if (interrupted) { + return; + } + long rank = fContext.getRank(); + ITmfEvent event = trace.getNext(fContext); + if (event == null) { + break; + } + long eventTime = event.getTimestamp().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + if (eventTime >= timeChartEvent.getTime() && eventTime <= timeChartEvent.getTime() + timeChartEvent.getDuration()) { + priority = Math.min(priority, ColorSettingsManager.getColorSettingPriority(event)); + } + isBookmarked |= fDecorationProvider.isBookmark(rank); + isVisible |= fDecorationProvider.isVisible(event); + isSearchMatch |= fDecorationProvider.isSearchMatch(event); + if (fContext.getRank() > range.getLastRank()) { + break; + } + } + } + } + timeChartEvent.setColorSettingPriority(priority); + timeChartEvent.setIsBookmarked(isBookmarked); + timeChartEvent.setIsVisible(isVisible); + timeChartEvent.setIsSearchMatch(isSearchMatch); + } + + public void cancel() { + interrupted = true; + } + } + + // ------------------------------------------------------------------------ + // Listeners + // ------------------------------------------------------------------------ + + @Override + public void timeRangeUpdated(TimeGraphRangeUpdateEvent event) { + fStartTime = event.getStartTime(); + fStopTime = event.getEndTime(); + itemize(fStartTime, fStopTime); + final ITmfTimestamp startTimestamp = new TmfTimestamp(event.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE); + final ITmfTimestamp endTimestamp = new TmfTimestamp(event.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE); + TmfTimeRange range = new TmfTimeRange(startTimestamp, endTimestamp); + broadcast(new TmfRangeSynchSignal(this, range)); + } + + @Override + public void selectionChanged(TimeGraphSelectionEvent event) { + ITimeGraphEntry timeAnalysisEntry = null; + if (event.getSelection() instanceof TimeChartAnalysisEntry) { + timeAnalysisEntry = event.getSelection(); + } else if (event.getSelection() instanceof TimeChartEvent) { + timeAnalysisEntry = ((TimeChartEvent) event.getSelection()).getEntry(); + } + if (timeAnalysisEntry instanceof TimeChartAnalysisEntry) { + broadcast(new TmfTraceSelectedSignal(this, ((TimeChartAnalysisEntry) timeAnalysisEntry).getTrace())); + } + } + + @Override + public void timeSelected(TimeGraphTimeEvent event) { + broadcast(new TmfTimeSynchSignal(this, new TmfTimestamp(event.getBeginTime(), TIMESTAMP_SCALE), new TmfTimestamp(event.getEndTime(), TIMESTAMP_SCALE))); + } + + @Override + public void colorSettingsChanged(ColorSetting[] colorSettings) { + // Set presentation provider again to trigger re-creation of new color settings which are stored + // in the TimeGraphControl class + fViewer.setTimeGraphProvider(fPresentationProvider); + redecorate(); + } + + @Override + public void resourceChanged(IResourceChangeEvent event) { + for (IMarkerDelta delta : event.findMarkerDeltas(IMarker.BOOKMARK, false)) { + for (TimeChartDecorationProvider provider : fDecorationProviders.values()) { + if (delta.getResource().equals(provider.getBookmarksFile())) { + if (delta.getKind() == IResourceDelta.CHANGED && delta.getMarker().getAttribute(IMarker.LOCATION, -1) != -1) { + provider.refreshBookmarks(); + } else if (delta.getKind() == IResourceDelta.REMOVED) { + provider.refreshBookmarks(); + } + } + } + } + redecorate(); + } + + // ------------------------------------------------------------------------ + // Signal handlers + // ------------------------------------------------------------------------ + + /** + * Handler for the Trace Opened signal + * + * @param signal + * The incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceOpened(TmfTraceOpenedSignal signal) { + final ITmfTrace trace = signal.getTrace(); + final IFile bookmarksFile = signal.getEditorFile(); + TimeChartAnalysisEntry timeAnalysisEntry = null; + for (int i = 0; i < fTimeAnalysisEntries.size(); i++) { + if (fTimeAnalysisEntries.get(i).getTrace().equals(trace)) { + timeAnalysisEntry = fTimeAnalysisEntries.get(i); + break; + } + } + if (timeAnalysisEntry == null) { + timeAnalysisEntry = new TimeChartAnalysisEntry(trace, fDisplayWidth * 2); + fTimeAnalysisEntries.add(timeAnalysisEntry); + fDecorationProviders.put(trace, new TimeChartDecorationProvider(bookmarksFile)); + Thread thread = new ProcessTraceThread(timeAnalysisEntry); + thread.start(); + } + refreshViewer(); + } + + /** + * Handler for the Trace Closed signal + * + * @param signal + * The incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceClosed(TmfTraceClosedSignal signal) { + final ITmfTrace trace = signal.getTrace(); + for (int i = 0; i < fTimeAnalysisEntries.size(); i++) { + if (fTimeAnalysisEntries.get(i).getTrace().equals(trace)) { + fTimeAnalysisEntries.remove(i); + fDecorationProviders.remove(trace); + synchronized (fDecorateThreads) { + for (DecorateThread thread : fDecorateThreads) { + if (thread.fTimeAnalysisEntry.getTrace() == trace) { + thread.cancel(); + fDecorateThreads.remove(thread); + break; + } + } + } + refreshViewer(); + break; + } + } + } + + /** + * Handler for the Trace Selected signal + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public void traceSelected(TmfTraceSelectedSignal signal) { + if (signal.getSource() != this) { + ITmfTrace trace = signal.getTrace(); + for (int i = 0; i < fTimeAnalysisEntries.size(); i++) { + if (fTimeAnalysisEntries.get(i).getTrace().equals(trace)) { + fViewer.setSelection(fTimeAnalysisEntries.get(i)); + break; + } + } + long beginTime = fTraceManager.getSelectionBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long endTime = fTraceManager.getSelectionEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + fViewer.setSelectionRange(beginTime, endTime); + } + } + + /** + * Handler for the Trace Updated signal + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public void traceUpdated(TmfTraceUpdatedSignal signal) { + final ITmfTrace trace = signal.getTrace(); + for (int i = 0; i < fTimeAnalysisEntries.size(); i++) { + TimeChartAnalysisEntry timeAnalysisEntry = fTimeAnalysisEntries.get(i); + if (timeAnalysisEntry.getTrace().equals(trace)) { + updateTraceEntry(timeAnalysisEntry, Long.MAX_VALUE, 0, Long.MAX_VALUE); + break; + } + } + } + + /** + * Handler for the Time Synch signal + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public void currentTimeUpdated(TmfTimeSynchSignal signal) { + final long beginTime = signal.getBeginTime().normalize(0, TIMESTAMP_SCALE).getValue(); + final long endTime = signal.getEndTime().normalize(0, TIMESTAMP_SCALE).getValue(); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (beginTime == endTime) { + fViewer.setSelectedTime(beginTime, true); + if (fStartTime != fViewer.getTime0() || fStopTime != fViewer.getTime1()) { + fStartTime = fViewer.getTime0(); + fStopTime = fViewer.getTime1(); + itemize(fStartTime, fStopTime); + } + } else { + fViewer.setSelectionRange(beginTime, endTime); + } + } + }); + } + + /** + * Handler for the Time Range Synch signal + * + * @param signal + * The incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void synchToRange(final TmfRangeSynchSignal signal) { + if (signal.getSource() == this) { + return; + } + final long startTime = signal.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + final long endTime = signal.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + fStartTime = startTime; + fStopTime = endTime; + itemize(fStartTime, fStopTime); + fViewer.setStartFinishTime(startTime, endTime); + } + }); + } + + /** + * Handler for the Event Filter Applied signal + * + * @param signal + * The incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void filterApplied(TmfEventFilterAppliedSignal signal) { + TimeChartDecorationProvider decorationProvider = fDecorationProviders.get(signal.getTrace()); + if (decorationProvider == null) { + return; + } + decorationProvider.filterApplied(signal.getEventFilter()); + redecorate(); + } + + /** + * Handler for the Event Search Applied signal + * + * @param signal + * The incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void searchApplied(TmfEventSearchAppliedSignal signal) { + TimeChartDecorationProvider decorationProvider = fDecorationProviders.get(signal.getTrace()); + if (decorationProvider == null) { + return; + } + decorationProvider.searchApplied(signal.getSearchFilter()); + redecorate(); + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java new file mode 100644 index 0000000000..79ca26cac3 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java @@ -0,0 +1,1271 @@ +/******************************************************************************* + * Copyright (c) 2012, 2014 Ericsson, École Polytechnique de Montréal + * + * 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: + * Patrick Tasse - Initial API and implementation + * Bernd Hufmann - Updated signal handling + * Geneviève Bastien - Move code to provide base classes for time graph view + * Marc-Andre Laperle - Add time zone preference + * Geneviève Bastien - Add event links between entries + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.timegraph; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.tracecompass.tmf.core.signal.TmfRangeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.TmfUiRefreshHandler; +import org.eclipse.tracecompass.tmf.ui.views.TmfView; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphRangeListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphSelectionListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphCombo; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphRangeUpdateEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat; +import org.eclipse.ui.IActionBars; + +/** + * An abstract view all time graph views can inherit + * + * This view contains either a time graph viewer, or a time graph combo which is + * divided between a tree viewer on the left and a time graph viewer on the right. + * + * @since 2.1 + */ +public abstract class AbstractTimeGraphView extends TmfView { + + /** + * Redraw state enum + */ + private enum State { + IDLE, BUSY, PENDING + } + + // ------------------------------------------------------------------------ + // Fields + // ------------------------------------------------------------------------ + + /** The timegraph wrapper */ + private ITimeGraphWrapper fTimeGraphWrapper; + + /** The selected trace */ + private ITmfTrace fTrace; + + /** The timegraph entry list */ + private List<TimeGraphEntry> fEntryList; + + /** The trace to entry list hash map */ + private final Map<ITmfTrace, List<TimeGraphEntry>> fEntryListMap = new HashMap<>(); + + /** The trace to build thread hash map */ + private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<>(); + + /** The start time */ + private long fStartTime; + + /** The end time */ + private long fEndTime; + + /** The display width */ + private final int fDisplayWidth; + + /** The zoom thread */ + private ZoomThread fZoomThread; + + /** The next resource action */ + private Action fNextResourceAction; + + /** The previous resource action */ + private Action fPreviousResourceAction; + + /** A comparator class */ + private Comparator<ITimeGraphEntry> fEntryComparator = null; + + /** The redraw state used to prevent unnecessary queuing of display runnables */ + private State fRedrawState = State.IDLE; + + /** The redraw synchronization object */ + private final Object fSyncObj = new Object(); + + /** The presentation provider for this view */ + private final TimeGraphPresentationProvider fPresentation; + + /** The tree column label array, or null if combo is not used */ + private String[] fColumns; + + /** The tree label provider, or null if combo is not used */ + private TreeLabelProvider fLabelProvider = null; + + /** The relative weight of the sash, ignored if combo is not used */ + private int[] fWeight = { 1, 1 }; + + /** The filter column label array, or null if filter is not used */ + private String[] fFilterColumns; + + /** The pack done flag */ + private boolean fPackDone = false; + + /** The filter label provider, or null if filter is not used */ + private TreeLabelProvider fFilterLabelProvider; + + // ------------------------------------------------------------------------ + // Classes + // ------------------------------------------------------------------------ + + private interface ITimeGraphWrapper { + + void setTimeGraphProvider(TimeGraphPresentationProvider fPresentation); + + TimeGraphViewer getTimeGraphViewer(); + + void addSelectionListener(ITimeGraphSelectionListener iTimeGraphSelectionListener); + + ISelectionProvider getSelectionProvider(); + + void setFocus(); + + boolean isDisposed(); + + void refresh(); + + void setInput(Object input); + + Object getInput(); + + void redraw(); + + void update(); + + } + + private class TimeGraphViewerWrapper implements ITimeGraphWrapper { + private TimeGraphViewer viewer; + + private TimeGraphViewerWrapper(Composite parent, int style) { + viewer = new TimeGraphViewer(parent, style); + } + + @Override + public void setTimeGraphProvider(TimeGraphPresentationProvider timeGraphProvider) { + viewer.setTimeGraphProvider(timeGraphProvider); + } + + @Override + public TimeGraphViewer getTimeGraphViewer() { + return viewer; + } + + @Override + public void addSelectionListener(ITimeGraphSelectionListener listener) { + viewer.addSelectionListener(listener); + } + + @Override + public ISelectionProvider getSelectionProvider() { + return viewer.getSelectionProvider(); + } + + @Override + public void setFocus() { + viewer.setFocus(); + } + + @Override + public boolean isDisposed() { + return viewer.getControl().isDisposed(); + } + + @Override + public void setInput(Object input) { + viewer.setInput(input); + } + + @Override + public Object getInput() { + return viewer.getInput(); + } + + @Override + public void refresh() { + viewer.refresh(); + } + + @Override + public void redraw() { + viewer.getControl().redraw(); + } + + @Override + public void update() { + viewer.getControl().update(); + } + } + + private class TimeGraphComboWrapper implements ITimeGraphWrapper { + private TimeGraphCombo combo; + + private TimeGraphComboWrapper(Composite parent, int style) { + combo = new TimeGraphCombo(parent, style, fWeight); + } + + @Override + public void setTimeGraphProvider(TimeGraphPresentationProvider timeGraphProvider) { + combo.setTimeGraphProvider(timeGraphProvider); + } + + @Override + public TimeGraphViewer getTimeGraphViewer() { + return combo.getTimeGraphViewer(); + } + + @Override + public void addSelectionListener(ITimeGraphSelectionListener listener) { + combo.addSelectionListener(listener); + } + + @Override + public ISelectionProvider getSelectionProvider() { + return combo.getTreeViewer(); + } + + @Override + public void setFocus() { + combo.setFocus(); + } + + @Override + public boolean isDisposed() { + return combo.isDisposed(); + } + + @Override + public void setInput(Object input) { + combo.setInput(input); + } + + @Override + public Object getInput() { + return combo.getInput(); + } + + @Override + public void refresh() { + combo.refresh(); + } + + @Override + public void redraw() { + combo.redraw(); + } + + @Override + public void update() { + combo.update(); + } + + TimeGraphCombo getTimeGraphCombo() { + return combo; + } + + TreeViewer getTreeViewer() { + return combo.getTreeViewer(); + } + + IAction getShowFilterAction() { + return combo.getShowFilterAction(); + } + } + + private class TreeContentProvider implements ITreeContentProvider { + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public ITimeGraphEntry[] getElements(Object inputElement) { + if (inputElement != null) { + try { + return ((List<?>) inputElement).toArray(new ITimeGraphEntry[0]); + } catch (ClassCastException e) { + } + } + return new ITimeGraphEntry[0]; + } + + @Override + public Object[] getChildren(Object parentElement) { + ITimeGraphEntry entry = (ITimeGraphEntry) parentElement; + List<? extends ITimeGraphEntry> children = entry.getChildren(); + return children.toArray(new ITimeGraphEntry[children.size()]); + } + + @Override + public Object getParent(Object element) { + ITimeGraphEntry entry = (ITimeGraphEntry) element; + return entry.getParent(); + } + + @Override + public boolean hasChildren(Object element) { + ITimeGraphEntry entry = (ITimeGraphEntry) element; + return entry.hasChildren(); + } + + } + + private class TimeGraphContentProvider implements ITimeGraphContentProvider { + + @Override + public ITimeGraphEntry[] getElements(Object inputElement) { + if (inputElement != null) { + try { + return ((List<?>) inputElement).toArray(new ITimeGraphEntry[0]); + } catch (ClassCastException e) { + } + } + return new ITimeGraphEntry[0]; + } + + } + + /** + * Base class to provide the labels for the tree viewer. Views extending + * this class typically need to override the getColumnText method if they + * have more than one column to display + */ + protected static class TreeLabelProvider implements ITableLabelProvider, ILabelProvider { + + @Override + public void addListener(ILabelProviderListener listener) { + } + + @Override + public void dispose() { + } + + @Override + public boolean isLabelProperty(Object element, String property) { + return false; + } + + @Override + public void removeListener(ILabelProviderListener listener) { + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + TimeGraphEntry entry = (TimeGraphEntry) element; + if (columnIndex == 0) { + return entry.getName(); + } + return new String(); + } + + /** + * @since 3.2 + */ + @Override + public Image getImage(Object element) { + return null; + } + + /** + * @since 3.2 + */ + @Override + public String getText(Object element) { + TimeGraphEntry entry = (TimeGraphEntry) element; + return entry.getName(); + } + + } + + private class BuildThread extends Thread { + private final ITmfTrace fBuildTrace; + private final ITmfTrace fParentTrace; + private final IProgressMonitor fMonitor; + + public BuildThread(final ITmfTrace trace, final ITmfTrace parentTrace, final String name) { + super(name + " build"); //$NON-NLS-1$ + fBuildTrace = trace; + fParentTrace = parentTrace; + fMonitor = new NullProgressMonitor(); + } + + @Override + public void run() { + buildEventList(fBuildTrace, fParentTrace, fMonitor); + synchronized (fBuildThreadMap) { + fBuildThreadMap.remove(fBuildTrace); + } + } + + public void cancel() { + fMonitor.setCanceled(true); + } + } + + private class ZoomThread extends Thread { + private final List<TimeGraphEntry> fZoomEntryList; + private final long fZoomStartTime; + private final long fZoomEndTime; + private final long fResolution; + private final IProgressMonitor fMonitor; + + public ZoomThread(List<TimeGraphEntry> entryList, long startTime, long endTime, String name) { + super(name + " zoom"); //$NON-NLS-1$ + fZoomEntryList = entryList; + fZoomStartTime = startTime; + fZoomEndTime = endTime; + fResolution = Math.max(1, (fZoomEndTime - fZoomStartTime) / fDisplayWidth); + fMonitor = new NullProgressMonitor(); + } + + @Override + public void run() { + if (fZoomEntryList == null) { + return; + } + for (TimeGraphEntry entry : fZoomEntryList) { + if (fMonitor.isCanceled()) { + return; + } + zoom(entry, fMonitor); + } + /* Refresh the arrows when zooming */ + List<ILinkEvent> events = getLinkList(fZoomStartTime, fZoomEndTime, fResolution, fMonitor); + if (events != null) { + fTimeGraphWrapper.getTimeGraphViewer().setLinks(events); + redraw(); + } + } + + private void zoom(TimeGraphEntry entry, IProgressMonitor monitor) { + if (fZoomStartTime <= fStartTime && fZoomEndTime >= fEndTime) { + entry.setZoomedEventList(null); + } else { + List<ITimeEvent> zoomedEventList = getEventList(entry, fZoomStartTime, fZoomEndTime, fResolution, monitor); + if (zoomedEventList != null) { + entry.setZoomedEventList(zoomedEventList); + } + } + redraw(); + for (ITimeGraphEntry child : entry.getChildren()) { + if (fMonitor.isCanceled()) { + return; + } + if (child instanceof TimeGraphEntry) { + zoom((TimeGraphEntry) child, monitor); + } + } + } + + public void cancel() { + fMonitor.setCanceled(true); + } + } + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructs a time graph view that contains either a time graph viewer or + * a time graph combo. + * + * By default, the view uses a time graph viewer. To use a time graph combo, + * the subclass constructor must call {@link #setTreeColumns(String[])} and + * {@link #setTreeLabelProvider(TreeLabelProvider)}. + * + * @param id + * The id of the view + * @param pres + * The presentation provider + */ + public AbstractTimeGraphView(String id, TimeGraphPresentationProvider pres) { + super(id); + fPresentation = pres; + fDisplayWidth = Display.getDefault().getBounds().width; + } + + // ------------------------------------------------------------------------ + // Getters and setters + // ------------------------------------------------------------------------ + + /** + * Getter for the time graph combo + * + * @return The time graph combo, or null if combo is not used + */ + protected TimeGraphCombo getTimeGraphCombo() { + if (fTimeGraphWrapper instanceof TimeGraphComboWrapper) { + return ((TimeGraphComboWrapper) fTimeGraphWrapper).getTimeGraphCombo(); + } + return null; + } + + /** + * Getter for the time graph viewer + * + * @return The time graph viewer + */ + protected TimeGraphViewer getTimeGraphViewer() { + return fTimeGraphWrapper.getTimeGraphViewer(); + } + + /** + * Getter for the presentation provider + * + * @return The time graph presentation provider + * @since 3.0 + */ + protected ITimeGraphPresentationProvider2 getPresentationProvider() { + return fPresentation; + } + + /** + * Sets the tree column labels. + * This should be called from the constructor. + * + * @param columns + * The array of tree column labels + */ + protected void setTreeColumns(final String[] columns) { + fColumns = columns; + } + + /** + * Sets the tree label provider. + * This should be called from the constructor. + * + * @param tlp + * The tree label provider + */ + protected void setTreeLabelProvider(final TreeLabelProvider tlp) { + fLabelProvider = tlp; + } + + /** + * Sets the relative weight of each part of the time graph combo. + * This should be called from the constructor. + * + * @param weights + * The array (length 2) of relative weights of each part of the combo + */ + protected void setWeight(final int[] weights) { + fWeight = weights; + } + + /** + * Sets the filter column labels. + * This should be called from the constructor. + * + * @param filterColumns + * The array of filter column labels + */ + protected void setFilterColumns(final String[] filterColumns) { + fFilterColumns = filterColumns; + } + + /** + * Sets the filter label provider. + * This should be called from the constructor. + * + * @param labelProvider + * The filter label provider + * + * @since 3.0 + */ + protected void setFilterLabelProvider(final TreeLabelProvider labelProvider) { + fFilterLabelProvider = labelProvider; + } + + /** + * Gets the display width + * + * @return the display width + */ + protected int getDisplayWidth() { + return fDisplayWidth; + } + + /** + * Gets the comparator for the entries + * + * @return The entry comparator + */ + protected Comparator<ITimeGraphEntry> getEntryComparator() { + return fEntryComparator; + } + + /** + * Sets the comparator class for the entries + * + * @param comparator + * A comparator object + */ + protected void setEntryComparator(final Comparator<ITimeGraphEntry> comparator) { + fEntryComparator = comparator; + } + + /** + * Gets the trace displayed in the view + * + * @return The trace + */ + protected ITmfTrace getTrace() { + return fTrace; + } + + /** + * Gets the start time + * + * @return The start time + */ + protected long getStartTime() { + return fStartTime; + } + + /** + * Sets the start time + * + * @param time + * The start time + */ + protected void setStartTime(long time) { + fStartTime = time; + } + + /** + * Gets the end time + * + * @return The end time + */ + protected long getEndTime() { + return fEndTime; + } + + /** + * Sets the end time + * + * @param time + * The end time + */ + protected void setEndTime(long time) { + fEndTime = time; + } + + /** + * Gets the entry list for a trace + * + * @param trace + * the trace + * + * @return the entry list map + * @since 3.0 + */ + protected List<TimeGraphEntry> getEntryList(ITmfTrace trace) { + synchronized (fEntryListMap) { + return fEntryListMap.get(trace); + } + } + + /** + * Adds a trace entry list to the entry list map + * + * @param trace + * the trace to add + * @param list + * the list of time graph entries + */ + protected void putEntryList(ITmfTrace trace, List<TimeGraphEntry> list) { + synchronized (fEntryListMap) { + fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list)); + } + } + + /** + * Adds a list of entries to a trace's entry list + * + * @param trace + * the trace + * @param list + * the list of time graph entries to add + * @since 3.0 + */ + protected void addToEntryList(ITmfTrace trace, List<TimeGraphEntry> list) { + synchronized (fEntryListMap) { + List<TimeGraphEntry> entryList = fEntryListMap.get(trace); + if (entryList == null) { + fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list)); + } else { + entryList.addAll(list); + } + } + } + + /** + * Removes a list of entries from a trace's entry list + * + * @param trace + * the trace + * @param list + * the list of time graph entries to remove + * @since 3.0 + */ + protected void removeFromEntryList(ITmfTrace trace, List<TimeGraphEntry> list) { + synchronized (fEntryListMap) { + List<TimeGraphEntry> entryList = fEntryListMap.get(trace); + if (entryList != null) { + entryList.removeAll(list); + } + } + } + + /** + * Text for the "next" button + * + * @return The "next" button text + */ + protected String getNextText() { + return Messages.AbstractTimeGraphtView_NextText; + } + + /** + * Tooltip for the "next" button + * + * @return Tooltip for the "next" button + */ + protected String getNextTooltip() { + return Messages.AbstractTimeGraphView_NextTooltip; + } + + /** + * Text for the "Previous" button + * + * @return The "Previous" button text + */ + protected String getPrevText() { + return Messages.AbstractTimeGraphView_PreviousText; + } + + /** + * Tooltip for the "previous" button + * + * @return Tooltip for the "previous" button + */ + protected String getPrevTooltip() { + return Messages.AbstractTimeGraphView_PreviousTooltip; + } + + // ------------------------------------------------------------------------ + // ViewPart + // ------------------------------------------------------------------------ + + @Override + public void createPartControl(Composite parent) { + if (fColumns == null || fLabelProvider == null) { + fTimeGraphWrapper = new TimeGraphViewerWrapper(parent, SWT.NONE); + TimeGraphViewer viewer = fTimeGraphWrapper.getTimeGraphViewer(); + viewer.setTimeGraphContentProvider(new TimeGraphContentProvider()); + } else { + TimeGraphComboWrapper wrapper = new TimeGraphComboWrapper(parent, SWT.NONE); + fTimeGraphWrapper = wrapper; + TimeGraphCombo combo = wrapper.getTimeGraphCombo(); + combo.setTreeContentProvider(new TreeContentProvider()); + combo.setTreeLabelProvider(fLabelProvider); + combo.setTreeColumns(fColumns); + combo.setFilterContentProvider(new TreeContentProvider()); + combo.setFilterLabelProvider(fFilterLabelProvider); + combo.setFilterColumns(fFilterColumns); + combo.setTimeGraphContentProvider(new TimeGraphContentProvider()); + } + + fTimeGraphWrapper.setTimeGraphProvider(fPresentation); + + fTimeGraphWrapper.getTimeGraphViewer().addRangeListener(new ITimeGraphRangeListener() { + @Override + public void timeRangeUpdated(TimeGraphRangeUpdateEvent event) { + final long startTime = event.getStartTime(); + final long endTime = event.getEndTime(); + TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(startTime), new TmfNanoTimestamp(endTime)); + broadcast(new TmfRangeSynchSignal(AbstractTimeGraphView.this, range)); + if (fZoomThread != null) { + fZoomThread.cancel(); + } + startZoomThread(startTime, endTime); + } + }); + + fTimeGraphWrapper.getTimeGraphViewer().addTimeListener(new ITimeGraphTimeListener() { + @Override + public void timeSelected(TimeGraphTimeEvent event) { + TmfNanoTimestamp startTime = new TmfNanoTimestamp(event.getBeginTime()); + TmfNanoTimestamp endTime = new TmfNanoTimestamp(event.getEndTime()); + broadcast(new TmfTimeSynchSignal(AbstractTimeGraphView.this, startTime, endTime)); + } + }); + + fTimeGraphWrapper.getTimeGraphViewer().setTimeFormat(TimeFormat.CALENDAR); + + IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager(); + fTimeGraphWrapper.getTimeGraphViewer().getTimeGraphControl().setStatusLineManager(statusLineManager); + + // View Action Handling + makeActions(); + contributeToActionBars(); + + ITmfTrace trace = getActiveTrace(); + if (trace != null) { + traceSelected(new TmfTraceSelectedSignal(this, trace)); + } + + // make selection available to other views + getSite().setSelectionProvider(fTimeGraphWrapper.getSelectionProvider()); + } + + @Override + public void setFocus() { + fTimeGraphWrapper.setFocus(); + } + + // ------------------------------------------------------------------------ + // Signal handlers + // ------------------------------------------------------------------------ + + /** + * Handler for the trace opened signal. + * + * @param signal + * The incoming signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceOpened(TmfTraceOpenedSignal signal) { + fTrace = signal.getTrace(); + loadTrace(); + } + + /** + * Handler for the trace selected signal + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public void traceSelected(final TmfTraceSelectedSignal signal) { + if (signal.getTrace() == fTrace) { + return; + } + fTrace = signal.getTrace(); + + loadTrace(); + } + + /** + * Trace is closed: clear the data structures and the view + * + * @param signal + * the signal received + */ + @TmfSignalHandler + public void traceClosed(final TmfTraceClosedSignal signal) { + synchronized (fBuildThreadMap) { + for (ITmfTrace trace : getTracesToBuild(signal.getTrace())) { + BuildThread buildThread = fBuildThreadMap.remove(trace); + if (buildThread != null) { + buildThread.cancel(); + } + } + } + synchronized (fEntryListMap) { + fEntryListMap.remove(signal.getTrace()); + } + if (signal.getTrace() == fTrace) { + fTrace = null; + fStartTime = 0; + fEndTime = 0; + if (fZoomThread != null) { + fZoomThread.cancel(); + } + refresh(); + } + } + + /** + * Handler for the time synch signal + * + * @param signal + * The signal that's received + */ + @TmfSignalHandler + public void synchToTime(final TmfTimeSynchSignal signal) { + if (signal.getSource() == this || fTrace == null) { + return; + } + final long beginTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + final long endTime = signal.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fTimeGraphWrapper.isDisposed()) { + return; + } + if (beginTime == endTime) { + fTimeGraphWrapper.getTimeGraphViewer().setSelectedTime(beginTime, true); + } else { + fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(beginTime, endTime); + } + startZoomThread(fTimeGraphWrapper.getTimeGraphViewer().getTime0(), fTimeGraphWrapper.getTimeGraphViewer().getTime1()); + + synchingToTime(beginTime); + } + }); + } + + /** + * Handler for the range synch signal + * + * @param signal + * The signal that's received + */ + @TmfSignalHandler + public void synchToRange(final TmfRangeSynchSignal signal) { + if (signal.getSource() == this || fTrace == null) { + return; + } + if (signal.getCurrentRange().getIntersection(fTrace.getTimeRange()) == null) { + return; + } + final long startTime = signal.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + final long endTime = signal.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fTimeGraphWrapper.isDisposed()) { + return; + } + fTimeGraphWrapper.getTimeGraphViewer().setStartFinishTime(startTime, endTime); + startZoomThread(startTime, endTime); + } + }); + } + + /** + * @param signal the format of the timestamps was updated. + * @since 2.1 + */ + @TmfSignalHandler + public void updateTimeFormat( final TmfTimestampFormatUpdateSignal signal){ + fTimeGraphWrapper.refresh(); + } + + // ------------------------------------------------------------------------ + // Internal + // ------------------------------------------------------------------------ + + private void loadTrace() { + synchronized (fEntryListMap) { + fEntryList = fEntryListMap.get(fTrace); + if (fEntryList == null) { + rebuild(); + } else { + fStartTime = fTrace.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + fEndTime = fTrace.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + refresh(); + } + } + } + + /** + * Forces a rebuild of the entries list, even if entries already exist for this trace + * @since 3.0 + */ + protected void rebuild() { + setStartTime(Long.MAX_VALUE); + setEndTime(Long.MIN_VALUE); + synchronized (fBuildThreadMap) { + for (ITmfTrace trace : getTracesToBuild(fTrace)) { + BuildThread buildThread = new BuildThread(trace, fTrace, getName()); + fBuildThreadMap.put(trace, buildThread); + buildThread.start(); + } + } + } + + /** + * Method called when synching to a given timestamp. Inheriting classes can + * perform actions here to update the view at the given timestamp. + * + * @param time + * The currently selected time + */ + protected void synchingToTime(long time) { + + } + + /** + * Return the list of traces whose data or analysis results will be used to + * populate the view. By default, if the trace is an experiment, the traces + * under it will be returned, otherwise, the trace itself is returned. + * + * A build thread will be started for each trace returned by this method, + * some of which may receive events in live streaming mode. + * + * @param trace + * The trace associated with this view + * @return List of traces with data to display + * @since 3.0 + */ + protected Iterable<ITmfTrace> getTracesToBuild(ITmfTrace trace) { + return Arrays.asList(TmfTraceManager.getTraceSet(trace)); + } + + /** + * Build the entries list to show in this time graph + * + * Called from the BuildThread + * + * @param trace + * The trace being built + * @param parentTrace + * The parent of the trace set, or the trace itself + * @param monitor + * The progress monitor object + * @since 3.0 + */ + protected abstract void buildEventList(ITmfTrace trace, ITmfTrace parentTrace, IProgressMonitor monitor); + + /** + * Gets the list of event for an entry in a given timerange + * + * @param entry + * The entry to get events for + * @param startTime + * Start of the time range + * @param endTime + * End of the time range + * @param resolution + * The resolution + * @param monitor + * The progress monitor object + * @return The list of events for the entry + */ + protected abstract @Nullable List<ITimeEvent> getEventList(TimeGraphEntry entry, + long startTime, long endTime, long resolution, + IProgressMonitor monitor); + + /** + * Gets the list of links (displayed as arrows) for a trace in a given + * timerange. Default implementation returns an empty list. + * + * @param startTime + * Start of the time range + * @param endTime + * End of the time range + * @param resolution + * The resolution + * @param monitor + * The progress monitor object + * @return The list of link events + * @since 2.1 + */ + protected List<ILinkEvent> getLinkList(long startTime, long endTime, + long resolution, IProgressMonitor monitor) { + return new ArrayList<>(); + } + + + /** + * Refresh the display + */ + protected void refresh() { + TmfUiRefreshHandler.getInstance().queueUpdate(this, new Runnable() { + @Override + public void run() { + if (fTimeGraphWrapper.isDisposed()) { + return; + } + boolean hasEntries = false; + synchronized (fEntryListMap) { + fEntryList = fEntryListMap.get(fTrace); + if (fEntryList == null) { + fEntryList = new CopyOnWriteArrayList<>(); + } else if (fEntryComparator != null) { + List<TimeGraphEntry> list = new ArrayList<>(fEntryList); + Collections.sort(list, fEntryComparator); + fEntryList.clear(); + fEntryList.addAll(list); + } + hasEntries = fEntryList.size() != 0; + } + if (fEntryList != fTimeGraphWrapper.getInput()) { + fTimeGraphWrapper.setInput(fEntryList); + } else { + fTimeGraphWrapper.refresh(); + } + fTimeGraphWrapper.getTimeGraphViewer().setTimeBounds(fStartTime, fEndTime); + + long selectionBeginTime = fTrace == null ? 0 : fTraceManager.getSelectionBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long selectionEndTime = fTrace == null ? 0 : fTraceManager.getSelectionEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long startTime = fTrace == null ? 0 : fTraceManager.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + long endTime = fTrace == null ? 0 : fTraceManager.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); + startTime = Math.max(startTime, fStartTime); + endTime = Math.min(endTime, fEndTime); + fTimeGraphWrapper.getTimeGraphViewer().setSelectionRange(selectionBeginTime, selectionEndTime); + fTimeGraphWrapper.getTimeGraphViewer().setStartFinishTime(startTime, endTime); + + if (fTimeGraphWrapper instanceof TimeGraphComboWrapper && !fPackDone) { + for (TreeColumn column : ((TimeGraphComboWrapper) fTimeGraphWrapper).getTreeViewer().getTree().getColumns()) { + column.pack(); + } + if (hasEntries) { + fPackDone = true; + } + } + + startZoomThread(startTime, endTime); + } + }); + } + + /** + * Redraw the canvas + */ + protected void redraw() { + synchronized (fSyncObj) { + if (fRedrawState == State.IDLE) { + fRedrawState = State.BUSY; + } else { + fRedrawState = State.PENDING; + return; + } + } + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (fTimeGraphWrapper.isDisposed()) { + return; + } + fTimeGraphWrapper.redraw(); + fTimeGraphWrapper.update(); + synchronized (fSyncObj) { + if (fRedrawState == State.PENDING) { + fRedrawState = State.IDLE; + redraw(); + } else { + fRedrawState = State.IDLE; + } + } + } + }); + } + + private void startZoomThread(long startTime, long endTime) { + if (fZoomThread != null) { + fZoomThread.cancel(); + } + fZoomThread = new ZoomThread(fEntryList, startTime, endTime, getName()); + fZoomThread.start(); + } + + private void makeActions() { + fPreviousResourceAction = fTimeGraphWrapper.getTimeGraphViewer().getPreviousItemAction(); + fPreviousResourceAction.setText(getPrevText()); + fPreviousResourceAction.setToolTipText(getPrevTooltip()); + fNextResourceAction = fTimeGraphWrapper.getTimeGraphViewer().getNextItemAction(); + fNextResourceAction.setText(getNextText()); + fNextResourceAction.setToolTipText(getNextTooltip()); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + fillLocalToolBar(bars.getToolBarManager()); + } + + /** + * Add actions to local tool bar manager + * + * @param manager the tool bar manager + */ + protected void fillLocalToolBar(IToolBarManager manager) { + if (fTimeGraphWrapper instanceof TimeGraphComboWrapper) { + if (fFilterColumns != null && fFilterLabelProvider != null && fFilterColumns.length > 0) { + manager.add(((TimeGraphComboWrapper) fTimeGraphWrapper).getShowFilterAction()); + } + } + manager.add(fTimeGraphWrapper.getTimeGraphViewer().getShowLegendAction()); + manager.add(new Separator()); + manager.add(fTimeGraphWrapper.getTimeGraphViewer().getResetScaleAction()); + manager.add(fTimeGraphWrapper.getTimeGraphViewer().getPreviousEventAction()); + manager.add(fTimeGraphWrapper.getTimeGraphViewer().getNextEventAction()); + manager.add(fPreviousResourceAction); + manager.add(fNextResourceAction); + manager.add(fTimeGraphWrapper.getTimeGraphViewer().getZoomInAction()); + manager.add(fTimeGraphWrapper.getTimeGraphViewer().getZoomOutAction()); + manager.add(new Separator()); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/Messages.java new file mode 100644 index 0000000000..b301d7048f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/Messages.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2013 École Polytechnique de Montréal + * + * 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: + * Geneviève Bastien - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.timegraph; + +import org.eclipse.osgi.util.NLS; + +/** + * Generic messages for the bar charts + * + * @since 2.1 + */ +@SuppressWarnings("javadoc") +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.tmf.ui.views.timegraph.messages"; //$NON-NLS-1$ + + public static String AbstractTimeGraphtView_NextText; + public static String AbstractTimeGraphView_NextTooltip; + public static String AbstractTimeGraphView_PreviousText; + public static String AbstractTimeGraphView_PreviousTooltip; + public static String TimeGraphPresentationProvider_multipleStates; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/messages.properties new file mode 100644 index 0000000000..52adaa617b --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/messages.properties @@ -0,0 +1,16 @@ +############################################################################### +# Copyright (c) 2014 Ericsson +# +# 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: +# Ericsson - Initial API and implementation +############################################################################### +AbstractTimeGraphtView_NextText=Next +AbstractTimeGraphView_NextTooltip=Next element +AbstractTimeGraphView_PreviousText=Previous +AbstractTimeGraphView_PreviousTooltip=Previous element +TimeGraphPresentationProvider_multipleStates=multiple states diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/DiagramToolTip.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/DiagramToolTip.java new file mode 100755 index 0000000000..1301222e9f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/DiagramToolTip.java @@ -0,0 +1,131 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * <p> + * This class is used to reproduce the same tooltip behavior on Windows and Linux when the mouse hovers over the + * sequence diagram + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class DiagramToolTip { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + private static final int CHARACTERS_PER_COLUMN = 100; + private static final int DEFAULT_CURSOR_HEIGHT = 32; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The parent control where the tooltip must be drawn. + */ + private Control fParent = null; + /** + * The tooltip shell. + */ + private Shell fToolTipShell = null; + /** + * The text box. + */ + private Text fTextBox = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Create a new tooltip for the given parent control + * + * @param parent the parent control. + */ + public DiagramToolTip(Control parent) { + fParent = parent; + fToolTipShell = new Shell(fParent.getShell(), SWT.MULTI); + fToolTipShell.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + fTextBox = new Text(fToolTipShell, SWT.WRAP | SWT.MULTI); + fTextBox.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Display the tooltip using the given text The tooltip will stay on screen until it is told otherwise + * + * @param value the text to display + */ + public void showToolTip(String value) { + if ((value == null) || (value.equalsIgnoreCase(""))) { //$NON-NLS-1$ + fToolTipShell.setVisible(false); + return; + } + + int w = fToolTipShell.getBounds().width; + Point hr = Display.getDefault().getCursorLocation(); + int cursorH = DEFAULT_CURSOR_HEIGHT; + for (int i = 0; i < Display.getDefault().getCursorSizes().length; i++) { + if (Display.getDefault().getCursorSizes()[i].y < cursorH) { + cursorH = Display.getDefault().getCursorSizes()[i].y; + } + } + if (hr.x + w > Display.getDefault().getBounds().width) { + int tempX = (hr.x + w) - Display.getDefault().getBounds().width; + if (tempX > Display.getDefault().getBounds().width) { + hr.x = 0; + } + hr.x = hr.x - tempX; + } + fTextBox.setText(value); + GC gc = new GC(fTextBox); + FontMetrics fm = gc.getFontMetrics(); + gc.dispose(); + int width = CHARACTERS_PER_COLUMN * fm.getAverageCharWidth(); + fTextBox.setSize(fTextBox.computeSize(width, fTextBox.getLineCount() * fTextBox.getLineHeight())); + fToolTipShell.setLocation(hr.x, hr.y + cursorH); + fToolTipShell.setSize(fTextBox.getSize()); + fTextBox.setVisible(true); + fToolTipShell.setVisible(true); + } + + /** + * Hide the tooltip + */ + public void hideToolTip() { + fToolTipShell.setVisible(false); + } + + /** + * @return parent control + * @since 2.0 + */ + protected Control getParent() { + return fParent; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/DrawableToolTip.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/DrawableToolTip.java new file mode 100755 index 0000000000..d06eb52d69 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/DrawableToolTip.java @@ -0,0 +1,305 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * <p> + * This class is used to reproduce the same tooltip behavior on Windows and Linux when the mouse move hover the time + * compression bar used to display elapsed time using a tooltip. The tooltip is composed of 2 parts, the text value and + * below, a scale to visually locate the value in a time range (usually the minimum an maximum elapsed time in the whole + * diagram) + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class DrawableToolTip implements PaintListener { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + private static final int HORIZONTAL_MARGIN = 10; + private static final int VERTICAL_MARGIN = 10; + private static final int TEXT_SCALE_MARGIN = 20; + private static final int SCALE_LENGTH = 100; + private static final int SHELL_WIDTH = 200; + private static final int SHELL_HEIGHT = 50; + private static final int NUMBER_STEPS = 10; + private static final int BASE_RED_VALUE = 255; + private static final int BASE_GREEN_BLUE_VALUE = 225; + private static final int COLOR_STEP = 25; + private static final int BOUNDS_Y_OFFSET = 26; + private static final int RECTANGLE_HEIGHT = 11; + private static final int DEFAULT_LINE_WIDTH = 10; + private static final int BORDER_LINE_WIDTH = 14; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The tooltip shell + */ + private Shell fToolTipShell = null; + /** + * The Time range data. + */ + private TmfTimeRange fMinMaxRange; + /** + * The current time. + */ + private ITmfTimestamp fCurrentValue; + /** + * The horizontal margin used for drawing. + */ + private static int fHorMargin = HORIZONTAL_MARGIN; + /** + * The vertical margin used for drawing. + */ + private static int fVertMargin = VERTICAL_MARGIN; + /** + * The minimum text scale margin. + */ + private static int fTextScaleMargin = TEXT_SCALE_MARGIN; + /** + * The length of the text scale. + */ + private static int fScaleLength = SCALE_LENGTH; + /** + * The text to display + */ + private String fMessage; + /** + * The color array used to represent the 10 time range slices + */ + private Color[] fColors; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Creates a drawable tool tip instance. + * + * @param parent The parent composite. + */ + public DrawableToolTip(Composite parent) { + fToolTipShell = new Shell(parent.getShell(), SWT.ON_TOP); + fToolTipShell.setLayout(new RowLayout()); + fToolTipShell.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + fToolTipShell.addPaintListener(this); + fToolTipShell.setSize(SHELL_WIDTH, SHELL_HEIGHT); + + fColors = new Color[NUMBER_STEPS]; + int greenBlue = BASE_GREEN_BLUE_VALUE; + final int step = COLOR_STEP; + for (int i = 0; i < fColors.length; i++) { + fColors[i] = new Color(Display.getDefault(), BASE_RED_VALUE, greenBlue, greenBlue); + greenBlue -= step; + } + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + /** + * Returns the message to display. + * + * @return the message to display. + */ + public String getText() { + return fMessage; + } + + /** + * Returns teh current time to display. + * + * @return the current time to display + */ + public String getAccessibleText() { + return fCurrentValue.toString(); + } + + /** + * Returns the horizontal margin. + * + * @return the horizontal margin. + */ + protected static int getHorizontalMargin() { + return fHorMargin; + } + + /** + * Sets the horizontal margin. + * + * @param margin The margin to set. + */ + protected static void setHorizontalMargin(int margin) { + fHorMargin = margin; + } + + /** + * Returns the vertical margin. + * + * @return the vertical margin. + */ + protected static int getVerticalMargin() { + return fVertMargin; + } + + /** + * Sets the vertical margin. + * + * @param margin The margin to set. + */ + protected static void setVerticalMargin(int margin) { + fVertMargin = margin; + } + + /** + * Returns the text scale margin. + * + * @return the text scale margin. + */ + protected static int getTextScaleMargin() { + return fTextScaleMargin; + } + + /** + * Sets the text scale margin. + * @param textScaleMargin The margin to set. + */ + protected static void setTestScaleMargin(int textScaleMargin) { + fTextScaleMargin = textScaleMargin; + } + + /** + * Returns the scale length. + * + * @return the scale length. + */ + protected static int getScaleLength() { + return fScaleLength; + } + + /** + * Sets the scale length. + * + * @param scaleLength The scale length to set. + */ + protected static void setScaleLength(int scaleLength) { + fScaleLength = scaleLength; + } + + /** + * Display the tooltip using the given time range(min,max) the current value and the time unit The tooltip will stay + * on screen until it is told otherwise + * + * @param value the current in the scale + * @param min the scale min + * @param max the scale max + * @since 2.0 + */ + public void showToolTip(ITmfTimestamp value, ITmfTimestamp min, ITmfTimestamp max) { + fMinMaxRange = new TmfTimeRange(min, max); + fCurrentValue = value; + + int w = fToolTipShell.getBounds().width; + int h = fToolTipShell.getBounds().height; + Point hr = Display.getDefault().getCursorLocation(); + fToolTipShell.setBounds(hr.x, hr.y + BOUNDS_Y_OFFSET, w, h); + fToolTipShell.setVisible(true); + } + + /** + * Hide the tooltip. + */ + public void hideToolTip() { + fToolTipShell.setVisible(false); + } + + /** + * Disposes the system resource used by this kind of toolTips (a colors array essentially) + */ + public void dispose() { + for (int i = 0; i < fColors.length; i++) { + fColors[i].dispose(); + } + } + + @Override + public void paintControl(PaintEvent event) { + fMessage = Messages.SequenceDiagram_Delta + " " + fCurrentValue.toString(); //$NON-NLS-1$ + Point size = event.gc.textExtent(fMessage); + if (size.x < fScaleLength) { + size.x = fScaleLength; + } + event.gc.drawText(fMessage, fHorMargin, fVertMargin, true); + event.gc.drawLine(fHorMargin, fVertMargin + fTextScaleMargin + size.y, fHorMargin + fScaleLength, fVertMargin + fTextScaleMargin + size.y); + + int step = fScaleLength / NUMBER_STEPS; + + ITmfTimestamp minMaxdelta = fMinMaxRange.getEndTime().getDelta(fMinMaxRange.getStartTime()); + double gr = (minMaxdelta.getValue()) / (double) NUMBER_STEPS; + + ITmfTimestamp delta = fCurrentValue.getDelta(fMinMaxRange.getStartTime()); + long absDelta = Math.abs(delta.getValue()); + + int colIndex = 0; + if (gr != 0) { + colIndex = Math.round((float) (absDelta / gr)); + if (colIndex > fColors.length) { + colIndex = fColors.length; + } else if (colIndex <= 1) { + colIndex = 1; + } + } else { + colIndex = 1; + } + + for (int i = 0; i <= NUMBER_STEPS; i++) { + if (i < NUMBER_STEPS) { + event.gc.setBackground(fColors[i]); + } + if ((i < colIndex) && (i < NUMBER_STEPS)) { + event.gc.fillRectangle(fHorMargin + i * step, fVertMargin + fTextScaleMargin + size.y - 5, step, RECTANGLE_HEIGHT); + } + if (i == 0) { + event.gc.drawText(Messages.SequenceDiagram_Min, fHorMargin, size.y + 2 * fVertMargin + fTextScaleMargin, true); + } + if (i == 0) { + int len = event.gc.textExtent(Messages.SequenceDiagram_Max).x; + event.gc.drawText(Messages.SequenceDiagram_Max, fHorMargin + fScaleLength - len + 1, size.y + 2 * fVertMargin + fTextScaleMargin, true); + } + int lineWidth = DEFAULT_LINE_WIDTH; + if ((i == 0) || (i == NUMBER_STEPS)) { + lineWidth = BORDER_LINE_WIDTH; + } + event.gc.drawLine(fHorMargin + i * step, fVertMargin + fTextScaleMargin + size.y - lineWidth / 2, fHorMargin + i * step, fVertMargin + fTextScaleMargin + size.y + lineWidth / 2); + } + fToolTipShell.setSize(size.x + 2 * fHorMargin + 2, 2 * size.y + 3 * fVertMargin + fTextScaleMargin); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/ITimeCompressionListener.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/ITimeCompressionListener.java new file mode 100755 index 0000000000..e6506c8bdb --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/ITimeCompressionListener.java @@ -0,0 +1,42 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; + +/** + * <p> + * Listener interface for the time compression bar to notify about dela selection. + * </p> + * + * @author sveyrier + * @version 1.0 + */ +public interface ITimeCompressionListener { + + /** + * Notifies listeners about a selected delta. + * + * @param lifeline + * The current lifeline. + * @param startEvent + * The start event selected. + * @param nbEvent + * A number of events. + * @param color + * The current color to use. + */ + void deltaSelected(Lifeline lifeline, int startEvent, int nbEvent, IColor color); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/NGC.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/NGC.java new file mode 100755 index 0000000000..a686868929 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/NGC.java @@ -0,0 +1,988 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IFont; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IImage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.impl.ColorImpl; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * <p> + * This class implements the graphical context for the sequence diagram widgets. + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class NGC implements IGC { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The graphical context. + */ + private GC fContext; + /** + * The reference to the sequence diagram view. + */ + private SDWidget fView; + /** + * A reference to the last used font. + */ + private Font fTempFont = null; + /** + * The color of the gradient. + */ + private IColor fGradientColor = null; + /** + * The color of the background. + */ + private IColor fBackground = null; + /** + * The color of the foreground. + */ + private IColor fForeground = null; + /** + * The current visible y screen bounds + */ + private int fVisibleY; + /** + * The current visible x screen bound. + */ + private int fVisibleX; + /** + * The current yx value (view visible height - visible screen bounds) + */ + private int yx; + /** + * The current xx value (view visible width - visible screen bounds) + */ + private int xx; + /** + * <code>true</code> to draw with focus else <code>false</code>. + */ + private boolean fDrawWithFocus = false; + + /** + * The static visible screen bounds. + */ + private static int fVisibleScreenBounds = 0; + + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor. + * + * @param scrollView A sequence diagram view reference. + * @param gc A graphical context. + */ + public NGC(SDWidget scrollView, GC gc) { + fContext = gc; + fView = scrollView; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void setLineStyle(int style) { + fContext.setLineStyle(style); + } + + @Override + public int getLineStyle() { + return fContext.getLineStyle(); + } + + @Override + public int getContentsX() { + return Math.round(fView.getContentsX() / fView.getZoomValue()); + } + + @Override + public int getContentsY() { + return Math.round(fView.getContentsY() / fView.getZoomValue()); + } + + @Override + public int getVisibleWidth() { + return Math.round(fView.getVisibleWidth() / fView.getZoomValue()); + } + + @Override + public int getVisibleHeight() { + return Math.round(fView.getVisibleHeight() / fView.getZoomValue()); + } + + /** + * Returns the current visible y screen bounds. + * + * @return the current visible y screen bounds + * @since 2.0 + */ + protected int getVisibleY() { + return fVisibleY; + } + + /** + * Sets the current visible y screen bounds. + * + * @param visibleY + * the current visible y screen bounds + * @since 2.0 + */ + protected void setVisibleY(int visibleY) { + fVisibleY = visibleY; + } + + /** + * Returns the current visible x screen bound. + * + * @return the current visible x screen bound. + * @since 2.0 + * + */ + protected int getfVisibleX() { + return fVisibleX; + } + + /** + * Sets the current visible x screen bound. + * + * @param visibleX + * the current visible x screen bound. + * @since 2.0 + * + */ + protected void setVisibleX(int visibleX) { + fVisibleX = visibleX; + } + + /** + * Returns current yx value (view visible height - visible screen bounds). + * + * @return current yx value + * @since 2.0 + */ + protected int getYx() { + return yx; + } + + /** + * Sets current yx value (view visible height - visible screen bounds). + * + * @param yx + * current yx value + * @since 2.0 + */ + protected void setYx(int yx) { + this.yx = yx; + } + + /** + * Returns the current xx value (view visible width - visible screen bounds) + * + * @return the current xx value + * @since 2.0 + */ + protected int getXx() { + return xx; + } + + /** + * Sets the current xx value (view visible width - visible screen bounds) + * + * @param xx + * the current xx value + * @since 2.0 + */ + protected void setXx(int xx) { + this.xx = xx; + } + + @Override + public int contentsToViewX(int x) { + return fView.contentsToViewX(x); + } + + @Override + public int contentsToViewY(int y) { + return fView.contentsToViewY(y); + } + + /** + * Get code for drawings at given x and y position. + * + * @param x The x position + * @param y The y position. + * @return A code for top, bottom, right and left + */ + protected byte code(int x, int y) { + byte c = 0; + fVisibleY = fVisibleScreenBounds; + fVisibleX = fVisibleScreenBounds; + yx = fView.getVisibleHeight() + fVisibleScreenBounds; + xx = fView.getVisibleWidth() + fVisibleScreenBounds; + if (y > yx) { + c |= 0x01; // top + } else if (y < fVisibleY) { + c |= 0x02; // bottom + } + + if (x > xx) { + c |= 0x04; // right + } else if (x < fVisibleX) { + c |= 0x08; // left + } + return c; + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + int localX1 = x1; + int localY1 = y1; + int localX2 = x2; + int localY2 = y2; + + localX1 = Math.round(localX1 * fView.getZoomValue()); + localY1 = Math.round(localY1 * fView.getZoomValue()); + localX2 = Math.round(localX2 * fView.getZoomValue()); + localY2 = Math.round(localY2 * fView.getZoomValue()); + localX1 = fView.contentsToViewX(localX1); + localY1 = fView.contentsToViewY(localY1); + localX2 = fView.contentsToViewX(localX2); + localY2 = fView.contentsToViewY(localY2); + + byte code1 = code(localX1, localY1); + byte code2 = code(localX2, localY2); + byte codex; + boolean draw = false; + boolean end = false; + int x = 0, y = 0; + + do { + if (code1 == 0 && code2 == 0) { + draw = true; + end = true; + } else if ((code1 & code2) != 0) { + end = true; + } else { + codex = (code1 != 0) ? code1 : code2; + if ((codex & 0x01) != 0) { // top + x = localX1 + ((localX2 - localX1) * (yx - localY1)) / (localY2 - localY1); + y = yx; + } else if ((codex & 0x02) != 0) { // bottom + x = localX1 + ((localX2 - localX1) * (fVisibleY - localY1)) / (localY2 - localY1); + y = fVisibleY; + } else if ((codex & 0x04) != 0) { // right + y = localY1 + ((localY2 - localY1) * (xx - localX1)) / (localX2 - localX1); + x = xx; + } else if ((codex & 0x08) != 0) { // left + y = localY1 + ((localY2 - localY1) * (fVisibleX - localX1)) / (localX2 - localX1); + x = fVisibleX; + } + + if (codex == code1) { + localX1 = x; + localY1 = y; + code1 = code(localX1, localY1); + } else { + localX2 = x; + localY2 = y; + code2 = code(localX2, localY2); + } + } + } while (!end); + + if (draw) { + fContext.drawLine(localX1, localY1, localX2, localY2); + } + } + + @Override + public void drawRectangle(int x, int y, int width, int height) { + int localX = x; + int localY = y; + int localWidth = width; + int localHeight = height; + + localX = Math.round(localX * fView.getZoomValue()); + // Workaround to avoid problems for some special cases (not very nice) + if (localY != getContentsY()) { + localY = Math.round(localY * fView.getZoomValue()); + localY = fView.contentsToViewY(localY); + } else { + localY = 0; + } + localWidth = Math.round(localWidth * fView.getZoomValue()); + localHeight = Math.round(localHeight * fView.getZoomValue()); + localX = fView.contentsToViewX(localX); + + if (localX < -fVisibleScreenBounds) { + localWidth = localWidth + localX + fVisibleScreenBounds; + localX = -fVisibleScreenBounds; + } + if (localY < -fVisibleScreenBounds) { + localHeight = localHeight + localY + fVisibleScreenBounds; + localY = -fVisibleScreenBounds; + } + if ((localWidth < -fVisibleScreenBounds) && (localX + localWidth < -fVisibleScreenBounds)) { + localWidth = -fVisibleScreenBounds; + } else if (localWidth + localX > fView.getVisibleWidth() + fVisibleScreenBounds) { + localWidth = fView.getVisibleWidth() + fVisibleScreenBounds - localX; + } + if ((localHeight < -fVisibleScreenBounds) && (localY + localHeight < -fVisibleScreenBounds)) { + localHeight = -fVisibleScreenBounds; + } else if (localHeight + localY > fView.getVisibleHeight() + fVisibleScreenBounds) { + localHeight = fView.getVisibleHeight() + fVisibleScreenBounds - localY; + } + fContext.drawRectangle(localX, localY, localWidth, localHeight); + } + + @Override + public void drawFocus(int x, int y, int width, int height) { + int localX = x; + int localY = y; + int localWidth = width; + int localHeight = height; + + IColor bC = getBackground(); + IColor fC = getForeground(); + + if (localWidth < 0) { + localX = localX + localWidth; + localWidth = -localWidth; + } + + if (localHeight < 0) { + localY = localY + localHeight; + localHeight = -localHeight; + } + + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + localWidth = Math.round(localWidth * fView.getZoomValue()); + localHeight = Math.round(localHeight * fView.getZoomValue()); + + setForeground(SDViewPref.getInstance().getForeGroundColorSelection()); + setBackground(SDViewPref.getInstance().getBackGroundColorSelection()); + + fContext.drawFocus(fView.contentsToViewX(localX - 1), fView.contentsToViewY(localY - 1), localWidth + 3, localHeight + 3); + + setBackground(bC); + setForeground(fC); + } + + @Override + public void fillPolygon(int[] points) { + int len = (points.length / 2) * 2; + int[] localPoint = new int[len]; + for (int i = 0; i < len; i++) { + localPoint[i] = fView.contentsToViewX(Math.round(points[i] * fView.getZoomValue())); + i++; + localPoint[i] = fView.contentsToViewY(Math.round(points[i] * fView.getZoomValue())); + } + + if (validatePolygonHeight(localPoint) <= 0) { + return; + } + + fContext.fillPolygon(localPoint); + } + + @Override + public void drawPolygon(int[] points) { + int len = (points.length / 2) * 2; + int[] localPoint = new int[len]; + for (int i = 0; i < len; i++) { + localPoint[i] = fView.contentsToViewX(Math.round(points[i] * fView.getZoomValue())); + i++; + localPoint[i] = fView.contentsToViewY(Math.round(points[i] * fView.getZoomValue())); + } + + if (validatePolygonHeight(localPoint) <= 0) { + return; + } + + fContext.drawPolygon(localPoint); + } + + @Override + public void fillRectangle(int x, int y, int width, int height) { + int localX = x; + int localY = y; + int localWidth = width; + int localHeight = height; + + localX = Math.round(localX * fView.getZoomValue()); + // Workaround to avoid problems for some special cases (not very nice) + if (localY != getContentsY()) { + localY = Math.round(localY * fView.getZoomValue()); + localY = fView.contentsToViewY(localY) + 1; + } else { + localY = 1; + } + localWidth = Math.round(localWidth * fView.getZoomValue()) - 1; + localHeight = Math.round(localHeight * fView.getZoomValue()) - 1; + localX = fView.contentsToViewX(localX) + 1; + if (localX < -fVisibleScreenBounds) { + localWidth = localWidth + localX + fVisibleScreenBounds; + localX = -fVisibleScreenBounds; + } + if (localY < -fVisibleScreenBounds) { + localHeight = localHeight + localY + fVisibleScreenBounds; + localY = -fVisibleScreenBounds; + } + if ((localWidth < -fVisibleScreenBounds) && (localX + localWidth < -fVisibleScreenBounds)) { + localWidth = -fVisibleScreenBounds; + } else if (localWidth + localX > fView.getVisibleWidth() + fVisibleScreenBounds) { + localWidth = fView.getVisibleWidth() + fVisibleScreenBounds - localX; + } + if ((localHeight < -fVisibleScreenBounds) && (localY + localHeight < -fVisibleScreenBounds)) { + localHeight = -fVisibleScreenBounds; + } else if (localHeight + localY > fView.getVisibleHeight() + fVisibleScreenBounds) { + localHeight = fView.getVisibleHeight() + fVisibleScreenBounds - localY; + } + fContext.fillRectangle(localX, localY, localWidth, localHeight); + } + + @Override + public void fillGradientRectangle(int x, int y, int width, int height, boolean isVertical) { + int localX = x; + int localY = y; + int localWidth = width; + int localHeight = height; + + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + localWidth = Math.round(localWidth * fView.getZoomValue()); + localHeight = Math.round(localHeight * fView.getZoomValue()); + IColor tempColor = fForeground; + setForeground(fGradientColor); + localX = fView.contentsToViewX(localX); + localY = fView.contentsToViewY(localY); + + if (localX < -fVisibleScreenBounds) { + localWidth = localWidth + localX + fVisibleScreenBounds; + localX = -fVisibleScreenBounds; + } + if (localY < -fVisibleScreenBounds) { + localHeight = localHeight + localY + fVisibleScreenBounds; + localY = -fVisibleScreenBounds; + } + + if ((localWidth < -fVisibleScreenBounds) && (localX + localWidth < -fVisibleScreenBounds)) { + localWidth = -fVisibleScreenBounds; + } else if (localWidth + localX > fView.getVisibleWidth() + fVisibleScreenBounds) { + localWidth = fView.getVisibleWidth() + fVisibleScreenBounds - localX; + } + if ((localHeight < -fVisibleScreenBounds) && (localY + localHeight < -fVisibleScreenBounds)) { + localHeight = -fVisibleScreenBounds; + } else if (localHeight + localY > fView.getVisibleHeight() + fVisibleScreenBounds) { + localHeight = fView.getVisibleHeight() + fVisibleScreenBounds - localY; + } + if (isVertical) { + fContext.fillGradientRectangle(localX, localY, localWidth, localHeight, isVertical); + } + else { + fContext.fillGradientRectangle(localX + localWidth, localY, -localWidth, localHeight + 1, isVertical); + } + setForeground(tempColor); + } + + @Override + public int textExtent(String name) { + return fContext.textExtent(name).x; + } + + @Override + public void drawText(String string, int x, int y, boolean isTrans) { + int localX = x; + int localY = y; + + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + fContext.drawText(string, fView.contentsToViewX(localX), fView.contentsToViewY(localY), isTrans); + if (fDrawWithFocus) { + Point r = fContext.textExtent(string); + fContext.drawFocus(localX - 1, localY - 1, r.x + 2, r.y + 2); + } + } + + @Override + public void drawText(String string, int x, int y) { + int localX = x; + int localY = y; + + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + fContext.drawText(string, fView.contentsToViewX(localX), fView.contentsToViewY(localY), true); + if (fDrawWithFocus) { + Point r = fContext.textExtent(string); + fContext.drawFocus(localX - 1, localY - 1, r.x + 2, r.y + 2); + } + } + + @Override + public void fillOval(int x, int y, int width, int height) { + int localX = x; + int localY = y; + int localWidth = width; + int localHeight = height; + + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + localWidth = Math.round(localWidth * fView.getZoomValue()); + localHeight = Math.round(localHeight * fView.getZoomValue()); + fContext.fillOval(fView.contentsToViewX(localX), fView.contentsToViewY(localY), localWidth, localHeight); + } + + @Override + public IColor getBackground() { + if ((fBackground != null) && (fBackground.getColor() instanceof Color) && (!((Color) (fBackground.getColor())).isDisposed())) { + return fBackground; + } + return ColorImpl.getSystemColor(SWT.COLOR_WHITE); + } + + @Override + public IColor getForeground() { + if ((fForeground != null) && (fForeground.getColor() instanceof Color) && (!((Color) (fForeground.getColor())).isDisposed())) { + return fForeground; + } + return ColorImpl.getSystemColor(SWT.COLOR_WHITE); + } + + @Override + public void setBackground(IColor color) { + if (color == null) { + return; + } + if (color.getColor() instanceof Color) { + fContext.setBackground((Color) color.getColor()); + fBackground = color; + } + } + + @Override + public void setForeground(IColor color) { + if (color == null) { + return; + } + if (color.getColor() instanceof Color) { + Color c = (Color) color.getColor(); + if (!c.isDisposed()) { + fContext.setForeground(c); + fForeground = color; + } + } + } + + @Override + public void setGradientColor(IColor color) { + if (color == null) { + return; + } + if (color.getColor() instanceof Color) { + fGradientColor = color; + } + } + + @Override + public void setLineWidth(int width) { + if (fView.isPrinting()) { + fContext.setLineWidth(width * 2); + } + else { + fContext.setLineWidth(width); + } + } + + @Override + public int getLineWidth() { + return fContext.getLineWidth(); + } + + /** + * Method to draw a text in rectangle. (Linux GTK Workaround) + * + * @param string The text to draw. + * @param x The x position. + * @param y The y position. + * @param isTransparent true for transparent else false + */ + protected void localDrawText(String string, int x, int y, boolean isTransparent) { + Point r = fContext.textExtent(string); + if (!isTransparent) { + fContext.fillRectangle(x, y, r.x, r.y); + } + fContext.drawText(string, x, y, isTransparent); + if ((fDrawWithFocus) && (string.length() > 1)) { + fContext.drawFocus(x - 1, y - 1, r.x + 2, r.y + 2); + } + } + + @Override + public void drawTextTruncatedCentred(String name, int xValue, int yValue, int width, int height, boolean trans) { + int localX = xValue; + int localY = yValue; + int localWidth = width; + int localHeight = height; + + Point tx = fContext.textExtent(name); + localX = Math.round(localX * fView.getZoomValue()); + int y = 0; + // Workaround to avoid round problems for some special cases (not very nice) + if (localY != getContentsY()) { + localY = Math.round(localY * fView.getZoomValue()); + y = fView.contentsToViewY(localY); + } + localWidth = Math.round(localWidth * fView.getZoomValue()); + localHeight = Math.round(localHeight * fView.getZoomValue()); + int x = fView.contentsToViewX(localX); + if (tx.y > localHeight) { + return; + } + + // Adjust height and y + if (y < -fVisibleScreenBounds) { + localHeight = localHeight + y + fVisibleScreenBounds; + y = -fVisibleScreenBounds; + } + if ((localHeight < -fVisibleScreenBounds) && (y + localHeight < -fVisibleScreenBounds)) { + localHeight = -fVisibleScreenBounds; + } else if (localHeight + y > fView.getVisibleHeight() + fVisibleScreenBounds) { + localHeight = fView.getVisibleHeight() + fVisibleScreenBounds - y; + } + + if (tx.x <= localWidth) { + localDrawText(name, x + 1 + (localWidth - tx.x) / 2, y + 1 + (localHeight - tx.y) / 2, trans); + } else { + String nameToDisplay = name; + for (int i = name.length() - 1; i >= 0 && fContext.textExtent(nameToDisplay).x >= localWidth; i--) { + nameToDisplay = name.substring(0, i); + } + int dotCount = 0; + for (int i = 1; i <= 3 && nameToDisplay.length() - i > 0; i++) { + dotCount++; + } + nameToDisplay = nameToDisplay.substring(0, nameToDisplay.length() - dotCount); + StringBuffer buf = new StringBuffer(nameToDisplay); + for (int i = 0; i < dotCount; i++) { + buf.append("."); //$NON-NLS-1$ + } + nameToDisplay = buf.toString(); + localDrawText(nameToDisplay, x + 1 + (localWidth - fContext.textExtent(nameToDisplay).x) / 2, y + 1 + (localHeight - fContext.textExtent(nameToDisplay).y) / 2, trans); + } + } + + @Override + public void drawTextTruncated(String name, int xValue, int yValue, int width, int height, boolean trans) { + int localX = xValue; + int localY = yValue; + int localWidth = width; + int localHeight = height; + + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + localWidth = Math.round(localWidth * fView.getZoomValue()); + localHeight = Math.round(localHeight * fView.getZoomValue()); + int x = fView.contentsToViewX(localX); + int y = fView.contentsToViewY(localY); + if (fContext.textExtent(name).x <= localWidth) { + localDrawText(name, x + 1, y + 1 + localHeight, trans); + } else { + String nameToDisplay = name; + for (int i = name.length() - 1; i >= 0 && fContext.textExtent(nameToDisplay).x >= localWidth; i--) { + nameToDisplay = name.substring(0, i); + } + int dotCount = 0; + for (int i = 1; i <= 3 && nameToDisplay.length() - i > 0; i++) { + dotCount++; + } + nameToDisplay = nameToDisplay.substring(0, nameToDisplay.length() - dotCount); + + StringBuffer buf = new StringBuffer(nameToDisplay); + + for (int i = 0; i < dotCount; i++) { + buf.append("."); //$NON-NLS-1$ + } + nameToDisplay = buf.toString(); + localDrawText(nameToDisplay, x + 1, y + 1 + localHeight, trans); + } + } + + @Override + public void drawImage(IImage image, int xValue, int yValue, int maxWith, int maxHeight) { + int localX = xValue; + int localY = yValue; + + Image img = null; + if (image != null && image.getImage() instanceof Image) { + img = (Image) image.getImage(); + } else { + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + int x = fView.contentsToViewX(localX); + int y = fView.contentsToViewY(localY); + float tempZoom = fView.getZoomValue(); + int width = Math.round(maxWith * tempZoom); + int height = Math.round(maxHeight * tempZoom); + fContext.setBackground(fView.getDisplay().getSystemColor(SWT.COLOR_RED)); + fContext.fillRectangle(x, y, width, height); + return; + } + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + int x = fView.contentsToViewX(localX); + int y = fView.contentsToViewY(localY); + Rectangle b = ((Image) image.getImage()).getBounds(); + int width = b.width; + int height = b.height; + if (width > maxWith) { + width = maxWith; + } + if (height > maxHeight) { + height = maxHeight; + } + float tempZoom = fView.getZoomValue(); + width = Math.round(width * tempZoom); + height = Math.round(height * tempZoom); + + if (fView.isPrinting() && width > 0 && height > 0) { + Image dbuffer = new Image(fView.getDisplay(), width, height); + GC tempgc = new GC(dbuffer); + tempgc.drawImage(img, 0, 0, b.width, b.height, 0, 0, width, height); + Image dbuffer2 = new Image(fView.getDisplay(), dbuffer.getImageData()); + fContext.drawImage(dbuffer2, x, y); + tempgc.dispose(); + dbuffer.dispose(); + dbuffer2.dispose(); + } else { + fContext.drawImage(img, 0, 0, b.width, b.height, x, y, width, height); + } + } + + @Override + public void drawArc(int x, int y, int width, int height, int startAngle, int endAngle) { + int localX = x; + int localY = y; + int localWidth = width; + int localHeight = height; + + localX = Math.round(localX * fView.getZoomValue()); + localY = Math.round(localY * fView.getZoomValue()); + localWidth = Math.round(localWidth * fView.getZoomValue()); + localHeight = Math.round(localHeight * fView.getZoomValue()); + if (localWidth == 0 || localHeight == 0 || endAngle == 0) { + return; + } + fContext.drawArc(fView.contentsToViewX(localX), fView.contentsToViewY(localY), localWidth, localHeight, startAngle, endAngle); + } + + @Override + public void setFont(IFont font) { + if (font.getFont() != null && ((Font) font.getFont()).getFontData().length > 0) { + FontData fontData = ((Font) font.getFont()).getFontData()[0]; + if (SDViewPref.getInstance().fontLinked() || fView.isPrinting()) { + int h = Math.round(fontData.getHeight() * fView.getZoomValue()); + if (h > 0) { + fontData.setHeight(h); + } + } + if (fTempFont != null) { + fTempFont.dispose(); + } + fTempFont = new Font(Display.getCurrent(), fontData); + fContext.setFont(fTempFont); + } + } + + @Override + public int getFontHeight(IFont font) { + if (font.getFont() != null && (font.getFont() instanceof Font) && ((Font) font.getFont()).getFontData().length > 0) { + Font toRestore = fContext.getFont(); + fContext.setFont((Font) font.getFont()); + int height = fContext.textExtent("lp").y;//$NON-NLS-1$ + fContext.setFont(toRestore); + return height; + } + return 0; + } + + /** + * Returns the current font height. + * + * @return the current font height. + */ + protected int getCurrentFontHeight() { + return fContext.textExtent("lp").y; //$NON-NLS-1$ + } + + @Override + public int getFontWidth(IFont font) { + if ((font.getFont() != null) && (font.getFont() instanceof Font)) { + Font toRestore = fContext.getFont(); + fContext.setFont((Font) font.getFont()); + int width = fContext.getFontMetrics().getAverageCharWidth(); + fContext.setFont(toRestore); + return width; + } + return 0; + } + + /** + * Disposes all created resources. + */ + public void dispose() { + if (fTempFont != null) { + fTempFont.dispose(); + } + fTempFont = null; + if (fContext != null) { + fContext.dispose(); + } + fContext = null; + } + + @Override + public float getZoom() { + if (fView != null) { + return fView.getZoomValue(); + } + return 1; + } + + @Override + public int getLineDotStyle() { + return SWT.LINE_DOT; + } + + @Override + public int getLineDashStyle() { + return SWT.LINE_DASH; + } + + @Override + public int getLineSolidStyle() { + return SWT.LINE_SOLID; + } + + @Override + public IColor createColor(int r, int g, int b) { + return new ColorImpl(Display.getDefault(), r, g, b); + } + + @Override + public void setDrawTextWithFocusStyle(boolean focus) { + fDrawWithFocus = focus; + } + + /** + * Returns the screen bounds. + * + * @return the screen bounds. + */ + protected static int getVscreenBounds() { + return fVisibleScreenBounds; + } + + /** + * Sets the visible screen bounds. + * + * @param vBounds the screen bounds. + */ + protected static void setVscreenBounds(int vBounds) { + fVisibleScreenBounds = vBounds; + } + + /** + * Returns the graphical context. + * + * @return the graphical context + * @since 2.0 + */ + protected GC getGc() { + return fContext; + } + + /** + * Returns the SD widget. + * + * @return the SD widget + * @since 2.0 + */ + protected SDWidget getSDWidget() { + return fView; + } + + /** + * Returns the gradient color. + * + * @return the gradient color + * @since 2.0 + */ + protected IColor setGradientColor() { + return fGradientColor; + } + + // ------------------------------------------------------------------------ + // Helper methods + // ------------------------------------------------------------------------ + + /** + * Validates the polygon height + * + * @param localPoint array of points + * @return height + */ + private int validatePolygonHeight(int[] localPoint) { + int i = 1; + int max = 0; + int min = Integer.MAX_VALUE; + while (i < localPoint.length) { + max = Math.abs(localPoint[i]) > Math.abs(max) ? localPoint[i] : max; + min = Math.abs(localPoint[i]) < Math.abs(min) ? localPoint[i] : min; + i+=2; + } + int height = max - min; + if (min < -fVisibleScreenBounds) { + height = height + min + fVisibleScreenBounds; + min = -fVisibleScreenBounds; + } + if ((height < -fVisibleScreenBounds) && (min + height < -fVisibleScreenBounds)) { + height = -fVisibleScreenBounds; + } else if (height + min > fView.getVisibleHeight() + fVisibleScreenBounds) { + height = fView.getVisibleHeight() + fVisibleScreenBounds - min; + } + return height; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDView.java new file mode 100644 index 0000000000..8bfed0365e --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDView.java @@ -0,0 +1,1181 @@ +/********************************************************************** + * Copyright (c) 2005, 2014 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import java.util.Iterator; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.BaseMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Frame; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessageReturn; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.ConfigureMinMax; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.FirstPage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.KeyBindingsManager; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.LastPage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.MoveToMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.NextPage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.OpenSDFiltersDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.OpenSDFindDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.OpenSDPagesDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.PrevPage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.Print; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.ShowNodeEnd; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.ShowNodeStart; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.Zoom; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.Zoom.ZoomType; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.IExtendedFilterProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.IExtendedFindProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDAdvancedPagingProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDCollapseProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDExtendedActionBarProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFilterProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFindProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDGraphNodeSupporter; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDPagingProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDPropertiesProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.load.IUml2SDLoader; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.load.LoadersManager; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.part.ViewPart; +import org.eclipse.ui.views.properties.IPropertySheetPage; + +/** + * <p> + * This class is a generic sequence diagram view implementation. + * </p> + + * @version 1.0 + * @author sveyrier + */ +public class SDView extends ViewPart { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * Name of menu separator for view modes + * @since 2.0 + */ + public static final String UML2SD_VIEW_MODES_SEPARATOR = "UML2SD_VIEW_MODES"; //$NON-NLS-1$ + /** + * Name of menu separator for working set + * @since 2.0 + */ + public static final String UML2SD_WORKING_SET_SEPARATOR = "UML2SD_WORKING_SET"; //$NON-NLS-1$ + /** + * Name of menu separator for sorting + * @since 2.0 + */ + public static final String UML2SD_SORTING_SEPARATOR = "UML2SD_SORTING"; //$NON-NLS-1$ + /** + * Name of menu separator for filtering + * @since 2.0 + */ + public static final String UML2SD_FILTERING_SEPARATOR = "UML2SD_FILTERING"; //$NON-NLS-1$ + /** + * Name of menu separator for view layout + * @since 2.0 + */ + public static final String UML2SD_VIEW_LAYOUT_SEPARATOR = "UML2SD_VIEW_LAYOUT"; //$NON-NLS-1$ + /** + * Name of menu separator for link editor + * @since 2.0 + */ + public static final String UML2SD_LINK_EDITOR_SEPARATOR = "UML2SD_LINK_EDITOR"; //$NON-NLS-1$ + /** + * Name of menu separator for other commands + * @since 2.0 + */ + public static final String UML2SD_OTHER_COMMANDS_SEPARATOR = "UML2SD_OTHER_COMMANDS"; //$NON-NLS-1$ + /** + * Name of menu separator for other plug-in commands + * @since 2.0 + */ + public static final String UML2SD_OTHER_PLUGINS_COMMANDS_SEPARATOR = "UML2SD_OTHER_PLUGINS_COMMANDS"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The sequence diagram widget. + */ + private SDWidget fSdWidget = null; + /** + * The time compression bar. + */ + private TimeCompressionBar fTimeCompressionBar = null; + /** + * The sequence diagram find provider implementation. + */ + private ISDFindProvider fSdFindProvider = null; + /** + * The sequence diagram paging provider implementation. + */ + private ISDPagingProvider fSdPagingProvider = null; + /** + * The sequence diagram filter provider implementation. + */ + private ISDFilterProvider fSdFilterProvider = null; + /** + * The extended sequence diagram filter provider implementation. + */ + private IExtendedFilterProvider fSdExFilterProvider = null; + /** + * The extended sequence diagram find provider implementation. + */ + private IExtendedFindProvider fSdExFindProvider = null; + /** + * The extended sequence diagram action bar provider implementation. + */ + private ISDExtendedActionBarProvider fSdExtendedActionBarProvider = null; + /** + * The sequence diagram property provider implementation. + */ + private ISDPropertiesProvider fSdPropertiesProvider = null; + /** + * Button for executing the next page action. + */ + private NextPage fNextPageButton = null; + /** + * Button for executing the previous page action. + */ + private PrevPage fPrevPageButton = null; + /** + * Button for executing the first page page action. + */ + private FirstPage fFirstPageButton = null; + /** + * Button for executing the last page action. + */ + private LastPage fLastPageButton = null; + /** + * The menu manager reference. + */ + private MenuManager fMenuMgr = null; + /** + * Flag to indicate whether view needs initialization or not. + */ + private boolean fNeedInit = true; + /** + * WaitCursor is the cursor to be displayed when long tasks are running + */ + private Cursor fWaitCursor; + + private Zoom fResetZoomAction; + private Zoom fNoZoomAction; + private Zoom fZoomInAction; + private Zoom fZoomOutAction; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void createPartControl(Composite c) { + Composite parent = new Composite(c, SWT.NONE); + GridLayout parentLayout = new GridLayout(); + parentLayout.numColumns = 2; + parentLayout.marginWidth = 0; + parentLayout.marginHeight = 0; + parent.setLayout(parentLayout); + + GridData timeLayoutdata = new GridData(GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL); + timeLayoutdata.widthHint = 10; + GridData seqDiagLayoutData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL); + fTimeCompressionBar = new TimeCompressionBar(parent, SWT.NONE); + fTimeCompressionBar.setLayoutData(timeLayoutdata); + fSdWidget = new SDWidget(parent, SWT.NONE); + fSdWidget.setLayoutData(seqDiagLayoutData); + fSdWidget.setSite(this); + fSdWidget.setTimeBar(fTimeCompressionBar); + + // Add this view to the key bindings manager + KeyBindingsManager.getInstance().add(this.getSite().getId()); + + createCoolbarContent(); + + hookContextMenu(); + + fTimeCompressionBar.setVisible(false); + parent.layout(true); + + Print print = new Print(this); + getViewSite().getActionBars().setGlobalActionHandler(ActionFactory.PRINT.getId(), print); + + fNeedInit = restoreLoader(); + } + + /** + * Load a blank page that is supposed to explain that a kind of interaction must be chosen. + */ + protected void loadBlank() { + IUml2SDLoader loader = new BlankUml2SdLoader(); + loader.setViewer(this); + setContentDescription(loader.getTitleString()); + } + + @Override + public void setFocus() { + if (fSdWidget != null) { + // update actions for key bindings + KeyBindingsManager.getInstance().setSdView(this); + fSdWidget.setFocus(); + } + if (isViewReady() && fNeedInit) { + fNeedInit = restoreLoader(); + } + } + + @Override + public void dispose() { + KeyBindingsManager.getInstance().remove(this.getSite().getId()); + disposeZoomActions(); + super.dispose(); + } + + private void disposeZoomActions() { + if (fResetZoomAction != null) { + fResetZoomAction.dispose(); + } + if (fNoZoomAction != null) { + fNoZoomAction.dispose(); + } + if (fZoomInAction != null) { + fZoomInAction.dispose(); + } + if (fZoomOutAction != null) { + fZoomOutAction.dispose(); + } + } + + /** + * Returns the SD widget. + * + * @return The SD widget. + */ + public SDWidget getSDWidget() { + return fSdWidget; + } + + /** + * Set the find provider for the opened sequence diagram viewer<br> + * If the provider is not set, the find menu item will not be available in the viewer<br> + * A find provider is called back when the user perform a find action<br> + * The find provider is responsible to move the sequence diagram to the GraphNode which match the + * find criteria as well as to highlight the GraphNode + * + * @param provider the search provider + */ + public void setSDFindProvider(ISDFindProvider provider) { + fSdFindProvider = provider; + fSdExFindProvider = null; + createCoolbarContent(); + if (provider != null) { + KeyBindingsManager.getInstance().setFindEnabled(true); + } + else { + KeyBindingsManager.getInstance().setFindEnabled(false); + } + } + + /** + * Set the find provider for the opened sequence diagram viewer<br> + * If the provider is not set, the find menu item will not be available in + * the viewer<br> + * A find provider is called back when the user perform a find action<br> + * If the extended find provider is set, it replaces the regular find + * provider (sdFindProvider).<br> + * + * @param provider + * The provider to set + */ + public void setExtendedFindProvider(IExtendedFindProvider provider) { + fSdExFindProvider = provider; + fSdFindProvider = null; + createCoolbarContent(); + if (provider != null) { + KeyBindingsManager.getInstance().setFindEnabled(true); + } + else { + KeyBindingsManager.getInstance().setFindEnabled(false); + } + } + + /** + * Returns the extended find provider + * + * @return extended find provider. + */ + public IExtendedFindProvider getExtendedFindProvider() { + return fSdExFindProvider; + } + + /** + * Resets all providers. + */ + public void resetProviders() { + KeyBindingsManager.getInstance().setFindEnabled(false); + fSdFindProvider = null; + fSdExFindProvider = null; + fSdFilterProvider = null; + fSdExFilterProvider = null; + fSdPagingProvider = null; + fSdExtendedActionBarProvider = null; + fSdPropertiesProvider = null; + if ((fSdWidget != null) && (!fSdWidget.isDisposed())) { + fSdWidget.setCollapseProvider(null); + } + } + + /** + * Set the filter provider for the opened sequence diagram viewer<br> + * If the provider is not set, the filter menu item will not be available in the viewer<br> + * A filter provider is called back when the user perform a filter action<br> + * + * @param provider the filter provider + */ + public void setSDFilterProvider(ISDFilterProvider provider) { + fSdFilterProvider = provider; + // Both systems can be used now, commenting out next statement + createCoolbarContent(); + } + + /** + * Sets the extended filter provider for the opened sequence diagram viewer. + * + * @param provider + * The provider to set + */ + public void setExtendedFilterProvider(IExtendedFilterProvider provider) { + fSdExFilterProvider = provider; + // Both systems can be used now, commenting out next statement + createCoolbarContent(); + } + + /** + * Returns the extended find provider. + * + * @return The extended find provider. + */ + public IExtendedFilterProvider getExtendedFilterProvider() { + return fSdExFilterProvider; + } + + /** + * Register the given provider to support Drag and Drop collapsing. This provider is + * responsible of updating the Frame. + * + * @param provider - the provider to register + */ + public void setCollapsingProvider(ISDCollapseProvider provider) { + if ((fSdWidget != null) && (!fSdWidget.isDisposed())) { + fSdWidget.setCollapseProvider(provider); + } + } + + /** + * Set the page provider for the opened sequence diagram viewer<br> + * If the sequence diagram provided (see setFrame) need to be split in many parts, a paging provider must be + * provided in order to handle page change requested by the user<br> + * Set a page provider will create the next and previous page buttons in the viewer coolBar + * + * @param provider the paging provider + */ + public void setSDPagingProvider(ISDPagingProvider provider) { + fSdPagingProvider = provider; + createCoolbarContent(); + } + + /** + * Returns the current page provider for the view + * + * @return the paging provider + */ + public ISDPagingProvider getSDPagingProvider() { + return fSdPagingProvider; + } + + /** + * Returns the current find provider for the view + * + * @return the find provider + */ + public ISDFindProvider getSDFindProvider() { + return fSdFindProvider; + } + + /** + * Returns the current filter provider for the view + * + * @return the filter provider + */ + public ISDFilterProvider getSDFilterProvider() { + return fSdFilterProvider; + } + + /** + * Set the extended action bar provider for the opened sequence diagram viewer<br> + * This allow to add programmatically actions in the coolbar and/or in the drop-down menu + * + * @param provider the search provider + */ + public void setSDExtendedActionBarProvider(ISDExtendedActionBarProvider provider) { + fSdExtendedActionBarProvider = provider; + createCoolbarContent(); + } + + /** + * Returns the current extended action bar provider for the view + * + * @return the extended action bar provider + */ + public ISDExtendedActionBarProvider getSDExtendedActionBarProvider() { + return fSdExtendedActionBarProvider; + } + + /** + * Set the properties view provider for the opened sequence diagram viewer + * + * @param provider the properties provider + */ + public void setSDPropertiesProvider(ISDPropertiesProvider provider) { + fSdPropertiesProvider = provider; + } + + /** + * Returns the current extended action bar provider for the view. + * + * @return the extended action bar provider + */ + public ISDPropertiesProvider getSDPropertiesProvider() { + return fSdPropertiesProvider; + } + + /** + * Sets the sdWidget. + * + * @param sdWidget + * A sdWidget to set + * @since 2.0 + */ + protected void setSDWidget(SDWidget sdWidget) { + fSdWidget = sdWidget; + } + + /** + * Sets the time compression bar. + * + * @param timeCompressionbar + * A sdWidget to set + * @since 2.0 + */ + protected void setTimeBar(TimeCompressionBar timeCompressionbar) { + fTimeCompressionBar = timeCompressionbar; + } + + /** + * Sets the initialization flag. + * + * @param needInit + * flag value to set + * @since 2.0 + */ + protected void setNeedInit(boolean needInit) { + fNeedInit = needInit; + } + + /** + * Creates the basic sequence diagram menu + */ + protected void hookContextMenu() { + fMenuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + fMenuMgr.setRemoveAllWhenShown(true); + fMenuMgr.addMenuListener(new IMenuListener() { + @Override + public void menuAboutToShow(IMenuManager manager) { + fillContextMenu(manager); + } + }); + Menu menu = fMenuMgr.createContextMenu(fSdWidget.getViewControl()); + fSdWidget.getViewControl().setMenu(menu); + getSite().registerContextMenu(fMenuMgr, fSdWidget.getSelectionProvider()); + } + + /** + * Returns the context menu manager + * + * @return the menu manager + */ + public MenuManager getMenuManager() { + return fMenuMgr; + } + + /** + * Fills the basic sequence diagram menu and define the dynamic menu item insertion point + * + * @param manager the menu manager + */ + protected void fillContextMenu(IMenuManager manager) { + manager.add(new Separator("Additions")); //$NON-NLS-1$ + if (getSDWidget() != null && getSDWidget().getCurrentGraphNode() != null) { + ISelectionProvider selProvider = fSdWidget.getSelectionProvider(); + ISelection sel = selProvider.getSelection(); + int nbMessage = 0; + Iterator<?> it = ((StructuredSelection) sel).iterator(); + while (it.hasNext()) { + Object node = it.next(); + if (node instanceof BaseMessage) { + nbMessage++; + } + } + if (nbMessage != 1) { + return; + } + GraphNode node = getSDWidget().getCurrentGraphNode(); + if ((node instanceof SyncMessageReturn) && (((SyncMessageReturn) node).getMessage() != null)) { + Action goToMessage = new MoveToMessage(this); + goToMessage.setText(Messages.SequenceDiagram_GoToMessage); + manager.add(goToMessage); + } + if ((node instanceof SyncMessage) && (((SyncMessage) node).getMessageReturn() != null)) { + Action goToMessage = new MoveToMessage(this); + goToMessage.setText(Messages.SequenceDiagram_GoToMessageReturn); + manager.add(goToMessage); + } + } + manager.add(new Separator("MultiSelectAdditions")); //$NON-NLS-1$ + } + + /** + * Enables/Disables an action with given name. + * + * @param actionName The action name + * @param state true or false + */ + public void setEnableAction(String actionName, boolean state) { + IActionBars bar = getViewSite().getActionBars(); + if (bar != null) { + IContributionItem item = bar.getMenuManager().find(actionName); + if ((item != null) && (item instanceof ActionContributionItem)) { + IAction action = ((ActionContributionItem) item).getAction(); + if (action != null) { + action.setEnabled(state); + } + item.setVisible(state); + bar.updateActionBars(); + } + } + } + + /** + * Creates the coolBar icon depending on the actions supported by the Sequence Diagram provider<br> + * - Navigation buttons are displayed if ISDPovider.HasPaging return true<br> + * - Navigation buttons are enabled depending on the value return by ISDPovider.HasNext and HasPrev<br> + * + * @see ISDGraphNodeSupporter Action support definition + * @see SDView#setSDFilterProvider(ISDFilterProvider) + * @see SDView#setSDFindProvider(ISDFindProvider) + * @see SDView#setSDPagingProvider(ISDPagingProvider) + */ + protected void createCoolbarContent() { + IActionBars bar = getViewSite().getActionBars(); + + bar.getMenuManager().removeAll(); + bar.getToolBarManager().removeAll(); + disposeZoomActions(); + + createMenuGroup(); + + fResetZoomAction = new Zoom(this, ZoomType.ZOOM_RESET); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fResetZoomAction); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fResetZoomAction); + + fNoZoomAction = new Zoom(this, ZoomType.ZOOM_NONE); + fNoZoomAction.setChecked(true); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fNoZoomAction); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fNoZoomAction); + + fZoomInAction = new Zoom(this, ZoomType.ZOOM_IN); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fZoomInAction); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fZoomInAction); + + fZoomOutAction = new Zoom(this, ZoomType.ZOOM_OUT); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fZoomOutAction); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fZoomOutAction); + + MenuManager navigation = new MenuManager(Messages.SequenceDiagram_Navigation); + + ShowNodeStart showNodeStart = new ShowNodeStart(this); + showNodeStart.setText(Messages.SequenceDiagram_ShowNodeStart); + + showNodeStart.setId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeStart");//$NON-NLS-1$ + showNodeStart.setActionDefinitionId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeStart");//$NON-NLS-1$ + navigation.add(showNodeStart); + + ShowNodeEnd showNodeEnd = new ShowNodeEnd(this); + showNodeEnd.setText(Messages.SequenceDiagram_ShowNodeEnd); + + showNodeEnd.setId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeEnd");//$NON-NLS-1$ + showNodeEnd.setActionDefinitionId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeEnd");//$NON-NLS-1$ + navigation.add(showNodeEnd); + + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, navigation); + + ConfigureMinMax minMax = new ConfigureMinMax(this); + minMax.setText(Messages.SequenceDiagram_ConfigureMinMax); + minMax.setId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ConfigureMinMax");//$NON-NLS-1$ + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, minMax); + + if ((fSdWidget.getFrame() != null) && (fSdWidget.getFrame().hasTimeInfo())) { + minMax.setEnabled(true); + } else { + minMax.setEnabled(false); + } + + // Do we need to display a paging item + if (fSdPagingProvider != null) { + fNextPageButton = new NextPage(this); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fNextPageButton); + fNextPageButton.setEnabled(fSdPagingProvider.hasNextPage()); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fNextPageButton); + + fPrevPageButton = new PrevPage(this); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fPrevPageButton); + fPrevPageButton.setEnabled(fSdPagingProvider.hasPrevPage()); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fPrevPageButton); + + fFirstPageButton = new FirstPage(this); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fFirstPageButton); + fFirstPageButton.setEnabled(fSdPagingProvider.hasPrevPage()); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fFirstPageButton); + + fLastPageButton = new LastPage(this); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fLastPageButton); + fLastPageButton.setEnabled(fSdPagingProvider.hasNextPage()); + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, fLastPageButton); + } + + if (fSdExFilterProvider != null) { + Action action = fSdExFilterProvider.getFilterAction(); + if (action != null) { + if (action.getId() == null) + { + action.setId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.extendedFilter"); //$NON-NLS-1$ + } + if (action.getImageDescriptor() == null) { + action.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FILTERS)); + } + if (action.getText() == null || action.getText().length() == 0) { + action.setText(Messages.SequenceDiagram_EditFilters); + } + bar.getMenuManager().prependToGroup(UML2SD_FILTERING_SEPARATOR, action); + bar.getToolBarManager().prependToGroup(UML2SD_FILTERING_SEPARATOR, action); + } + } + // Both systems can be used now: commenting out else keyword + if (fSdFilterProvider != null) { + bar.getMenuManager().appendToGroup(UML2SD_FILTERING_SEPARATOR, new OpenSDFiltersDialog(this, fSdFilterProvider)); + } + if (fSdPagingProvider instanceof ISDAdvancedPagingProvider) { + IContributionItem sdPaging = bar.getMenuManager().find(OpenSDPagesDialog.ID); + if (sdPaging != null) { + bar.getMenuManager().remove(sdPaging); + sdPaging = null; + } + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, new OpenSDPagesDialog(this, (ISDAdvancedPagingProvider) fSdPagingProvider)); + updatePagesMenuItem(bar); + } + + if (fSdExFindProvider != null) { + Action action = fSdExFindProvider.getFindAction(); + if (action != null) { + if (action.getId() == null) { + action.setId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.extendedFind"); //$NON-NLS-1$ + } + if (action.getImageDescriptor() == null) { + action.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SEARCH_SEQ)); + } + if (action.getText() == null) { + action.setText(Messages.SequenceDiagram_Find + "..."); //$NON-NLS-1$ + } + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, action); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, action); + } + } else if (fSdFindProvider != null) { + bar.getMenuManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, new OpenSDFindDialog(this)); + bar.getToolBarManager().appendToGroup(UML2SD_OTHER_COMMANDS_SEPARATOR, new OpenSDFindDialog(this)); + } + + if (fSdExtendedActionBarProvider != null) { + fSdExtendedActionBarProvider.supplementCoolbarContent(bar); + } + + bar.updateActionBars(); + } + + /** + * Updates the view coolbar buttons state according to the value return by: - + * ISDExtendedActionBarProvider.hasNextPage()<br> + * - ISDExtendedActionBarProvider.hasPrevPage()<br> + * + */ + public void updateCoolBar() { + if (fSdPagingProvider != null) { + IActionBars bar = getViewSite().getActionBars(); + if (bar == null) { + return; + } + IToolBarManager barManager = bar.getToolBarManager(); + if (barManager == null) { + return; + } + IContributionItem nextPage = barManager.find(NextPage.ID); + if (nextPage instanceof ActionContributionItem) { + IAction nextPageAction = ((ActionContributionItem) nextPage).getAction(); + if (nextPageAction instanceof NextPage) { + ((NextPage) nextPageAction).setEnabled(fSdPagingProvider.hasNextPage()); + } + } + + IContributionItem prevPage = barManager.find(PrevPage.ID); + if (prevPage instanceof ActionContributionItem) { + IAction prevPageAction = ((ActionContributionItem) prevPage).getAction(); + if (prevPageAction instanceof PrevPage) { + ((PrevPage) prevPageAction).setEnabled(fSdPagingProvider.hasPrevPage()); + } + } + + IContributionItem firstPage = barManager.find(FirstPage.ID); + if (firstPage instanceof ActionContributionItem) { + IAction firstPageAction = ((ActionContributionItem) firstPage).getAction(); + if (firstPageAction instanceof FirstPage) { + ((FirstPage) firstPageAction).setEnabled(fSdPagingProvider.hasPrevPage()); + } + } + + IContributionItem lastPage = barManager.find(LastPage.ID); + if (lastPage instanceof ActionContributionItem) { + IAction lastPageAction = ((ActionContributionItem) lastPage).getAction(); + if (lastPageAction instanceof LastPage) { + ((LastPage) lastPageAction).setEnabled(fSdPagingProvider.hasNextPage()); + } + } + + updatePagesMenuItem(bar); + } + } + + /** + * Enables or disables the Pages... menu item, depending on the number of pages + * + * @param bar the bar containing the action + */ + protected void updatePagesMenuItem(IActionBars bar) { + if (fSdPagingProvider instanceof ISDAdvancedPagingProvider) { + IMenuManager menuManager = bar.getMenuManager(); + ActionContributionItem contributionItem = (ActionContributionItem) menuManager.find(OpenSDPagesDialog.ID); + IAction openSDPagesDialog = null; + if (contributionItem != null) { + openSDPagesDialog = contributionItem.getAction(); + } + + if (openSDPagesDialog instanceof OpenSDPagesDialog) { + openSDPagesDialog.setEnabled(((ISDAdvancedPagingProvider) fSdPagingProvider).pagesCount() > 1); + } + } + } + + /** + * The frame to render (the sequence diagram) + * + * @param frame the frame to display + */ + public void setFrame(Frame frame) { + setFrame(frame, true); + } + + /** + * The frame to render (the sequence diagram) + * + * @param frame the frame to display + * @param resetPosition boolean Flag whether to reset the position or not. + */ + protected void setFrame(Frame frame, boolean resetPosition) { + if (getSDWidget() == null) { + return; + } + + if (frame == null) { + loadBlank(); + return; + } + + IUml2SDLoader loader = LoadersManager.getInstance().getCurrentLoader(getViewSite().getId(), this); + if (loader == null) { + return; + } + + if (loader.getTitleString() != null) { + setContentDescription(loader.getTitleString()); + } + + getSDWidget().setFrame(frame, resetPosition); + + if (fTimeCompressionBar != null) { + fTimeCompressionBar.setFrame(frame); + } + updateCoolBar(); + if (fTimeCompressionBar != null) { + if (!frame.hasTimeInfo()) { + Composite parent = fTimeCompressionBar.getParent(); + fTimeCompressionBar.setVisible(false); + parent.layout(true); + } else { + Composite parent = fTimeCompressionBar.getParent(); + fTimeCompressionBar.setVisible(true); + parent.layout(true); + } + } + IContributionItem shortKeysMenu = getViewSite().getActionBars().getMenuManager().find("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers");//$NON-NLS-1$ + MenuManager shortKeys = (MenuManager) shortKeysMenu; + if (shortKeys != null) { + IContributionItem[] items = shortKeys.getItems(); + for (int i = 0; i < items.length; i++) { + if (items[i] instanceof ActionContributionItem) { + IAction action = ((ActionContributionItem) items[i]).getAction(); + if (action != null) { + action.setEnabled(true); + } + } + } + } + createCoolbarContent(); + } + + /** + * Activate or deactivate the short key command given in parameter (see plugin.xml) + * + * @param id the command id defined in the plugin.xml + * @param value the state value + */ + public void setEnableCommand(String id, boolean value) { + IContributionItem shortKeysMenu = getViewSite().getActionBars().getMenuManager().find("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers");//$NON-NLS-1$ + MenuManager shortKeys = (MenuManager) shortKeysMenu; + if (shortKeys == null) { + return; + } + IContributionItem item = shortKeys.find(id); + if ((item != null) && (item instanceof ActionContributionItem)) { + IAction action = ((ActionContributionItem) item).getAction(); + if (action != null) { + action.setEnabled(value); + } + } + } + + /** + * Set the frame from an other thread than the one executing the main loop + * + * @param frame The frame to set (and display) + */ + public void setFrameSync(final Frame frame) { + if (getSDWidget() == null || getSDWidget().isDisposed()) { + return; + } + getSDWidget().getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (getSDWidget() == null || getSDWidget().isDisposed() || + ((fTimeCompressionBar != null) && fTimeCompressionBar.isDisposed())) { + return; + } + setFrame(frame); + } + }); + + } + + /** + * Ensure an object is visible from an other thread than the one executing the main loop + * + * @param sm The node to make visible in view + */ + public void ensureVisibleSync(final GraphNode sm) { + getSDWidget().getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (getSDWidget() == null || getSDWidget().isDisposed()) { + return; + } + getSDWidget().ensureVisible(sm); + } + }); + } + + /** + * Set the frame and ensure an object is visible from an other thread than the one executing the main loop + * + * @param sm The node to make visible in view + * @param frame Frame The frame to set + */ + public void setFrameAndEnsureVisibleSync(final Frame frame, final GraphNode sm) { + if (getSDWidget() == null || getSDWidget().isDisposed()) { + return; + } + getSDWidget().getDisplay().syncExec(new Runnable() { + @Override + public void run() { + if (getSDWidget() == null || getSDWidget().isDisposed()) { + return; + } + setFrameAndEnsureVisible(frame, sm); + } + }); + } + + /** + * Set the frame and ensure an object is visible + * + * @param sm The node to make visible in view + * @param frame Frame The frame to set + */ + public void setFrameAndEnsureVisible(Frame frame, GraphNode sm) { + getSDWidget().clearSelection(); + setFrame(frame, false); + getSDWidget().ensureVisible(sm); + } + + /** + * Set the frame and ensure an object is visible from an other thread than the one executing the main loop + * + * @param frame The frame to set. + * @param x The x coordinate to make visible. + * @param y The y coordinate to make visible. + */ + public void setFrameAndEnsureVisibleSync(final Frame frame, final int x, final int y) { + if (getSDWidget() == null || getSDWidget().isDisposed()) { + return; + } + + getSDWidget().getDisplay().syncExec(new Runnable() { + @Override + public void run() { + setFrameAndEnsureVisible(frame, x, y); + } + }); + } + + /** + * Set the frame and ensure an object is visible + * + * @param frame The frame to set. + * @param x The x coordinate to make visible. + * @param y The y coordinate to make visible. + */ + public void setFrameAndEnsureVisible(Frame frame, int x, int y) { + getSDWidget().clearSelection(); + setFrame(frame, false); + getSDWidget().ensureVisible(x, y); + getSDWidget().redraw(); + } + + /** + * Toggle between default and wait cursors from an other thread than the one executing the main loop + * + * @param wait <code>true</code> for wait cursor else <code>false</code> for default cursor. + */ + public void toggleWaitCursorAsync(final boolean wait) { + if (getSDWidget() == null || getSDWidget().isDisposed()) { + return; + } + + getSDWidget().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + if (getSDWidget() == null || getSDWidget().isDisposed()) { + return; + } + if (wait) { + if (fWaitCursor != null && !fWaitCursor.isDisposed()) { + fWaitCursor.dispose(); + } + fWaitCursor = new Cursor(getSDWidget().getDisplay(), SWT.CURSOR_WAIT); + getSDWidget().setCursor(fWaitCursor); + getSDWidget().getDisplay().update(); + } else { + if (fWaitCursor != null && !fWaitCursor.isDisposed()) { + fWaitCursor.dispose(); + } + fWaitCursor = null; + getSDWidget().setCursor(null); + getSDWidget().getDisplay().update(); + } + } + }); + } + + /** + * Return the time compression bar widget + * + * @return the time compression bar + */ + public TimeCompressionBar getTimeCompressionBar() { + return fTimeCompressionBar; + } + + /** + * Returns the current Frame (the sequence diagram container) + * + * @return the current frame + */ + public Frame getFrame() { + if (getSDWidget() != null) { + return getSDWidget().getFrame(); + } + return null; + } + + /** + * Gets the initialization flag. + * @return the value of the initialization flag. + * @since 2.0 + */ + protected boolean isNeedInit() { + return fNeedInit; + } + + /** + * Restores the loader for the view based on the view ID. + * + * @return boolean <code>true</code> if initialization is needed else <code>false</code>. + */ + protected boolean restoreLoader() { + String id = getViewSite().getId(); + if (id == null) { + return true; + } + IUml2SDLoader loader = LoadersManager.getInstance().getCurrentLoader(id, this); + if ((loader != null)) { + loader.setViewer(this); + return false; + } + loadBlank(); + return true; + } + + /** + * Checks if current view is ready to be used. + * + * @return boolean <code>true</code> if view is ready else <code>false</code>. + */ + protected boolean isViewReady() { + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + if (page == null) { + return false; + } + + IViewReference[] ref = page.getViewReferences(); + for (int i = 0; i < ref.length; i++) { + if (ref[i].getView(false) == this) { + return true; + } + } + return false; + } + + /** + * Creates the menu group. + */ + protected void createMenuGroup() { + IActionBars bar = getViewSite().getActionBars(); + if (bar == null) { + return; + } + bar.getToolBarManager().add(new Separator(UML2SD_VIEW_MODES_SEPARATOR)); + bar.getToolBarManager().add(new Separator(UML2SD_WORKING_SET_SEPARATOR)); + bar.getToolBarManager().add(new Separator(UML2SD_SORTING_SEPARATOR)); + bar.getToolBarManager().add(new Separator(UML2SD_FILTERING_SEPARATOR)); + bar.getToolBarManager().add(new Separator(UML2SD_VIEW_LAYOUT_SEPARATOR)); + bar.getToolBarManager().add(new Separator(UML2SD_LINK_EDITOR_SEPARATOR)); + bar.getToolBarManager().add(new Separator(UML2SD_OTHER_COMMANDS_SEPARATOR)); + bar.getToolBarManager().add(new Separator(UML2SD_OTHER_PLUGINS_COMMANDS_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_VIEW_MODES_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_WORKING_SET_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_SORTING_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_FILTERING_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_VIEW_LAYOUT_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_LINK_EDITOR_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_OTHER_COMMANDS_SEPARATOR)); + bar.getMenuManager().add(new Separator(UML2SD_OTHER_PLUGINS_COMMANDS_SEPARATOR)); + } + + @Override + public Object getAdapter(Class adapter) { + Object obj = super.getAdapter(adapter); + if (fSdPropertiesProvider != null && adapter.equals(IPropertySheetPage.class)) { + return fSdPropertiesProvider.getPropertySheetEntry(); + } + + return obj; + } + + /** + * Loader for a blank sequence diagram. + * + * @version 1.0 + */ + public static class BlankUml2SdLoader implements IUml2SDLoader { + @Override + public void setViewer(SDView viewer) { + // Nothing to do + Frame f = new Frame(); + f.setName(""); //$NON-NLS-1$ + viewer.setFrame(f); + } + + @Override + public String getTitleString() { + return ""; //$NON-NLS-1$ + } + + @Override + public void dispose() { + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDWidget.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDWidget.java new file mode 100755 index 0000000000..089ebc0e49 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDWidget.java @@ -0,0 +1,2066 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import org.eclipse.jface.contexts.IContextIds; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.ACC; +import org.eclipse.swt.accessibility.Accessible; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleControlAdapter; +import org.eclipse.swt.accessibility.AccessibleControlEvent; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.printing.Printer; +import org.eclipse.swt.printing.PrinterData; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Caret; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.BaseMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.BasicExecutionOccurrence; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Frame; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.ITimeRange; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Metrics; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.SDPrintDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.SDPrintDialogUI; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDCollapseProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.load.LoadersManager; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.part.ViewPart; + +/** + * <p> + * This class implements sequence diagram widget used in the sequence diagram view. + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class SDWidget extends ScrollView implements SelectionListener, + IPropertyChangeListener, DisposeListener, ITimeCompressionListener { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The frame to display in the sequence diagram widget. + */ + private Frame fFrame; + /** + * The overview image to display. + */ + private Image fOverView = null; + /** + * The zoom in menu item. + */ + private MenuItem fZoomIn = null; + /** + * The zoom out menu item. + */ + private MenuItem fZoomOut = null; + /** + * The sequence diagram selection provider. + */ + private SDWidgetSelectionProvider fSelProvider = null; + /** + * The current zoom value. + */ + private float fZoomValue = 1; + /** + * The current zoomInMode (true for zoom in). + */ + private boolean fZoomInMode = false; + /** + * The current zoomOutMode (true for zoom out). + */ + private boolean fZoomOutMode = false; + /** + * The current list of selected graph nodes. + */ + private List<GraphNode> fSelectedNodeList = null; + /** + * Flag whether ctrl button is selected or not. + */ + private boolean fCtrlSelection = false; + /** + * A reference to the view site. + */ + private ViewPart fSite = null; + /** + * The current graph node (the last selected one). + */ + private GraphNode fCurrentGraphNode = null; + /** + * The first graph node in list (multiple selection). + */ + private GraphNode fListStart = null; + /** + * The previous graph node (multiple selection). + */ + private List<GraphNode> fPrevList = null; + /** + * The time compression bar. + */ + private TimeCompressionBar fTimeBar = null; + /** + * The current diagram tool tip. + */ + private DiagramToolTip fToolTip = null; + /** + * The accessible object reference of view control. + */ + private Accessible fAccessible = null; + /** + * The current node for the tooltip to display. + */ + private GraphNode fToolTipNode; + /** + * The life line to drag and drop. + */ + private Lifeline fDragAndDrop = null; + /** + * The number of focused widgets. + */ + private int fFocusedWidget = -1; + /** + * The printer zoom. + */ + private float fPrinterZoom = 0; + /** + * Y coordinate for printer. + */ + private int fPrinterY = 0; + /** + * X coordinate for printer. + */ + private int fPrinterX = 0; + /** + * Flag whether drag and drop is enabled or not. + */ + private boolean fIsDragAndDrop = false; + /** + * The x coordinate for drag. + */ + private int fDragX = 0; + /** + * The y coordinate for drag. + */ + private int fDragY = 0; + /** + * The reorder mode. + */ + private boolean fReorderMode = false; + /** + * The collapse caret image. + */ + private Image fCollapaseCaretImg = null; + /** + * The arrow up caret image. + */ + private Image fArrowUpCaretImg = null; + /** + * The current caret image. + */ + private Image fCurrentCaretImage = null; + /** + * A sequence diagramm collapse provider (for collapsing graph nodes) + */ + private ISDCollapseProvider fCollapseProvider = null; + /** + * The insertion caret. + */ + private Caret fInsertionCartet = null; + /** + * The reorder list when in reorder mode. + */ + private List<Lifeline[]> fReorderList = null; + /** + * Flag to specify whether in printing mode or not. + */ + private boolean fIsPrinting = false; + /** + * A printer reference. + */ + private Printer fPrinter = null; + /** + * Flag whether shift was selected or not. + */ + private boolean fShiftSelection = false; + /** + * The scroll tooltip. + */ + private DiagramToolTip fScrollToolTip = null; + /** + * Timer for auto_scroll feature + */ + private AutoScroll fLocalAutoScroll = null; + /** + * TimerTask for auto_scroll feature !=null when auto scroll is running + */ + private Timer fLocalAutoScrollTimer = null; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + /** + * Constructor for SDWidget. + * @param c The parent composite + * @param s The style + */ + public SDWidget(Composite c, int s) { + super(c, s | SWT.NO_BACKGROUND, true); + setOverviewEnabled(true); + fSelectedNodeList = new ArrayList<>(); + fSelProvider = new SDWidgetSelectionProvider(); + SDViewPref.getInstance().addPropertyChangeListener(this); + fToolTip = new DiagramToolTip(getViewControl()); + super.addDisposeListener(this); + + fScrollToolTip = new DiagramToolTip(c); + getVerticalBar().addListener(SWT.MouseUp, new Listener() { + + @Override + public void handleEvent(Event event) { + fScrollToolTip.hideToolTip(); + } + + }); + fAccessible = getViewControl().getAccessible(); + + fAccessible.addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + // Case toolTip + if (e.childID == 0) { + if (fToolTipNode != null) { + if (fToolTipNode instanceof Lifeline) { + Lifeline lifeline = (Lifeline) fToolTipNode; + e.result = lifeline.getToolTipText(); + } else { + e.result = fToolTipNode.getName() + getPostfixForTooltip(true); + } + } + } else { + if (getFocusNode() != null) { + if (getFocusNode() instanceof Lifeline) { + e.result = MessageFormat.format(Messages.SequenceDiagram_LifelineNode, new Object[] { String.valueOf(getFocusNode().getName()) }); + } + if (getFocusNode() instanceof BaseMessage) { + BaseMessage mes = (BaseMessage) getFocusNode(); + if ((mes.getStartLifeline() != null) && (mes.getEndLifeline() != null)) { + e.result = MessageFormat.format( + Messages.SequenceDiagram_MessageNode, + new Object[] { String.valueOf(mes.getName()), String.valueOf(mes.getStartLifeline().getName()), Integer.valueOf(mes.getStartOccurrence()), String.valueOf(mes.getEndLifeline().getName()), + Integer.valueOf(mes.getEndOccurrence()) }); + } else if ((mes.getStartLifeline() == null) && (mes.getEndLifeline() != null)) { + e.result = MessageFormat.format(Messages.SequenceDiagram_FoundMessageNode, new Object[] { String.valueOf(mes.getName()), String.valueOf(mes.getEndLifeline().getName()), Integer.valueOf(mes.getEndOccurrence()) }); + } else if ((mes.getStartLifeline() != null) && (mes.getEndLifeline() == null)) { + e.result = MessageFormat.format(Messages.SequenceDiagram_LostMessageNode, new Object[] { String.valueOf(mes.getName()), String.valueOf(mes.getStartLifeline().getName()), Integer.valueOf(mes.getStartOccurrence()) }); + } + } else if (getFocusNode() instanceof BasicExecutionOccurrence) { + BasicExecutionOccurrence exec = (BasicExecutionOccurrence) getFocusNode(); + e.result = MessageFormat.format(Messages.SequenceDiagram_ExecutionOccurrenceWithParams, + new Object[] { String.valueOf(exec.getName()), String.valueOf(exec.getLifeline().getName()), Integer.valueOf(exec.getStartOccurrence()), Integer.valueOf(exec.getEndOccurrence()) }); + } + + } + } + } + }); + + fAccessible.addAccessibleControlListener(new AccessibleControlAdapter() { + @Override + public void getFocus(AccessibleControlEvent e) { + if (fFocusedWidget == -1) { + e.childID = ACC.CHILDID_SELF; + } else { + e.childID = fFocusedWidget; + } + } + + @Override + public void getRole(AccessibleControlEvent e) { + switch (e.childID) { + case ACC.CHILDID_SELF: + e.detail = ACC.ROLE_CLIENT_AREA; + break; + case 0: + e.detail = ACC.ROLE_TOOLTIP; + break; + case 1: + e.detail = ACC.ROLE_LABEL; + break; + default: + break; + } + } + + @Override + public void getState(AccessibleControlEvent e) { + e.detail = ACC.STATE_FOCUSABLE; + if (e.childID == ACC.CHILDID_SELF) { + e.detail |= ACC.STATE_FOCUSED; + } else { + e.detail |= ACC.STATE_SELECTABLE; + if (e.childID == fFocusedWidget) { + e.detail |= ACC.STATE_FOCUSED | ACC.STATE_SELECTED | ACC.STATE_CHECKED; + } + } + } + }); + + fInsertionCartet = new Caret((Canvas) getViewControl(), SWT.NONE); + fInsertionCartet.setVisible(false); + + fCollapaseCaretImg = Activator.getDefault().getImageFromPath(ITmfImageConstants.IMG_UI_ARROW_COLLAPSE_OBJ); + fArrowUpCaretImg = Activator.getDefault().getImageFromPath(ITmfImageConstants.IMG_UI_ARROW_UP_OBJ); + + fReorderList = new ArrayList<>(); + getViewControl().addTraverseListener(new LocalTraverseListener()); + + addTraverseListener(new LocalTraverseListener()); + + getViewControl().addFocusListener(new FocusListener() { + + @Override + public void focusGained(FocusEvent e) { + SDViewPref.getInstance().setNoFocusSelection(false); + fCtrlSelection = false; + fShiftSelection = false; + redraw(); + } + + @Override + public void focusLost(FocusEvent e) { + SDViewPref.getInstance().setNoFocusSelection(true); + redraw(); + } + }); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * Sets the time compression bar. + * + * @param bar The time compression bar to set + */ + public void setTimeBar(TimeCompressionBar bar) { + if (bar != null) { + fTimeBar = bar; + fTimeBar.addTimeCompressionListener(this); + } + } + + /** + * Resize the contents to insure the frame fit into the view + * + * @param frame the frame which will be drawn in the view + */ + public void resizeContents(Frame frame) { + int width = Math.round((frame.getWidth() + 2 * Metrics.FRAME_H_MARGIN) * fZoomValue); + int height = Math.round((frame.getHeight() + 2 * Metrics.FRAME_V_MARGIN) * fZoomValue); + resizeContents(width, height); + } + + /** + * The frame to render (the sequence diagram) + * + * @param theFrame the frame to display + * @param resetPosition boolean + */ + public void setFrame(Frame theFrame, boolean resetPosition) { + fReorderList.clear(); + fSelectedNodeList.clear(); + fSelProvider.setSelection(new StructuredSelection()); + fFrame = theFrame; + if (resetPosition) { + setContentsPos(0, 0); + resizeContents(fFrame); + redraw(); + } + // prepare the old overview to be reused + if (fOverView != null) { + fOverView.dispose(); + } + fOverView = null; + resizeContents(fFrame); + } + + /** + * Returns the current Frame (the sequence diagram container) + * + * @return the frame + */ + public Frame getFrame() { + return fFrame; + } + + /** + * Returns the selection provider for the current sequence diagram + * + * @return the selection provider + */ + public ISelectionProvider getSelectionProvider() { + return fSelProvider; + } + + /** + * Returns a list of selected graph nodes. + * + * @return a list of selected graph nodes. + */ + public List<GraphNode> getSelection() { + return fSelectedNodeList; + } + + /** + * Adds a graph node to the selected nodes list. + * + * @param node A graph node + */ + public void addSelection(GraphNode node) { + if (node == null) { + return; + } + fSelectedNodeList.add(node); + node.setSelected(true); + fCurrentGraphNode = node; + StructuredSelection selection = new StructuredSelection(fSelectedNodeList); + fSelProvider.setSelection(selection); + } + + /** + * Adds a list of node to the selected nodes list. + * + * @param list of graph nodes + */ + public void addSelection(List<GraphNode> list) { + for (int i = 0; i < list.size(); i++) { + if (!fSelectedNodeList.contains(list.get(i))) { + fSelectedNodeList.add(list.get(i)); + list.get(i).setSelected(true); + } + } + StructuredSelection selection = new StructuredSelection(fSelectedNodeList); + fSelProvider.setSelection(selection); + } + + /** + * Removes a node from the selected nodes list. + * + * @param node to remove + */ + public void removeSelection(GraphNode node) { + fSelectedNodeList.remove(node); + node.setSelected(false); + node.setFocused(false); + StructuredSelection selection = new StructuredSelection(fSelectedNodeList); + fSelProvider.setSelection(selection); + } + + /** + * Removes a list of graph nodes from the selected nodes list. + * + * @param list of nodes to remove. + */ + public void removeSelection(List<GraphNode> list) { + fSelectedNodeList.removeAll(list); + for (int i = 0; i < list.size(); i++) { + list.get(i).setSelected(false); + list.get(i).setFocused(false); + } + StructuredSelection selection = new StructuredSelection(fSelectedNodeList); + fSelProvider.setSelection(selection); + } + + /** + * Clear the list of GraphNodes which must be drawn selected. + */ + public void clearSelection() { + for (int i = 0; i < fSelectedNodeList.size(); i++) { + fSelectedNodeList.get(i).setSelected(false); + fSelectedNodeList.get(i).setFocused(false); + } + fCurrentGraphNode = null; + fSelectedNodeList.clear(); + fSelProvider.setSelection(new StructuredSelection()); + } + + /** + * Sets view part. + * + * @param viewSite The view part to set + */ + public void setSite(ViewPart viewSite) { + fSite = viewSite; + fSite.getSite().setSelectionProvider(fSelProvider); + IContextService service = (IContextService) fSite.getSite().getWorkbenchWindow().getService(IContextService.class); + service.activateContext("org.eclipse.linuxtools.tmf.ui.view.uml2sd.context"); //$NON-NLS-1$ + service.activateContext(IContextIds.CONTEXT_ID_WINDOW); + } + + /** + * Returns the GraphNode overView the mouse if any + * + * @return the current graph node + * */ + public GraphNode getMouseOverNode() { + return fCurrentGraphNode; + } + + /** + * Sets the zoom in mode. + * + * @param value + * The mode value to set. + */ + public void setZoomInMode(boolean value) { + if (value) { + setZoomOutMode(false); + } + fZoomInMode = value; + } + + /** + * Sets the zoom out mode. + * + * @param value + * The mode value to set. + */ + public void setZoomOutMode(boolean value) { + if (value) { + setZoomInMode(false); + } + fZoomOutMode = value; + } + + /** + * Sets the current zoom value. + * + * @param zoomValue + * The current zoom value + * @since 2.0 + */ + public void setZoomValue(float zoomValue) { + fZoomValue = zoomValue; + } + + /** + * Moves the Sequence diagram to ensure the given node is visible and draw it selected + * + * @param node the GraphNode to move to + */ + public void moveTo(GraphNode node) { + if (node == null) { + return; + } + clearSelection(); + addSelection(node); + ensureVisible(node); + } + + /** + * Moves the Sequence diagram to ensure the given node is visible + * + * @param node the GraphNode to move to + */ + public void ensureVisible(GraphNode node) { + if (node == null) { + return; + } + int x = Math.round(node.getX() * fZoomValue); + int y = Math.round(node.getY() * fZoomValue); + int width = Math.round(node.getWidth() * fZoomValue); + int height = Math.round(node.getHeight() * fZoomValue); + if ((node instanceof BaseMessage) && (height == 0)) { + int header = Metrics.LIFELINE_HEARDER_TEXT_V_MARGIN * 2 + Metrics.getLifelineHeaderFontHeigth(); + height = -Math.round((Metrics.getMessagesSpacing() + header) * fZoomValue); + y = y + Math.round(Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT * fZoomValue); + } + if (node instanceof BasicExecutionOccurrence) { + width = 1; + height = 1; + } + if (node instanceof Lifeline) { + y = getContentsY(); + height = getVisibleHeight(); + } + ensureVisible(x, y, width, height, SWT.CENTER, true); + redraw(); + } + + /** + * Returns the current zoom factor. + * @return the current zoom factor. + */ + public float getZoomFactor() { + return fZoomValue; + } + + /** + * Returns teh printer reference. + * + * @return the printer reference + */ + public Printer getPrinter() { + return fPrinter; + } + + /** + * Returns whether the widget is used for printing or not. + * + * @return whether the widget is used for printing or not + */ + public boolean isPrinting() { + return fIsPrinting; + } + + /** + * Returns the current graph node. + * + * @return the current graph node + * @since 2.0 + */ + public GraphNode getCurrentGraphNode() { + return fCurrentGraphNode; + } + + /** + * Returns the current zoom value. + * + * @return the current zoom value + * @since 2.0 + */ + public float getZoomValue() { + return fZoomValue; + } + + /** + * Gets the zoom in mode. + * + * @return the mode value to set. + * @since 2.0 + */ + public boolean getZoomInMode() { + return fZoomInMode; + } + + + /** + * Gets the zoom out mode. + * + * @return the mode value to set. + * @since 2.0 + */ + public boolean getZoomOutMode() { + return fZoomOutMode; + } + + /** + * Returns if ctrl selection + * @return true if ctrl selection else false + * @since 2.0 + */ + public boolean isCtrlSelection() { + return fCtrlSelection; + } + + /** + * Returns if shift selection + * @return true if shift Selection else false + * @since 2.0 + */ + public boolean isShiftSelection() { + return fCtrlSelection; + } + + /** + * Gets the overview image. + * + * @param rect Rectangle to include overview. + * @return the overview image + */ + public Image getOverview(Rectangle rect) { + float oldzoom = fZoomValue; + if ((fOverView != null) && ((rect.width != fOverView.getBounds().width) || (rect.height != fOverView.getBounds().height))) { + fOverView.dispose(); + fOverView = null; + } + if (fOverView == null) { + int backX = getContentsX(); + int backY = getContentsY(); + setContentsPos(0, 0); + fOverView = new Image(getDisplay(), rect.width, rect.height); + GC gcim = new GC(fOverView); + NGC context = new NGC(this, gcim); + context.setBackground(SDViewPref.getInstance().getBackGroundColor(ISDPreferences.PREF_FRAME)); + fFrame.draw(context); + setContentsPos(backX, backY); + gcim.dispose(); + context.dispose(); + } + fZoomValue = oldzoom; + return fOverView; + } + + /** + * Resets the zoom factor. + */ + public void resetZoomFactor() { + int currentX = Math.round(getContentsX() / fZoomValue); + int currentY = Math.round(getContentsY() / fZoomValue); + fZoomValue = 1; + if (fTimeBar != null && !fTimeBar.isDisposed()) { + fTimeBar.setZoom(fZoomValue); + } + redraw(); + update(); + setContentsPos(currentX, currentY); + } + + /** + * Enable or disable the lifeline reodering using Drag and Drop + * + * @param mode - true to enable false otherwise + */ + public void setReorderMode(boolean mode) { + fReorderMode = mode; + } + + /** + * Return the lifelines reorder sequence (using Drag and Drop) if the the reorder mode is turn on. Each ArryList + * element is of type Lifeline[2] with Lifeline[0] inserted before Lifeline[1] in the diagram + * + * @return - the re-odered sequence + */ + public List<Lifeline[]> getLifelineReoderList() { + return fReorderList; + } + + /** + * Sets the focus on given graph node (current node). + * + * @param node + * The graph node to focus on. + */ + public void setFocus(GraphNode node) { + if (node == null) { + return; + } + if (fCurrentGraphNode != null) { + fCurrentGraphNode.setFocused(false); + } + fCurrentGraphNode = node; + node.setFocused(true); + ensureVisible(node); + setFocus(0); + } + + /** + * Returns the graph node focused on. + * + * @return the current graph node + */ + public GraphNode getFocusNode() { + return fCurrentGraphNode; + } + + /** + * Method to traverse right. + */ + public void traverseRight() { + Object selectedNode = getFocusNode(); + if (selectedNode == null) { + traverseLeft(); + } + GraphNode node = null; + if ((selectedNode instanceof BaseMessage) && (((BaseMessage) selectedNode).getEndLifeline() != null)) { + node = fFrame.getCalledMessage((BaseMessage) selectedNode); + } + if (selectedNode instanceof BasicExecutionOccurrence) { + selectedNode = ((BasicExecutionOccurrence) selectedNode).getLifeline(); + } + if ((node == null) && (selectedNode instanceof Lifeline)) { + for (int i = 0; i < fFrame.lifeLinesCount(); i++) { + if ((selectedNode == fFrame.getLifeline(i)) && (i < fFrame.lifeLinesCount() - 1)) { + node = fFrame.getLifeline(i + 1); + break; + } + } + } + if (node != null) { + setFocus(node); + redraw(); + } + } + + /** + * Method to traverse left. + */ + public void traverseLeft() { + Object selectedNode = getFocusNode(); + GraphNode node = null; + if ((selectedNode instanceof BaseMessage) && (((BaseMessage) selectedNode).getStartLifeline() != null)) { + node = fFrame.getCallerMessage((BaseMessage) selectedNode); + } + if (selectedNode instanceof BasicExecutionOccurrence) { + selectedNode = ((BasicExecutionOccurrence) selectedNode).getLifeline(); + } + if (node == null) { + if ((selectedNode instanceof BaseMessage) && (((BaseMessage) selectedNode).getEndLifeline() != null)) { + selectedNode = ((BaseMessage) selectedNode).getEndLifeline(); + } + for (int i = 0; i < fFrame.lifeLinesCount(); i++) { + if ((selectedNode == fFrame.getLifeline(i)) && (i > 0)) { + node = fFrame.getLifeline(i - 1); + break; + } + } + if ((fFrame.lifeLinesCount() > 0) && (node == null)) { + node = fFrame.getLifeline(0); + } + } + if (node != null) { + setFocus(node); + redraw(); + } + } + + /** + * Method to traverse up. + */ + public void traverseUp() { + Object selectedNode = getFocusNode(); + if (selectedNode == null) { + traverseLeft(); + } + GraphNode node = null; + if (selectedNode instanceof BaseMessage) { + node = fFrame.getPrevLifelineMessage(((BaseMessage) selectedNode).getStartLifeline(), (BaseMessage) selectedNode); + } else if (selectedNode instanceof Lifeline) { + node = fFrame.getPrevLifelineMessage((Lifeline) selectedNode, null); + if (!(node instanceof Lifeline)) { + node = null; + } + } else if (selectedNode instanceof BasicExecutionOccurrence) { + node = fFrame.getPrevExecOccurrence((BasicExecutionOccurrence) selectedNode); + if (node == null) { + node = ((BasicExecutionOccurrence) selectedNode).getLifeline(); + } + } + if ((node == null) && (selectedNode instanceof BaseMessage) && (((BaseMessage) selectedNode).getStartLifeline() != null)) { + node = ((BaseMessage) selectedNode).getStartLifeline(); + } + + if (node != null) { + setFocus(node); + redraw(); + } + } + + /** + * Method to traverse down. + */ + public void traverseDown() { + Object selectedNode = getFocusNode(); + if (selectedNode == null) { + traverseLeft(); + } + GraphNode node; + if (selectedNode instanceof BaseMessage) { + node = fFrame.getNextLifelineMessage(((BaseMessage) selectedNode).getStartLifeline(), (BaseMessage) selectedNode); + } else if (selectedNode instanceof Lifeline) { + node = fFrame.getFirstExecution((Lifeline) selectedNode); + } else if (selectedNode instanceof BasicExecutionOccurrence) { + node = fFrame.getNextExecOccurrence((BasicExecutionOccurrence) selectedNode); + } else { + return; + } + + if (node != null) { + setFocus(node); + redraw(); + } + } + + /** + * Method to traverse home. + */ + public void traverseHome() { + Object selectedNode = getFocusNode(); + if (selectedNode == null) { + traverseLeft(); + } + GraphNode node = null; + + if (selectedNode instanceof BaseMessage) { + if (((BaseMessage) selectedNode).getStartLifeline() != null) { + node = fFrame.getNextLifelineMessage(((BaseMessage) selectedNode).getStartLifeline(), null); + } else { + node = fFrame.getNextLifelineMessage(((BaseMessage) selectedNode).getEndLifeline(), null); + } + } else if (selectedNode instanceof Lifeline) { + node = fFrame.getNextLifelineMessage((Lifeline) selectedNode, null); + } else if (selectedNode instanceof BasicExecutionOccurrence) { + node = fFrame.getFirstExecution(((BasicExecutionOccurrence) selectedNode).getLifeline()); + } else { + if (fFrame.lifeLinesCount() > 0) { + Lifeline lifeline = fFrame.getLifeline(0); + node = fFrame.getNextLifelineMessage(lifeline, null); + } + } + + if (node != null) { + setFocus(node); + redraw(); + } + } + + /** + * Method to traverse to the end. + */ + public void traverseEnd() { + Object selectedNode = getFocusNode(); + if (selectedNode == null) { + traverseLeft(); + } + GraphNode node; + if (selectedNode instanceof BaseMessage) { + node = fFrame.getPrevLifelineMessage(((BaseMessage) selectedNode).getStartLifeline(), null); + } else if (selectedNode instanceof Lifeline) { + node = fFrame.getPrevLifelineMessage((Lifeline) selectedNode, null); + } else if (selectedNode instanceof BasicExecutionOccurrence) { + node = fFrame.getLastExecOccurrence(((BasicExecutionOccurrence) selectedNode).getLifeline()); + } else { + if (fFrame.lifeLinesCount() > 0) { + Lifeline lifeline = fFrame.getLifeline(0); + node = fFrame.getPrevLifelineMessage(lifeline, null); + } else { + return; + } + } + + if (node != null) { + setFocus(node); + redraw(); + } + } + + /** + * Method to print UI. + * + * @param sdPrintDialog the sequence diagram printer dialog. + */ + public void printUI(SDPrintDialogUI sdPrintDialog) { + PrinterData data = sdPrintDialog.getPrinterData(); + + if ((data == null) || (fFrame == null)) { + return; + } + + fPrinter = new Printer(data); + + String jobName = MessageFormat.format(Messages.SequenceDiagram_plus, new Object[] { String.valueOf(fSite.getContentDescription()), String.valueOf(fFrame.getName()) }); + fPrinter.startJob(jobName); + + GC gc = new GC(fPrinter); + + float lastZoom = fZoomValue; + + Rectangle area = getClientArea(); + GC gcim = null; + + gcim = gc; + NGC context = new NGC(this, gcim); + + // Set the metrics to use for lifeline text and message text + // using the Graphical Context + Metrics.setLifelineFontHeight(context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE))); + Metrics.setLifelineFontWidth(context.getFontWidth(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE))); + Metrics.setLifelineWidth(SDViewPref.getInstance().getLifelineWidth()); + Metrics.setFrameFontHeight(context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_FRAME_NAME))); + Metrics.setLifelineHeaderFontHeight(context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE_HEADER))); + + int syncMessFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_SYNC_MESS)); + int syncMessRetFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_SYNC_MESS_RET)); + int asyncMessFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_ASYNC_MESS)); + int asyncMessRetFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_ASYNC_MESS_RET)); + + int messageFontHeight = 0; + if (syncMessFontH > syncMessRetFontH) { + messageFontHeight = syncMessFontH; + } else { + messageFontHeight = syncMessRetFontH; + } + if (messageFontHeight < asyncMessFontH) { + messageFontHeight = asyncMessFontH; + } + if (messageFontHeight < asyncMessRetFontH) { + messageFontHeight = asyncMessRetFontH; + } + Metrics.setMessageFontHeight(messageFontHeight); + context.setFont(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE)); + + int width = Math.round((fFrame.getWidth() + 2 * Metrics.FRAME_H_MARGIN) * fZoomValue); + int height = Math.round((fFrame.getHeight() + 2 * Metrics.FRAME_V_MARGIN) * fZoomValue); + if (width < area.width) { + width = area.width; + } + if (height < area.height) { + height = area.height; + } + resizeContents(width, height); + + context.setBackground(SDViewPref.getInstance().getBackGroundColor(ISDPreferences.PREF_FRAME)); + context.fillRectangle(0, 0, getContentsWidth(), Metrics.FRAME_V_MARGIN); + context.fillRectangle(0, 0, fFrame.getX(), getContentsHeight()); + context.fillRectangle(fFrame.getX() + fFrame.getWidth() + 1, 0, getContentsWidth() - (fFrame.getX() + fFrame.getWidth() + 1), getContentsHeight()); + context.fillRectangle(0, fFrame.getY() + fFrame.getHeight() + 1, getContentsWidth(), getContentsHeight() - (fFrame.getY() + fFrame.getHeight() + 1)); + gcim.setLineWidth(1); + + fPrinter.startPage(); + fZoomValue = lastZoom; + + int restoreX = getContentsX(); + int restoreY = getContentsY(); + + float zh = sdPrintDialog.getStepY() * sdPrintDialog.getZoomFactor(); + float zw = sdPrintDialog.getStepX() * sdPrintDialog.getZoomFactor(); + + float zoomValueH = fPrinter.getClientArea().height / zh; + float zoomValueW = fPrinter.getClientArea().width / zw; + if (zoomValueH > zoomValueW) { + fPrinterZoom = zoomValueH; + } else { + fPrinterZoom = zoomValueW; + } + + if (sdPrintDialog.printSelection()) { + int[] pagesList = sdPrintDialog.getPageList(); + + for (int pageIndex = 0; pageIndex < pagesList.length; pageIndex++) { + printPage(pagesList[pageIndex], sdPrintDialog, context); + } + } else if (sdPrintDialog.printAll()) { + for (int pageIndex = 1; pageIndex <= sdPrintDialog.maxNumOfPages(); pageIndex++) { + printPage(pageIndex, sdPrintDialog, context); + } + } else if (sdPrintDialog.printCurrent()) { + printPage(getContentsX(), getContentsY(), sdPrintDialog, context, 1); + } else if (sdPrintDialog.printRange()) { + for (int pageIndex = sdPrintDialog.getFrom(); pageIndex <= sdPrintDialog.maxNumOfPages() && pageIndex <= sdPrintDialog.getTo(); pageIndex++) { + printPage(pageIndex, sdPrintDialog, context); + } + } + + fPrinter.endJob(); + fIsPrinting = false; + + gc.dispose(); + context.dispose(); + + fZoomValue = lastZoom; + fPrinter.dispose(); + setContentsPos(restoreX, restoreY); + } + + /** + * Method to print. + */ + public void print() { + SDPrintDialog sdPrinter = new SDPrintDialog(this.getShell(), this); + try { + if (sdPrinter.open() != 0) { + return; + } + } catch (Exception e) { + Activator.getDefault().logError("Error creating image", e); //$NON-NLS-1$ + return; + } + printUI(sdPrinter.getDialogUI()); + } + + /** + * Method to print a page. + * + * @param pageNum The page number + * @param pd The sequence diagram print dialog + * @param context The graphical context + */ + public void printPage(int pageNum, SDPrintDialogUI pd, NGC context) { + int j = pageNum / pd.getNbRow(); + int i = pageNum % pd.getNbRow(); + if (i != 0) { + j++; + } else { + i = pd.getNbRow(); + } + + i--; + j--; + + i = (int) (i * pd.getStepX()); + j = (int) (j * pd.getStepY()); + + printPage(i, j, pd, context, pageNum); + + fPrinter.endPage(); + } + + /** + * Method to print page ranges. + * + * @param i + * The start page + * @param j + * The end page + * @param pd + * The sequence diagram print dialog + * @param context + * The graphical context + * @param pageNum + * The current page + */ + public void printPage(int i, int j, SDPrintDialogUI pd, NGC context, int pageNum) { + fIsPrinting = false; + int pageNumFontZoom = fPrinter.getClientArea().height / getVisibleHeight(); + fPrinterX = i; + fPrinterY = j; + setContentsPos(i, j); + update(); + fIsPrinting = true; + float lastZoom = fZoomValue; + fZoomValue = fPrinterZoom * lastZoom; + + fFrame.draw(context); + + fZoomValue = pageNumFontZoom; + context.setFont(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE)); + String currentPageNum = String.valueOf(pageNum); + int ii = context.textExtent(currentPageNum); + int jj = context.getCurrentFontHeight(); + fZoomValue = fPrinterZoom * lastZoom; + context.drawText(currentPageNum, Math.round(fPrinterX + getVisibleWidth() / fPrinterZoom - ii / fPrinterZoom), Math.round(fPrinterY + getVisibleHeight() / fPrinterZoom - jj / fPrinterZoom), false); + fIsPrinting = false; + fZoomValue = lastZoom; + } + + /** + * Sets the collapse provider. + * + * @param provider The collapse provider to set + */ + protected void setCollapseProvider(ISDCollapseProvider provider) { + fCollapseProvider = provider; + } + + + /** + * Checks for focus of children. + * + * @param children Control to check + * @return true if child is on focus else false + */ + protected boolean checkFocusOnChilds(Control children) { + if (children instanceof Composite) { + Control[] child = ((Composite) children).getChildren(); + for (int i = 0; i < child.length; i++) { + if (child[i].isFocusControl()) { + return true; + } + checkFocusOnChilds(child[i]); + } + } + return false; + } + + /** + * A post action for a tooltip (before displaying). + * + * @param accessible true if accessible else false + * @return the tooltip text. + */ + protected String getPostfixForTooltip(boolean accessible) { + StringBuffer postfix = new StringBuffer(); + // Determine if the tooltip must show the time difference between the current mouse position and + // the last selected graphNode + if ((fCurrentGraphNode != null) && + (fCurrentGraphNode instanceof ITimeRange) && + (fToolTipNode instanceof ITimeRange) && + (fCurrentGraphNode != fToolTipNode) && + ((ITimeRange) fToolTipNode).hasTimeInfo() && + ((ITimeRange) fCurrentGraphNode).hasTimeInfo()) { + postfix.append(" -> "); //$NON-NLS-1$ + postfix.append(fCurrentGraphNode.getName()); + postfix.append("\n"); //$NON-NLS-1$ + postfix.append(Messages.SequenceDiagram_Delta); + postfix.append(" "); //$NON-NLS-1$ + + //double delta = ((ITimeRange)toolTipNode).getLastTime()-((ITimeRange)currentGraphNode).getLastTime(); + ITmfTimestamp firstTime = ((ITimeRange) fCurrentGraphNode).getEndTime(); + ITmfTimestamp lastTime = ((ITimeRange) fToolTipNode).getEndTime(); + ITmfTimestamp delta = lastTime.getDelta(firstTime); + postfix.append(delta.toString()); + + } else { + if ((fToolTipNode instanceof ITimeRange) && ((ITimeRange) fToolTipNode).hasTimeInfo()) { + postfix.append("\n"); //$NON-NLS-1$ + ITmfTimestamp firstTime = ((ITimeRange) fToolTipNode).getStartTime(); + ITmfTimestamp lastTime = ((ITimeRange) fToolTipNode).getEndTime(); + + if (firstTime != null) { + if (lastTime != null && firstTime.compareTo(lastTime, true) != 0) { + postfix.append("start: "); //$NON-NLS-1$ + postfix.append(firstTime.toString()); + postfix.append("\n"); //$NON-NLS-1$ + postfix.append("end: "); //$NON-NLS-1$ + postfix.append(lastTime.toString()); + postfix.append("\n"); //$NON-NLS-1$ + } else { + postfix.append(firstTime.toString()); + } + } + else if (lastTime != null) { + postfix.append(lastTime.toString()); + } + } + } + return postfix.toString(); + } + + /** + * Sets a new focused widget. + * + * @param newFocusShape A new focus shape. + */ + protected void setFocus(int newFocusShape) { + fFocusedWidget = newFocusShape; + if (fFocusedWidget == -1) { + getViewControl().getAccessible().setFocus(ACC.CHILDID_SELF); + } else { + getViewControl().getAccessible().setFocus(fFocusedWidget); + } + } + + /** + * Highlight the given GraphNode<br> + * The GraphNode is then displayed using the system default selection color + * + * @param node the GraphNode to highlight + */ + protected void performSelection(GraphNode node) { + if ((fCtrlSelection) || (fShiftSelection)) { + if (node != null) { + if (fSelectedNodeList.contains(node)) { + removeSelection(node); + } else { + addSelection(node); + } + } else { + return; + } + } else { + clearSelection(); + if (node != null) { + addSelection(node); + } + } + } + + /** + * Returns a draw buffer image. + * + * @return a Image containing the draw buffer. + */ + protected Image getDrawBuffer() { + + update(); + Rectangle area = getClientArea(); + Image dbuffer = new Image(getDisplay(), area.width, area.height); + GC gcim = new GC(dbuffer); + NGC context = new NGC(this, gcim); + + // Set the metrics to use for lifeline text and message text + // using the Graphical Context + Metrics.setLifelineFontHeight(context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE))); + Metrics.setLifelineFontWidth(context.getFontWidth(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE))); + Metrics.setLifelineWidth(SDViewPref.getInstance().getLifelineWidth()); + Metrics.setFrameFontHeight(context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_FRAME_NAME))); + Metrics.setLifelineHeaderFontHeight(context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE_HEADER))); + + int syncMessFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_SYNC_MESS)); + int syncMessRetFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_SYNC_MESS_RET)); + int asyncMessFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_ASYNC_MESS)); + int asyncMessRetFontH = context.getFontHeight(SDViewPref.getInstance().getFont(ISDPreferences.PREF_ASYNC_MESS_RET)); + + int messageFontHeight = 0; + if (syncMessFontH > syncMessRetFontH) { + messageFontHeight = syncMessFontH; + } else { + messageFontHeight = syncMessRetFontH; + } + if (messageFontHeight < asyncMessFontH) { + messageFontHeight = asyncMessFontH; + } + if (messageFontHeight < asyncMessRetFontH) { + messageFontHeight = asyncMessRetFontH; + } + Metrics.setMessageFontHeight(messageFontHeight); + context.setFont(SDViewPref.getInstance().getFont(ISDPreferences.PREF_LIFELINE)); + + int width = (int) ((fFrame.getWidth() + 2 * Metrics.FRAME_H_MARGIN) * fZoomValue); + int height = (int) ((fFrame.getHeight() + 2 * Metrics.FRAME_V_MARGIN) * fZoomValue); + + resizeContents(width, height); + + context.setBackground(SDViewPref.getInstance().getBackGroundColor(ISDPreferences.PREF_FRAME)); + context.fillRectangle(0, 0, getContentsWidth(), Metrics.FRAME_V_MARGIN); + context.fillRectangle(0, 0, fFrame.getX(), getContentsHeight()); + context.fillRectangle(fFrame.getX() + fFrame.getWidth() + 1, 0, getContentsWidth() - (fFrame.getX() + fFrame.getWidth() + 1), getContentsHeight()); + context.fillRectangle(0, fFrame.getY() + fFrame.getHeight() + 1, getContentsWidth(), getContentsHeight() - (fFrame.getY() + fFrame.getHeight() + 1)); + gcim.setLineWidth(1); + + fFrame.draw(context); + if (fDragAndDrop != null) { + Lifeline node = fDragAndDrop; + boolean isSelected = fDragAndDrop.isSelected(); + boolean hasFocus = fDragAndDrop.hasFocus(); + node.setSelected(false); + node.setFocused(false); + node.draw(context, fDragX, fDragY); + node.setSelected(isSelected); + node.setFocused(hasFocus); + } + gcim.dispose(); + context.dispose(); + return dbuffer; + } + + @Override + protected void keyPressedEvent(KeyEvent event) { + if (!(isFocusControl() || getViewControl().isFocusControl())) { + Control[] child = getParent().getChildren(); + for (int i = 0; i < child.length; i++) { + if ((child[i].isFocusControl())&& (!(child[i] instanceof ScrollView))) { + getViewControl().setFocus(); + break; + } + } + } + setFocus(-1); + + if (event.keyCode == SWT.CTRL) { + fCtrlSelection = true; + } + if (event.keyCode == SWT.SHIFT) { + fShiftSelection = true; + fPrevList = new ArrayList<>(); + fPrevList.addAll(getSelection()); + } + + GraphNode prevNode = getFocusNode(); + + if (event.keyCode == SWT.ARROW_RIGHT) { + traverseRight(); + } + + if (event.keyCode == SWT.ARROW_LEFT) { + traverseLeft(); + } + + if (event.keyCode == SWT.ARROW_DOWN) { + traverseDown(); + } + + if (event.keyCode == SWT.ARROW_UP) { + traverseUp(); + } + + if (event.keyCode == SWT.HOME) { + traverseHome(); + } + + if (event.keyCode == SWT.END) { + traverseEnd(); + } + + if ((!fShiftSelection) && (!fCtrlSelection)) { + fListStart = fCurrentGraphNode; + } + + if (event.character == ' ') { + performSelection(fCurrentGraphNode); + if (!fShiftSelection) { + fListStart = fCurrentGraphNode; + } + } + + if ((fShiftSelection) && (prevNode != getFocusNode())) { + clearSelection(); + addSelection(fPrevList); + addSelection(fFrame.getNodeList(fListStart, getFocusNode())); + if (getFocusNode() instanceof Lifeline) { + ensureVisible(getFocusNode().getX(), getFocusNode().getY(), getFocusNode().getWidth(), getFocusNode().getHeight(), SWT.CENTER | SWT.VERTICAL, true); + } else { + ensureVisible(getFocusNode()); + } + } else if ((!fCtrlSelection) && (!fShiftSelection)) { + + clearSelection(); + if (getFocusNode() != null) { + addSelection(getFocusNode()); + + if (getFocusNode() instanceof Lifeline) { + ensureVisible(getFocusNode().getX(), getFocusNode().getY(), getFocusNode().getWidth(), getFocusNode().getHeight(), SWT.CENTER | SWT.VERTICAL, true); + } else { + ensureVisible(getFocusNode()); + } + } + } + + if (fCurrentGraphNode != null) { + fCurrentGraphNode.setFocused(true); + } + redraw(); + + if ((event.character == ' ') && ((fZoomInMode) || (fZoomOutMode))) { + int cx = Math.round((getContentsX() + getVisibleWidth() / 2) / fZoomValue); + int cy = Math.round((getContentsY() + getVisibleHeight() / 2) / fZoomValue); + if (fZoomInMode) { + if (fZoomValue < 64) { + fZoomValue = fZoomValue * (float) 1.25; + } + } else { + fZoomValue = fZoomValue / (float) 1.25; + } + int x = Math.round(cx * fZoomValue - getVisibleWidth() / (float) 2); + int y = Math.round(cy * fZoomValue - getVisibleHeight() / (float) 2); + setContentsPos(x, y); + if (fTimeBar != null) { + fTimeBar.setZoom(fZoomValue); + } + // redraw also resize the scrollView content + redraw(); + } + } + + @Override + protected void keyReleasedEvent(KeyEvent event) { + setFocus(-1); + if (event.keyCode == SWT.CTRL) { + fCtrlSelection = false; + } + if (event.keyCode == SWT.SHIFT) { + fShiftSelection = false; + } + super.keyReleasedEvent(event); + setFocus(1); + } + + @Override + public boolean isFocusControl() { + Control[] child = getChildren(); + for (int i = 0; i < child.length; i++) { + if (child[i].isFocusControl()) { + return true; + } + checkFocusOnChilds(child[i]); + } + return false; + } + + @Override + public boolean setContentsPos(int x, int y) { + int localX = x; + int localY = y; + + if (localX < 0) { + localX = 0; + } + if (localY < 0) { + localY = 0; + } + if (fFrame == null) { + return false; + } + if (localX + getVisibleWidth() > getContentsWidth()) { + localX = getContentsWidth() - getVisibleWidth(); + } + if (localY + getVisibleHeight() > getContentsHeight()) { + localY = getContentsHeight() - getVisibleHeight(); + } + int x1 = Math.round(localX / fZoomValue); + int y2 = Math.round(localY / fZoomValue); + int width = Math.round(getVisibleWidth() / fZoomValue); + int height = Math.round(getVisibleHeight() / fZoomValue); + fFrame.updateIndex(x1, y2, width, height); + + if (fInsertionCartet != null && fInsertionCartet.isVisible()) { + fInsertionCartet.setVisible(false); + } + + return super.setContentsPos(localX, localY); + } + + @Override + protected void contentsMouseHover(MouseEvent event) { + GraphNode graphNode = null; + if (fFrame != null) { + int x = Math.round(event.x / fZoomValue); + int y = Math.round(event.y / fZoomValue); + graphNode = fFrame.getNodeAt(x, y); + if ((graphNode != null) && (SDViewPref.getInstance().tooltipEnabled())) { + fToolTipNode = graphNode; + String postfix = getPostfixForTooltip(true); + if (graphNode instanceof Lifeline) { + Lifeline lifeline = (Lifeline) graphNode; + fToolTip.showToolTip(lifeline.getToolTipText() + postfix); + setFocus(0); + } else { + fToolTip.showToolTip(graphNode.getName() + postfix); + setFocus(0); + } + } else { + fToolTip.hideToolTip(); + } + } + } + + @Override + protected void contentsMouseMoveEvent(MouseEvent e) { + fScrollToolTip.hideToolTip(); + fToolTip.hideToolTip(); + if (!(isFocusControl() || getViewControl().isFocusControl())) { + Control[] child = getParent().getChildren(); + for (int i = 0; i < child.length; i++) { + if ((child[i].isFocusControl()) && (!(child[i] instanceof ScrollView))) { + getViewControl().setFocus(); + break; + } + } + } + setFocus(-1); + + if (((e.stateMask & SWT.BUTTON_MASK) != 0) && ((fDragAndDrop != null) || fIsDragAndDrop) && (fReorderMode || fCollapseProvider != null)) { + fIsDragAndDrop = false; + if (fCurrentGraphNode instanceof Lifeline) { + fDragAndDrop = (Lifeline) fCurrentGraphNode; + } + if (fDragAndDrop != null) { + int dx = 0; + int dy = 0; + if (e.x > getContentsX() + getVisibleWidth()) { + dx = e.x - (getContentsX() + getVisibleWidth()); + } else if (e.x < getContentsX()) { + dx = -getContentsX() + e.x; + } + if (e.y > getContentsY() + getVisibleHeight()) { + dy = e.y - (getContentsY() + getVisibleHeight()); + } else if (e.y < getContentsY()) { + dy = -getContentsY() + e.y; + } + fDragX = e.x; + fDragY = e.y; + if (dx != 0 || dy != 0) { + if (fLocalAutoScroll == null) { + if (fLocalAutoScrollTimer == null) { + fLocalAutoScrollTimer = new Timer(true); + } + fLocalAutoScroll = new AutoScroll(this, dx, dy); + fLocalAutoScrollTimer.schedule(fLocalAutoScroll, 0, 75); + } else { + fLocalAutoScroll.fDeltaX = dx; + fLocalAutoScroll.fDeltaY = dy; + } + } else if (fLocalAutoScroll != null) { + fLocalAutoScroll.cancel(); + fLocalAutoScroll = null; + } + fDragX = Math.round(e.x / fZoomValue); + fDragY = Math.round(e.y / fZoomValue); + redraw(); + Lifeline node = fFrame.getCloserLifeline(fDragX); + if ((node != null) && (node != fDragAndDrop)) { + int y = 0; + int y1 = 0; + int height = Metrics.getLifelineHeaderFontHeigth() + 2 * Metrics.LIFELINE_HEARDER_TEXT_V_MARGIN; + int hMargin = Metrics.LIFELINE_VT_MAGIN / 4; + int x = node.getX(); + int width = node.getWidth(); + if (fFrame.getVisibleAreaY() < node.getY() + node.getHeight() - height - hMargin) { + y = contentsToViewY(Math.round((node.getY() + node.getHeight()) * fZoomValue)); + } else { + y = Math.round(height * fZoomValue); + } + + if (fFrame.getVisibleAreaY() < contentsToViewY(node.getY() - hMargin)) { + y1 = contentsToViewY(Math.round((node.getY() - hMargin) * fZoomValue)); + } else { + y1 = Math.round(height * fZoomValue); + } + + int rx = Math.round(x * fZoomValue); + + fInsertionCartet.setVisible(true); + if ((fInsertionCartet.getImage() != null) && (!fInsertionCartet.getImage().isDisposed())) { + fInsertionCartet.getImage().dispose(); + } + if (rx <= e.x && Math.round(rx + (width * fZoomValue)) >= e.x) { + if (fCollapseProvider != null) { + ImageData data = fCollapaseCaretImg.getImageData(); + data = data.scaledTo(Math.round(fCollapaseCaretImg.getBounds().width * fZoomValue), Math.round(fCollapaseCaretImg.getBounds().height * fZoomValue)); + fCurrentCaretImage = new Image(Display.getCurrent(), data); + fInsertionCartet.setImage(fCurrentCaretImage); + fInsertionCartet.setLocation(contentsToViewX(rx + Math.round((width / (float) 2) * fZoomValue)) - fCurrentCaretImage.getBounds().width / 2, y); + } + } else if (fReorderMode) { + if (rx > e.x) { + if (node.getIndex() > 1 && fFrame.getLifeline(node.getIndex() - 2) == fDragAndDrop) { + return; + } + ImageData data = fArrowUpCaretImg.getImageData(); + data = data.scaledTo(Math.round(fArrowUpCaretImg.getBounds().width * fZoomValue), Math.round(fArrowUpCaretImg.getBounds().height * fZoomValue)); + fCurrentCaretImage = new Image(Display.getCurrent(), data); + fInsertionCartet.setImage(fCurrentCaretImage); + fInsertionCartet.setLocation(contentsToViewX(Math.round((x - Metrics.LIFELINE_SPACING / 2) * fZoomValue)) - fCurrentCaretImage.getBounds().width / 2, y1); + } else { + if (node.getIndex() < fFrame.lifeLinesCount() && fFrame.getLifeline(node.getIndex()) == fDragAndDrop) { + return; + } + ImageData data = fArrowUpCaretImg.getImageData(); + data = data.scaledTo(Math.round(fArrowUpCaretImg.getBounds().width * fZoomValue), Math.round(fArrowUpCaretImg.getBounds().height * fZoomValue)); + fCurrentCaretImage = new Image(Display.getCurrent(), data); + fInsertionCartet.setImage(fCurrentCaretImage); + fInsertionCartet.setLocation(contentsToViewX(Math.round((x + width + Metrics.LIFELINE_SPACING / 2) * fZoomValue)) - fCurrentCaretImage.getBounds().width / 2 + 1, y1); + } + } + } else { + fInsertionCartet.setVisible(false); + } + } + } else { + super.contentsMouseMoveEvent(e); + } + } + + @Override + protected void contentsMouseUpEvent(MouseEvent event) { + // Just in case the diagram highlight a time compression region + // this region need to be released when clicking everywhere + fInsertionCartet.setVisible(false); + if (fDragAndDrop != null) { + if ((fOverView != null) && (!fOverView.isDisposed())) { + fOverView.dispose(); + } + fOverView = null; + Lifeline node = fFrame.getCloserLifeline(fDragX); + if (node != null) { + int rx = Math.round(node.getX() * fZoomValue); + if (rx <= event.x && Math.round(rx + (node.getWidth() * fZoomValue)) >= event.x) { + if ((fCollapseProvider != null) && (fDragAndDrop != node)) { + fCollapseProvider.collapseTwoLifelines(fDragAndDrop, node); + } + } else if (rx < event.x) { + fFrame.insertLifelineAfter(fDragAndDrop, node); + if (node.getIndex() < fFrame.lifeLinesCount()) { + Lifeline temp[] = { fDragAndDrop, fFrame.getLifeline(node.getIndex()) }; + fReorderList.add(temp); + } else { + Lifeline temp[] = { fDragAndDrop, null }; + fReorderList.add(temp); + } + } else { + fFrame.insertLifelineBefore(fDragAndDrop, node); + Lifeline temp[] = { fDragAndDrop, node }; + fReorderList.add(temp); + } + } + } + fDragAndDrop = null; + redraw(); + if (fFrame == null) { + return; + } + fFrame.resetTimeCompression(); + + // reset auto scroll if it's engaged + if (fLocalAutoScroll != null) { + fLocalAutoScroll.cancel(); + fLocalAutoScroll = null; + } + super.contentsMouseUpEvent(event); + } + + @Override + protected void contentsMouseDownEvent(MouseEvent event) { + if (fCurrentGraphNode != null) { + fCurrentGraphNode.setFocused(false); + } + + // Just in case the diagram highlight a time compression region + // this region need to be released when clicking everywhere + if (fFrame == null) { + return; + } + + fFrame.resetTimeCompression(); + + if ((event.stateMask & SWT.CTRL) != 0) { + fCtrlSelection = true; + } else { + fCtrlSelection = false; + } + + if (((fZoomInMode) || (fZoomOutMode)) && (event.button == 1)) { + int cx = Math.round(event.x / fZoomValue); + int cy = Math.round(event.y / fZoomValue); + if (fZoomInMode) { + if (fZoomValue < 64) { + fZoomValue = fZoomValue * (float) 1.25; + } + } else { + fZoomValue = fZoomValue / (float) 1.25; + } + int x = Math.round(cx * fZoomValue - getVisibleWidth() / (float) 2); + int y = Math.round(cy * fZoomValue - getVisibleHeight() / (float) 2); + setContentsPos(x, y); + if (fTimeBar != null) { + fTimeBar.setZoom(fZoomValue); + } + // redraw also resize the scrollView content + redraw(); + } else { + GraphNode node = null; + int x = Math.round(event.x / fZoomValue); + int y = Math.round(event.y / fZoomValue); + node = fFrame.getNodeAt(x, y); + + if ((event.button == 1) || ((node != null) && !node.isSelected())) { + if (!fShiftSelection) { + fListStart = node; + } + if (fShiftSelection) { + clearSelection(); + addSelection(fFrame.getNodeList(fListStart, node)); + } else { + performSelection(node); + } + fCurrentGraphNode = node; + if (node != null) { + node.setFocused(true); + } + } + redraw(); + } + if (fDragAndDrop == null) { + super.contentsMouseDownEvent(event); + } + fIsDragAndDrop = (event.button == 1); + + } + + /** + * TimerTask for auto scroll feature. + */ + protected static class AutoScroll extends TimerTask { + /** + * Field delta x. + */ + public int fDeltaX; + /** + * Field delta y. + */ + public int fDeltaY; + /** + * Field sequence diagram reference. + */ + public SDWidget fSdWidget; + + /** + * Constructor for AutoScroll. + * @param sv sequence diagram widget reference + * @param dx delta x + * @param dy delta y + */ + public AutoScroll(SDWidget sv, int dx, int dy) { + fSdWidget = sv; + fDeltaX = dx; + fDeltaY = dy; + } + + @Override + public void run() { + Display display = Display.getDefault(); + if ((display == null) || (display.isDisposed())) { + return; + } + display.asyncExec(new Runnable() { + @Override + public void run() { + if (fSdWidget.isDisposed()) { + return; + } + fSdWidget.fDragX += fDeltaX; + fSdWidget.fDragY += fDeltaY; + fSdWidget.scrollBy(fDeltaX, fDeltaY); + } + }); + } + } + + @Override + protected void drawContents(GC gc, int clipx, int clipy, int clipw, int cliph) { + if (fFrame == null) { + gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE)); + gc.fillRectangle(0, 0, getVisibleWidth(), getVisibleHeight()); + gc.dispose(); + return; + } + SDViewPref.getInstance(); + + Rectangle area = getClientArea(); + Image dbuffer = getDrawBuffer(); + int height = Math.round((fFrame.getHeight() + 2 * Metrics.FRAME_V_MARGIN) * fZoomValue); + + try { + gc.drawImage(dbuffer, 0, 0, area.width, area.height, 0, 0, area.width, area.height); + } catch (Exception e) { + Activator.getDefault().logError("Error drawin content", e); //$NON-NLS-1$ + } + dbuffer.dispose(); + setHScrollBarIncrement(Math.round(SDViewPref.getInstance().getLifelineWidth() / (float) 2 * fZoomValue)); + setVScrollBarIncrement(Math.round(Metrics.getMessagesSpacing() * fZoomValue)); + if ((fTimeBar != null) && (fFrame.hasTimeInfo())) { + fTimeBar.resizeContents(9, height + getHorizontalBarHeight()); + fTimeBar.setContentsPos(getContentsX(), getContentsY()); + fTimeBar.redraw(); + fTimeBar.update(); + } + float xRatio = getContentsWidth() / (float) getVisibleWidth(); + float yRatio = getContentsHeight() / (float) getVisibleHeight(); + if (yRatio > xRatio) { + setOverviewSize((int) (getVisibleHeight() * 0.75)); + } else { + setOverviewSize((int) (getVisibleWidth() * 0.75)); + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent event) { + } + + @Override + public void widgetSelected(SelectionEvent event) { + if (event.widget == fZoomIn) { + fZoomValue = fZoomValue * 2; + } else if (event.widget == fZoomOut) { + fZoomValue = fZoomValue / 2; + } + redraw(); + } + + /** + * Called when property changed occurs in the preference page. "PREFOK" is + * fired when the user press the ok or apply button + */ + @Override + public void propertyChange(PropertyChangeEvent e) { + if (fFrame != null && !isDisposed()) { + fFrame.resetTimeCompression(); + } + if (e.getProperty().equals("PREFOK")) //$NON-NLS-1$ + { + // Prepare the overview to be reused for the new + // settings (especially the colors) + if (fOverView != null) { + fOverView.dispose(); + } + fOverView = null; + redraw(); + } + } + + @Override + public void widgetDisposed(DisposeEvent e) { + if (fOverView != null) { + fOverView.dispose(); + } + super.removeDisposeListener(this); + if ((fCurrentCaretImage != null) && (!fCurrentCaretImage.isDisposed())) { + fCurrentCaretImage.dispose(); + } + if ((fArrowUpCaretImg != null) && (!fArrowUpCaretImg.isDisposed())) { + fArrowUpCaretImg.dispose(); + } + if ((fCollapaseCaretImg != null) && (!fCollapaseCaretImg.isDisposed())) { + fCollapaseCaretImg.dispose(); + } + SDViewPref.getInstance().removePropertyChangeListener(this); + LoadersManager lm = LoadersManager.getInstance(); + if (fSite instanceof SDView) { + ((SDView) fSite).resetProviders(); + if (lm != null) { + lm.resetLoader(((SDView) fSite).getViewSite().getId()); + } + } + } + + @Override + protected void drawOverview(GC gc, Rectangle r) { + float oldzoom = fZoomValue; + if (getContentsWidth() > getContentsHeight()) { + fZoomValue = (float) r.width / (float) getContentsWidth() * oldzoom; + } else { + fZoomValue = (float) r.height / (float) getContentsHeight() * oldzoom; + } + if ((fOverView != null) && ((r.width != fOverView.getBounds().width) || (r.height != fOverView.getBounds().height))) { + fOverView.dispose(); + fOverView = null; + } + if (fOverView == null) { + int backX = getContentsX(); + int backY = getContentsY(); + setContentsPos(0, 0); + fOverView = new Image(getDisplay(), r.width, r.height); + GC gcim = new GC(fOverView); + NGC context = new NGC(this, gcim); + context.setBackground(SDViewPref.getInstance().getBackGroundColor(ISDPreferences.PREF_FRAME)); + fFrame.draw(context); + setContentsPos(backX, backY); + gcim.dispose(); + context.dispose(); + } + if ((fOverView != null) && (r.width == fOverView.getBounds().width) && (r.height == fOverView.getBounds().height)) { + gc.drawImage(fOverView, 0, 0, r.width, r.height, 0, 0, r.width, r.height); + } + + fZoomValue = oldzoom; + + super.drawOverview(gc, r); + } + + @Override + public void deltaSelected(Lifeline lifeline, int startEvent, int nbEvent, IColor color) { + fFrame.highlightTimeCompression(lifeline, startEvent, nbEvent, color); + ensureVisible(lifeline); + int y1 = lifeline.getY() + lifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * startEvent; + int y2 = lifeline.getY() + lifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * (startEvent + nbEvent); + ensureVisible(lifeline.getX(), y1 - (Metrics.getLifelineHeaderFontHeigth() + +2 * Metrics.LIFELINE_HEARDER_TEXT_V_MARGIN), lifeline.getWidth(), y2 - y1 + 3, SWT.CENTER | SWT.VERTICAL, true); + redraw(); + update(); + } + + @Override + public int getVisibleWidth() { + if (fIsPrinting) { + return fPrinter.getClientArea().width; + } + return super.getVisibleWidth(); + } + + @Override + public int getVisibleHeight() { + if (fIsPrinting) { + return fPrinter.getClientArea().height; + } + return super.getVisibleHeight(); + } + + @Override + public int contentsToViewX(int x) { + if (fIsPrinting) { + int v = Math.round(fPrinterX * fPrinterZoom); + return x - v; + } + return x - getContentsX(); + } + + @Override + public int contentsToViewY(int y) { + if (fIsPrinting) { + int v = Math.round(fPrinterY * fPrinterZoom); + return y - v; + } + return y - getContentsY(); + } + + @Override + public int getContentsX() { + if (fIsPrinting) { + return Math.round(fPrinterX * fPrinterZoom); + } + return super.getContentsX(); + } + + @Override + public int getContentsY() { + if (fIsPrinting) { + return Math.round(fPrinterY * fPrinterZoom); + } + return super.getContentsY(); + } + + /** + * Traverse Listener implementation. + */ + protected static class LocalTraverseListener implements TraverseListener { + @Override + public void keyTraversed(TraverseEvent e) { + if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) { + e.doit = true; + } + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDWidgetSelectionProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDWidgetSelectionProvider.java new file mode 100755 index 0000000000..4dd0c8d011 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/SDWidgetSelectionProvider.java @@ -0,0 +1,88 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; + +/** + * <p> + * Informs all registered listeners of graph node selection change in the Frame. + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class SDWidgetSelectionProvider implements ISelectionProvider { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The listener list + */ + private List<ISelectionChangedListener> fListenerList = null; + + /** + * The current selection + */ + private ISelection fCurrentSelection = null; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Standard constructor + */ + protected SDWidgetSelectionProvider() { + fListenerList = new ArrayList<>(); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + if (!fListenerList.contains(listener)) { + fListenerList.add(listener); + } + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + fListenerList.remove(listener); + } + + @Override + public void setSelection(ISelection selection) { + fCurrentSelection = selection; + for (int i = 0; i < fListenerList.size(); i++) { + ISelectionChangedListener list = fListenerList.get(i); + list.selectionChanged(new SelectionChangedEvent(this, fCurrentSelection)); + } + } + + @Override + public ISelection getSelection() { + return fCurrentSelection; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/ScrollView.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/ScrollView.java new file mode 100755 index 0000000000..bf199965b2 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/ScrollView.java @@ -0,0 +1,2067 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import java.util.Timer; +import java.util.TimerTask; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TypedEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Scrollable; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * ScrollView widget provides a scrolling area with on-demand scroll bars. + * Overview scrollable panel can be used (@see setOverviewEnabled()). + * + * @author Eric Miravete + * @version 1.0 + */ +public class ScrollView extends Composite { + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * Scroll bar mode AUTO + */ + public static final int AUTO = 0; + /** + * Scroll bar mode ALWAYS_ON + */ + public static final int ALWAYS_ON = 1; + /** + * Scroll bar mode ALWAYS_OFF + */ + public static final int ALWAYS_OFF = 2; + /** + * Bit mask for visible vertical scroll bar + */ + public static final int VBAR = 0x01; + /** + * Bit mask for visible horizontal scroll bar + */ + public static final int HBAR = 0x02; + + private static final int DEFAULT_H_SCROLL_INCREMENT = 10; + private static final int DEFAULT_V_SCROLL_INCREMENT = 10; + private static final int DEFAULT_AUTO_SCROLL_PERIOD = 75; + private static final int DEFAULT_OVERVIEW_SIZE = 100; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * Value of the contents height property. + */ + private int fContentsHeight = 0; + /** + * Value of the contents width property. + */ + private int fContentsWidth = 0; + /** + * Value of the contents x coordinate property + */ + private int fContentsX = 0; + /** + * Value of the contents y coordinate property + */ + private int fContentsY = 0; + /** + * Scroll bar mode of horizontal scroll bar. + */ + private int fHorScrollbarMode = AUTO; + /** + * Scroll bar mode of vertical scroll bar. + */ + private int fVertScrollbarMode = AUTO; + /** + * Increment for the horizontal scroll bar. + */ + private int fHorScrollbarIncrement = DEFAULT_H_SCROLL_INCREMENT; + /** + * Increment for the vertical scroll bar. + */ + private int fVertScrollbarIncrement = DEFAULT_V_SCROLL_INCREMENT; + /** + * Flag whether auto scroll is enabled or not. + */ + private boolean fAutoScrollEnabled = true; + /** + * Value of the auto scroll period. + */ + private int fAutoScrollPeriod = DEFAULT_AUTO_SCROLL_PERIOD; + /** + * The local paint listener reference. + */ + private PaintListener fLocalPaintListener = null; + /** + * The local mouse move listener reference. + */ + private MouseMoveListener fLocalMouseMoveListener = null; + /** + * The local mouse listener reference. + */ + private MouseListener fLocalMouseListener = null; + /** + * The local control listener reference. + */ + private ControlListener fLocalControlListener = null; + /** + * The local key listener reference. + */ + private KeyListener fLocalKeyListener = null; + // Canvas for vertical/horizontal Scroll Bar only ... because new ScrollBar() does works. + /** + * Canvas for horizontal scroll bar. + */ + private Canvas fHorScrollBar; + /** + * Canvas for vertical scroll bar. + */ + private Canvas fVertScrollBar; + /** + * Canvas for the view control. + */ + private Canvas fViewControl; + /** + * Control used in the bottom right corner @see setCornerControl() and @see setOverviewEnabled(true) + */ + private Control fCornerControl; + /** + * Size of overview widget. + */ + private int fOverviewSize = DEFAULT_OVERVIEW_SIZE; // default size for overview + /** + * Timer for auto_scroll feature + */ + private AutoScroll fAutoScroll = null; + /** + * TimerTask for auto_scroll feature !=null when auto scroll is running + */ + private Timer fAutoScrollTimer = null; + /** + * where mouse down appear on contents area (x coordinate) + */ + private int fMouseDownX = -1; + /** + * where mouse down appear on contents area (y coordinate) + */ + private int fMousDownY = -1; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Create a ScrollView, child of composite c. Both scroll bar have the mode AUTO. Auto scroll feature is enabled + * using a delay of 250ms. Overview feature is not enabled by default (use setOverviewEnabled()). + * + * @param c The parent composite + * @param style The SWT style bits @see SWT + */ + public ScrollView(Composite c, int style) { + this(c, style, true); + } + + /** + * Create a ScrollView, child of composite c. Both scroll bar have the mode AUTO. Auto scroll feature is enabled + * using a delay of 250ms. Overview feature is not enabled by default (use setOverviewEnabled()). + * + * @param c The parent composite. + * @param style The SWT style bits @see SWT + * @param mouseWheel Flag to force scrollView to handles mouse wheel + */ + public ScrollView(Composite c, int style, boolean mouseWheel) { + super(c, SWT.NONE); + + fHorScrollBar = new Canvas(this, SWT.H_SCROLL); + if (mouseWheel) { + // force scroll bar to get mouse wheel, those scrollbar will be hidden + fViewControl = new Canvas(this, style | SWT.H_SCROLL | SWT.V_SCROLL); + } else { + fViewControl = new Canvas(this, style); + } + fViewControl.setBackground(getBackground()); + // hide scroll bar as their are replaced by fHorScrollBar and fVertScrollBar. + if (mouseWheel) { + fViewControl.getVerticalBar().setVisible(false); + fViewControl.getHorizontalBar().setVisible(false); + } + fVertScrollBar = new Canvas(this, SWT.V_SCROLL); + + setLayout(new SVLayout()); + + fLocalPaintListener = new PaintListener() { + @Override + public void paintControl(PaintEvent event) { + // use clipping, to reduce cost of paint. + Rectangle r = event.gc.getClipping(); + int cx = viewToContentsX(r.x); + int cy = viewToContentsY(r.y); + drawContents(event.gc, cx, cy, r.width, r.height); + } + }; + fViewControl.addPaintListener(fLocalPaintListener); + + fLocalMouseMoveListener = new MouseMoveListener() { + @Override + public void mouseMove(MouseEvent e) { + int ox = e.x, oy = e.y; + e.x = viewToContentsX(e.x); + e.y = viewToContentsY(e.y); + contentsMouseMoveEvent(e); + e.x = ox; + e.y = oy; + } + }; + + fViewControl.addMouseMoveListener(fLocalMouseMoveListener); + + MouseTrackListener localMouseTrackListener = new MouseTrackListener() { + @Override + public void mouseEnter(MouseEvent e) { + int ox = e.x, oy = e.y; + e.x = viewToContentsX(e.x); + e.y = viewToContentsY(e.y); + contentsMouseEnter(e); + e.x = ox; + e.y = oy; + } + + @Override + public void mouseHover(MouseEvent e) { + int ox = e.x, oy = e.y; + e.x = viewToContentsX(e.x); + e.y = viewToContentsY(e.y); + contentsMouseHover(e); + e.x = ox; + e.y = oy; + } + + @Override + public void mouseExit(MouseEvent e) { + int ox = e.x, oy = e.y; + e.x = viewToContentsX(e.x); + e.y = viewToContentsY(e.y); + contentsMouseExit(e); + e.x = ox; + e.y = oy; + } + + }; + + fViewControl.addMouseTrackListener(localMouseTrackListener); + + fLocalMouseListener = new MouseListener() { + @Override + public void mouseDoubleClick(MouseEvent e) { + int ox = e.x, oy = e.y; + e.x = viewToContentsX(e.x); + e.y = viewToContentsY(e.y); + contentsMouseDoubleClickEvent(e); + e.x = ox; + e.y = oy; + } + + @Override + public void mouseDown(MouseEvent e) { + int ox = e.x, oy = e.y; + e.x = viewToContentsX(e.x); + fMouseDownX = e.x; + e.y = viewToContentsY(e.y); + fMousDownY = e.y; + contentsMouseDownEvent(e); + e.x = ox; + e.y = oy; + } + + @Override + public void mouseUp(MouseEvent e) { + int ox = e.x, oy = e.y; + e.x = viewToContentsX(e.x); + e.y = viewToContentsY(e.y); + contentsMouseUpEvent(e); + e.x = ox; + e.y = oy; + // here because class extending me can catch mouse Up and want to scroll... + fMouseDownX = -1; + fMousDownY = -1; + } + }; + fViewControl.addMouseListener(fLocalMouseListener); + + fLocalKeyListener = new KeyListener() { + @Override + public void keyPressed(KeyEvent e) { + keyPressedEvent(e); + } + + @Override + public void keyReleased(KeyEvent e) { + keyReleasedEvent(e); + } + }; + + fViewControl.addKeyListener(fLocalKeyListener); + + getVerticalBar().addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + setContentsPos(fContentsX, getVerticalBar().getSelection()); + // need to change "hidden" vertical bar value ? + // force focus on fViewControl so we got future mouse wheel's scroll events + if (!fViewControl.isFocusControl()) { + fViewControl.setFocus(); + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + + if (fViewControl.getVerticalBar() != null) { + // add fViewControl hidden scrollbar listener to get mouse wheel ... + fViewControl.getVerticalBar().addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + ScrollBar b = fViewControl.getVerticalBar(); + setContentsPos(fContentsX, b.getSelection()); + // change "real" vertical bar selection too + getVerticalBar().setSelection(b.getSelection()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + getHorizontalBar().addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + setContentsPos(getHorizontalBar().getSelection(), fContentsY); + // need to change "real" horizontal bar too ? + // force focus on fViewControl so we got future mouse wheel's scroll events + if (!fViewControl.isFocusControl()) { + fViewControl.setFocus(); + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + if (fViewControl.getHorizontalBar() != null) { + fViewControl.getHorizontalBar().addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + ScrollBar b = fViewControl.getHorizontalBar(); + setContentsPos(b.getSelection(), fContentsY); + // change "real" vertical bar selection too + getHorizontalBar().setSelection(b.getSelection()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public boolean setFocus() { + return fViewControl.forceFocus(); + } + + @Override + public void setCursor(Cursor cursor) { + fViewControl.setCursor(cursor); + } + + @Override + public void dispose() { + if (fAutoScroll != null) { + fAutoScroll.cancel(); + fAutoScroll = null; + } + if (fViewControl != null) { + fViewControl.dispose(); + } + fViewControl = null; + if (fVertScrollBar != null) { + fVertScrollBar.dispose(); + } + fVertScrollBar = null; + if (fHorScrollBar != null) { + fHorScrollBar.dispose(); + } + fHorScrollBar = null; + if (fCornerControl != null) { + Object data = fCornerControl.getData(); + if (data instanceof Overview) { + ((Overview) data).dispose(); + } + fCornerControl.dispose(); + fCornerControl = null; + } + super.dispose(); + } + + @Override + public Rectangle getClientArea() { + Rectangle area = fViewControl.getClientArea(); + /* Clamp the size of the returned area to 1x1 minimum */ + area.width = Math.max(area.width, 1); + area.height = Math.max(area.height, 1); + return area; + } + + @Override + public void setBackground(Color c) { + super.setBackground(c); + fViewControl.setBackground(c); + } + + @Override + public void setToolTipText(String text) { + fViewControl.setToolTipText(text); + } + + /** + * Draw overview area, @see setOverviewEnabled. By default draw a rectangle corresponding to the visible area of + * scroll view. You can redefine this method to draw the contents as drawContents does... ...in an other magnify + * factor. + * + * @param gc GC to used to draw. + * @param r Rectangle corresponding to the client area of overview. + */ + protected void drawOverview(GC gc, Rectangle r) { + int x = (int) (r.width * fContentsX / (float) fContentsWidth); + int y = (int) (r.height * fContentsY / (float) fContentsHeight); + int vw = getVisibleWidth(); + int vh = getVisibleHeight(); + int w = r.width - 1; + if (fContentsWidth > vw) { + w = (int) (r.width * vw / (float) fContentsWidth); + } + int h = r.height - 1; + if (fContentsHeight > vh) { + h = (int) (r.height * vh / (float) fContentsHeight); + } + + gc.setForeground(getForeground()); + // too small rectangle ? + if (w < 5 || h < 5) { + // use a cross ... + gc.drawLine(x, 0, x, r.height); + gc.drawLine(0, y, r.width, y); + } else { + gc.drawRectangle(x, y, w, h); + } + } + + /** + * Remove the local Listener and add the new listener. + * + * @param nlistener the new listener + */ + public void replaceControlListener(ControlListener nlistener) { + if (fLocalControlListener != null) { + removeControlListener(fLocalControlListener); + fLocalControlListener = null; + } + addControlListener(nlistener); + } + + /** + * Remove the local Listener and add the new listener. + * + * @param nlistener the new listener + */ + public void replaceKeyListener(KeyListener nlistener) { + if (fLocalKeyListener != null) { + removeKeyListener(fLocalKeyListener); + fLocalKeyListener = null; + } + addKeyListener(nlistener); + } + + /** + * Remove the local Listener and add the new listener. + * + * @param nlistener the new listener + */ + public void replaceMouseListener(MouseListener nlistener) { + if (fLocalMouseListener != null) { + removeMouseListener(fLocalMouseListener); + fLocalMouseListener = null; + } + fViewControl.addMouseListener(nlistener); + } + + /** + * Remove the local Listener and add the new listener. + * + * @param nlistener the new listener + */ + public void replaceMouseMoveListener(MouseMoveListener nlistener) { + if (fLocalMouseMoveListener != null) { + removeMouseMoveListener(fLocalMouseMoveListener); + fLocalMouseMoveListener = null; + } + fViewControl.addMouseMoveListener(nlistener); + } + + /** + * Remove the local Listener and add the new listener. + * + * @param nlistener the new listener + */ + public void replacePaintListener(PaintListener nlistener) { + if (fLocalPaintListener != null) { + removePaintListener(fLocalPaintListener); + fLocalPaintListener = null; + } + fViewControl.addPaintListener(nlistener); + } + + /** + * Access method for the contentsHeight property. + * + * @return the current value of the contentsHeight property + */ + public int getContentsHeight() { + return fContentsHeight; + } + + /** + * Access method for the contentsWidth property. + * + * @return the current value of the contentsWidth property + */ + public int getContentsWidth() { + return fContentsWidth; + } + + /** + * Access method for the contentsX property. + * + * @return the current value of the contentsX property + */ + public int getContentsX() { + return fContentsX; + } + + /** + * Access method for the contentsY property. + * + * @return the current value of the contentsY property + */ + public int getContentsY() { + return fContentsY; + } + + /** + * Determines if the dragAutoScroll property is true. + * + * @return <code>true<code> if the dragAutoScroll property is true + */ + public boolean isDragAutoScroll() { + return fAutoScrollEnabled; + } + + /** + * Sets the value of the dragAutoScroll property. + * + * @param aDragAutoScroll the new value of the dragAutoScroll property + */ + public void setDragAutoScroll(boolean aDragAutoScroll) { + fAutoScrollEnabled = aDragAutoScroll; + if (!fAutoScrollEnabled && (fAutoScroll != null)) { + fAutoScroll.cancel(); + fAutoScroll = null; + } + } + + /** + * Change delay (in millisec) used for auto scroll feature. + * + * @param period new period between to auto scroll + */ + public void setDragAutoScrollPeriod(int period) { + fAutoScrollPeriod = Math.max(0, period); + } + + /** + * Return auto scroll period. + * + * @return The period + */ + public int getDragAutoScrollPeriod() { + return fAutoScrollPeriod; + } + + /** + * Access method for the hScrollBarMode property. + * + * @return the current value of the hScrollBarMode property + */ + public int getHScrollBarMode() { + return fHorScrollbarMode; + } + + /** + * Sets the value of the hScrollBarMode property. + * + * @param aHScrollBarMode the new value of the hScrollBarMode property + */ + public void setHScrollBarMode(int aHScrollBarMode) { + fHorScrollbarMode = aHScrollBarMode; + } + + /** + * Access method for the vScrollBarMode property. + * + * @return the current value of the vScrollBarMode property + */ + public int getVScrollBarMode() { + return fVertScrollbarMode; + } + + /** + * Sets the value of the vScrollBarMode property. + * + * @param aVScrollBarMode the new value of the vScrollBarMode property + */ + public void setVScrollBarMode(int aVScrollBarMode) { + fVertScrollbarMode = aVScrollBarMode; + } + + /** + * Return horizontal scroll bar increment, default:1 + * + * @return The increment + */ + public int getHScrollBarIncrement() { + return fHorScrollbarIncrement; + } + + /** + * Return vertical scroll bar increment, default:1 + * + * @return The increment + */ + public int getVScrollBarIncrement() { + return fVertScrollbarIncrement; + } + + /** + * Change horizontal scroll bar increment, minimum:1. Page increment is + * always set to visible width. + * + * @param inc + * Increment value to set + */ + public void setHScrollBarIncrement(int inc) { + fHorScrollbarIncrement = Math.max(1, inc); + } + + /** + * Change vertical scroll bar increment, minimum:1. Page increment is always + * set to visible height. + * + * @param inc + * Increment value to set + */ + public void setVScrollBarIncrement(int inc) { + fVertScrollbarIncrement = Math.max(1, inc); + } + + /** + * Enable or disable overview feature. Enabling overview, dispose and replace existing corner control by a button. + * Clicking in it open overview, move mouse cursor holding button to move scroll view and release button to hide + * overview. Tip: hold control and/or shift key while moving mouse when overview is open made fine scroll. + * + * @param value true to engage overview feature + */ + public void setOverviewEnabled(boolean value) { + if (isOverviewEnabled() == value) { + return; + } + + Control cc = null; + if (value) { + Button b = new Button(this, SWT.NONE); + b.setText("+"); //$NON-NLS-1$ + Overview ovr = new Overview(); + ovr.useControl(b); + b.setData(ovr); + cc = b; + b.setToolTipText(Messages.SequenceDiagram_OpenOverviewTooltip); + } + setCornerControl(cc); + } + + /** + * Change overview size (at ratio 1:1), default is 100 + * + * @param size + * The new size + */ + public void setOverviewSize(int size) { + fOverviewSize = Math.abs(size); + } + + /** + * Returns whether the overview is enabled or not. + * + * @return true is overview feature is enabled + */ + public boolean isOverviewEnabled() { + if (fCornerControl instanceof Button) { + Object data = ((Button) fCornerControl).getData(); + // overview alreay + if (data instanceof Overview) { + return true; + } + } + return false; + } + + /** + * Returns the overview size at ratio 1:1. + * + * @return current overview size at ratio 1:1 + */ + public int getOverviewSize() { + return fOverviewSize; + } + + /** + * Returns control used to display view (might not be this object). Use this control to add/remove listener on the + * draw area. + * + * @return control used to display view (might not be this object). + */ + public Control getViewControl() { + return fViewControl; + } + + /** + * Called when the mouse enter the ScrollView area + * + * @param e + * Mouse event + */ + protected void contentsMouseExit(MouseEvent e) { + } + + /** + * Called when the mouse enter the ScrollView area after and system defined + * time + * + * @param e + * Mouse event + */ + protected void contentsMouseHover(MouseEvent e) { + } + + /** + * Called when the mouse enter the ScrollView area + * + * @param e + * Mouse event + */ + protected void contentsMouseEnter(MouseEvent e) { + } + + /** + * Called when user double on contents area. + * + * @param e + * Mouse event + */ + protected void contentsMouseDoubleClickEvent(MouseEvent e) { + } + + /** + * Called when mouse is on contents area and button is pressed. + * + * @param e + * Mouse event + */ + protected void contentsMouseDownEvent(MouseEvent e) { + fMouseDownX = e.x; + fMousDownY = e.y; + } + + /** + * TimerTask for auto scroll feature. + */ + protected static class AutoScroll extends TimerTask { + + /** X delta */ + private int deltaX; + + /** Y delta */ + private int deltaY; + + /** ScrollView object */ + private ScrollView scrollView; + + /** + * Constructor. + * + * @param sv + * ScrollView object to use + * @param dx + * X delta + * @param dy + * Y delta + */ + public AutoScroll(ScrollView sv, int dx, int dy) { + scrollView = sv; + deltaX = dx; + deltaY = dy; + } + + @Override + public void run() { + final Display display = Display.getDefault(); + if ((display == null) || display.isDisposed()) { + return; + } + display.asyncExec(new Runnable() { + @Override + public void run() { + if (!scrollView.isDisposed()) { + scrollView.scrollBy(deltaX, deltaY); + } + } + }); + } + } + + /** + * Called when mouse is on contents area and mode. + * + * @param event + * Mouse event + */ + protected void contentsMouseMoveEvent(MouseEvent event) { + if ((event.stateMask & SWT.BUTTON_MASK) != 0) { + if (!fAutoScrollEnabled) { + scrollBy(-(event.x - fMouseDownX), -(event.y - fMousDownY)); + return; + } + + int sx = 0, sy = 0; + + int vRight = getContentsX() + getVisibleWidth(); + int vBottom = getContentsY() + getVisibleHeight(); + + // auto scroll... ? + if (event.x < getContentsX()) { + sx = (getContentsX() - event.x); + fMouseDownX = getContentsX(); + } else if (event.x > vRight) { + sx = -event.x + vRight; + fMouseDownX = vRight; + } + if (event.y < getContentsY()) { + sy = (getContentsY() - event.y); + fMousDownY = getContentsY(); + } else if (event.y > vBottom) { + sy = -event.y + vBottom; + fMousDownY = vBottom; + } + + if (sx != 0 || sy != 0) { + // start auto scroll... + if (fAutoScroll == null) { + if (fAutoScrollTimer == null) { + fAutoScrollTimer = new Timer(true); + } + fAutoScroll = new AutoScroll(this, sx, sy); + fAutoScrollTimer.schedule(fAutoScroll, 0, fAutoScrollPeriod); + } else { + fAutoScroll.deltaX = sx; + fAutoScroll.deltaY = sy; + } + } else { + if (fAutoScroll != null) { + fAutoScroll.cancel(); + fAutoScroll = null; + } + + scrollBy(-(event.x - fMouseDownX), -(event.y - fMousDownY)); + } + } + } + + /** + * Called when mouse is on contents area and button is released + * + * @param event + * Mouse event + */ + protected void contentsMouseUpEvent(MouseEvent event) { + // reset auto scroll if it's engaged + if (fAutoScroll != null) { + fAutoScroll.cancel(); + fAutoScroll = null; + } + } + + /** + * Responsible to draw contents area. At least rectangle clipX must be + * redrawn. This rectangle is given in contents coordinates. By default, no + * paint is produced. + * + * @param gc + * Graphics context + * @param clipx + * X clip + * @param clipy + * Y clip + * @param clipw + * W clip + * @param cliph + * H clip + */ + protected void drawContents(GC gc, int clipx, int clipy, int clipw, int cliph) { + } + + /** + * Change the size of the contents area. + * + * @param width new width of the area. + * @param height new height of the area. + */ + public void resizeContents(int width, int height) { + int localWidth = width; + int localHeight = height; + + if (localWidth < 0) { + localWidth = 0; + } + if (localHeight < 0) { + localHeight = 0; + } + + int oldW = fContentsWidth; + int oldH = fContentsHeight; + + if (localWidth == oldW && localHeight == oldH) { + return; + } + + fContentsWidth = localWidth; + fContentsHeight = localHeight; + + if (oldW > localWidth) { + int s = localWidth; + localWidth = oldW; + oldW = s; + } + + int visWidth = getVisibleWidth(); + int visHeight = getVisibleHeight(); + if (oldW < visWidth) { + if (localWidth > visWidth) { + localWidth = visWidth; + } + fViewControl.redraw(getContentsX() + oldW, 0, localWidth - oldW, visHeight, true); + } + + if (oldH > localHeight) { + int s = localHeight; + localHeight = oldH; + oldH = s; + } + + if (oldH < visHeight) { + if (localHeight > visHeight) { + localHeight = visHeight; + } + fViewControl.redraw(0, getContentsY() + oldH, visWidth, localHeight - oldH, true); + } + if (updateScrollBarVisiblity()) { + layout(); + } else { + updateScrollBarsValues(); + } + } + + // redefined for internal use .. + @Override + public void redraw() { + super.redraw(); + // ..need to redraw this already: + fViewControl.redraw(); + } + + /** + * @param delataX The delta in X + * @param deltaY the delta in Y + */ + public void scrollBy(int delataX, int deltaY) { + setContentsPos(getContentsX() + delataX, getContentsY() + deltaY); + } + + /** + * Scroll to ensure point(in contents coordinates) is visible. + * + * @param px Point's x position + * @param py Point's y position + */ + public void ensureVisible(int px, int py) { + int cx = getContentsX(), cy = getContentsY(); + int right = getContentsX() + getVisibleWidth(); + int bottom = getContentsY() + getVisibleHeight(); + if (px < getContentsX()) { + cx = px; + } else if (px > right) { + cx = px - getVisibleWidth(); + } + if (py < getContentsY()) { + cy = py; + } else if (py > bottom) { + cy = py - getVisibleHeight(); + } + setContentsPos(cx, cy); + } + + /** + * Make rectangle (x,y,w,h, in contents coordinates) visible. if rectangle cannot be completely visible, use + * align flags. + * + * @param xValue x contents coordinates of rectangle. + * @param yValue y contents coordinates of rectangle. + * @param width width of rectangle. + * @param height height of rectangle. + * @param align bit or'ed SWT flag like SWT.LEFT,RIGHT,CENTER,TOP,BOTTOM,VERTICAL used only for bigger rectangle + * than visible area. By default CENTER/VERTICAL + */ + public void ensureVisible(int xValue, int yValue, int width, int height, int align) { + ensureVisible(xValue, yValue, width, height, align, false); + } + + /** + * Make rectangle (xValue,yValue,width,height, in contents coordinates) visible. if rectangle cannot be completely visible, use + * align flags. + * + * @param xValue x contents coordinates of rectangle. + * @param yValue y contents coordinates of rectangle. + * @param width width of rectangle. + * @param height height of rectangle. + * @param align bit or'ed SWT flag like SWT.LEFT,RIGHT,CENTER,TOP,BOTTOM,VERTICAL used only for bigger rectangle + * than visible area. By default CENTER/VERTICAL + * @param forceAlign force alignment for rectangle smaller than the visible area + */ + protected void ensureVisible(int xValue, int yValue, int width, int height, int align, boolean forceAlign) { + + int localX = xValue; + int localY = yValue; + int localWidth = width; + int localHeight = height; + + if (localWidth < 0) { + localX = localX + localWidth; + localWidth = -localWidth; + } + if (localHeight < 0) { + localY = localY + localHeight; + localHeight = -localHeight; + } + int hbar = getHorizontalBarHeight(); + int vbar = getVerticalBarWidth(); + int cx = getContentsX(); + int cy = getContentsY(); + int right = getContentsX() + getVisibleWidth() - vbar; + int bottom = getContentsY() + getVisibleHeight() - hbar; + boolean alignH = false, alignV = false; + + if (localX < getContentsX()) { + cx = localX; + } else if (localX + localWidth > right) { + cx = localX - localWidth; + } + if (localY < getContentsY()) { + cy = localY; + } else if (localY + localHeight > bottom) { + cy = localY - localHeight; + } + + if (localWidth > getVisibleWidth()) { + alignH = true; + } + if (localHeight > getVisibleHeight()) { + alignV = true; + } + // compute alignment on visible area horizontally + if (alignH || (forceAlign && localX + localWidth > right)) { + // use align flags + if ((align & SWT.LEFT) != 0) { + cx = localX; + } else if ((align & SWT.RIGHT) != 0) { + cx = right - localWidth; + } else { // center + cx = localX + (localWidth - getVisibleWidth()) / 2; + } + } + // compute alignment on visible area vertically + if (alignV || (forceAlign && localY + localHeight > bottom)) { + // use align flags + if ((align & SWT.TOP) != 0) { + cy = localY; + } else if ((align & SWT.BOTTOM) != 0) { + cy = bottom - localHeight; + } else { // center + cy = localY + (localHeight - getVisibleHeight()) / 2; + } + } + setContentsPos(cx, cy); + } + + /** + * Returns true if point is visible (expressed in contents coordinates). + * + * @param px Point's x position + * @param py Point's y position + * @return true if point is visible (expressed in contents coordinates) + */ + public boolean isVisible(int px, int py) { + if (px < getContentsX()) { + return false; + } + if (py < getContentsY()) { + return false; + } + if (px > (getContentsX() + getVisibleWidth())) { + return false; + } + if (py > (getContentsY() + getVisibleHeight())) { + return false; + } + return true; + } + + /** + * Returns true if rectangle if partially visible. + * + * @param xValue x contents coordinates of rectangle. + * @param yValue y contents coordinates of rectangle. + * @param width width of rectangle. + * @param height height of rectangle. + * @return true if rectangle if partially visible. + */ + public boolean isVisible(int xValue, int yValue, int width, int height) { + if (xValue + width < getContentsX()) { + return false; + } + if (yValue + height < getContentsY()) { + return false; + } + int vr = getContentsX() + getVisibleWidth(); + int vb = getContentsY() + getVisibleHeight(); + if (xValue > vr) { + return false; + } + if (yValue > vb) { + return false; + } + return true; + } + + /** + * Returns visible part of rectangle, or null if rectangle is not visible. + * Rectangle is expressed in contents coordinates. + * + * @param xValue + * x contents coordinates of rectangle. + * @param yValue + * y contents coordinates of rectangle. + * @param width + * width of rectangle. + * @param height + * height of rectangle. + * @return visible part of rectangle, or null if rectangle is not visible. + */ + public Rectangle getVisiblePart(int xValue, int yValue, int width, int height) { + if (xValue + width < getContentsX()) { + return null; + } + if (yValue + height < getContentsY()) { + return null; + } + int vr = getContentsX() + getVisibleWidth(); + int vb = getContentsY() + getVisibleHeight(); + if (xValue > vr) { + return null; + } + if (yValue > vb) { + return null; + } + int rr = xValue + width, rb = yValue + height; + int nl = Math.max(xValue, getContentsX()), nt = Math.max(yValue, getContentsY()), nr = Math.min(rr, vr), nb = Math.min(rb, vb); + return new Rectangle(nl, nt, nr - nl, nb - nt); + } + + /** + * Returns the visible part for given rectangle. + * + * @param rect A rectangle + * + * @return gets visible part of rectangle (or <code>null</code>) + */ + public final Rectangle getVisiblePart(Rectangle rect) { + if (rect == null) { + return null; + } + return getVisiblePart(rect.x, rect.y, rect.width, rect.height); + } + + /** + * Change top left position of visible area. Check if the given point is inside contents area. + * + * @param xValue x contents coordinates of visible area. + * @param yValue y contents coordinates of visible area. + * @return true if view really moves + */ + public boolean setContentsPos(int xValue, int yValue) { + int nx = xValue, ny = yValue; + if (getVisibleWidth() >= getContentsWidth()) { + nx = 0; + } else { + if (xValue < 0) { + nx = 0; + } else if (xValue + getVisibleWidth() > getContentsWidth()) { + nx = getContentsWidth() - getVisibleWidth(); + } + } + if (getVisibleHeight() >= getContentsHeight()) { + ny = 0; + } else { + if (yValue <= 0) { + ny = 0; + } else if (yValue + getVisibleHeight() > getContentsHeight()) { + ny = getContentsHeight() - getVisibleHeight(); + } + } + // no move + if (nx == fContentsX && ny == fContentsY) { + return false; + } + fContentsX = nx; + fContentsY = ny; + updateScrollBarsValues(); + // ? find smallest area to redraw only them ? + fViewControl.redraw(); + return true; + } + + @Override + public ScrollBar getVerticalBar() { + return fVertScrollBar.getVerticalBar(); + } + + @Override + public ScrollBar getHorizontalBar() { + return fHorScrollBar.getHorizontalBar(); + } + + /** + * Compute visibility of vertical/horizontal bar using given width/height and current visibility (i.e. is bar size are already in + * for_xxx) + * @param forWidth width of foreground + * @param forHeight height of foreground + * @param currHorVis The current visibility state of horizontal scroll bar + * @param currVertvis The current visibility state of vertical scroll bar + * @return <code>true</code> if visibility changed else <code>false</code> + */ + public int computeBarVisibility(int forWidth, int forHeight, boolean currHorVis, boolean currVertvis) { + + int localForWidth = forWidth; + int vis = 0x00; + switch (fVertScrollbarMode) { + case ALWAYS_OFF: + break; + case ALWAYS_ON: + vis |= VBAR; + break; + case AUTO: + if (getContentsHeight() > forHeight) { + vis = VBAR; + // v bar size is already in for_width. + if (!currVertvis) {// (curr_vis&0x01)==0) + localForWidth -= getVerticalBarWidth(); + } + } + break; + default: + break; + } + + switch (fHorScrollbarMode) { + case ALWAYS_OFF: + break; + case ALWAYS_ON: + vis |= HBAR; + break; + case AUTO: + if (getContentsWidth() > localForWidth) { + vis |= HBAR; + // h bar is not in for_height + if ((!currHorVis) && (getContentsHeight() > (forHeight - getHorizontalBarHeight()))) {// (curr_vis&0x02)==0 ) + vis |= VBAR; + } + } + break; + default: + break; + } + return vis; + } + + /** + * Setup scroll bars visibility. + * + * @return True if one of visibility changed. + */ + protected boolean updateScrollBarVisiblity() { + boolean change = false; + + boolean currVertVis = fVertScrollBar.getVisible(); + boolean currHorVis = fHorScrollBar.getVisible(); + int barNewVis = computeBarVisibility(getVisibleWidth(), getVisibleHeight(), currHorVis, currVertVis); + boolean newVertVis = (barNewVis & VBAR) != 0; + boolean newHorVis = (barNewVis & HBAR) != 0; + if (currVertVis ^ newVertVis) { // vertsb_.getVisible() ) + fVertScrollBar.setVisible(newVertVis); + change = true; + } + if (currHorVis ^ newHorVis) { + fHorScrollBar.setVisible(newHorVis); + change = true; + } + + // update corner control visibility: + if (fCornerControl != null && change) { + boolean vis = newVertVis || newHorVis; + if (vis ^ fCornerControl.getVisible()) { + fCornerControl.setVisible(vis); + change = true; // but must be already the case + } + } + return change; + } + + /** + * Setup scroll bar using contents, visible and scroll bar mode properties. + */ + protected void updateScrollBarsValues() { + /* update vertical scrollbar */ + ScrollBar b = getVerticalBar(); + if (b != null) { + b.setMinimum(0); + b.setMaximum(getContentsHeight()); + b.setThumb(getVisibleHeight()); + b.setPageIncrement(getVisibleHeight()); + b.setIncrement(fVertScrollbarIncrement); + b.setSelection(getContentsY()); + } + + // update "hidden" vertical bar too + b = fViewControl.getVerticalBar(); + if (b != null) { + b.setMinimum(0); + b.setMaximum(getContentsHeight()); + b.setThumb(getVisibleHeight()); + b.setPageIncrement(getVisibleHeight()); + b.setIncrement(fVertScrollbarIncrement); + b.setSelection(getContentsY()); + } + + /* update horizontal scrollbar */ + b = getHorizontalBar(); + if (b != null) { + b.setMinimum(0); + b.setMaximum(getContentsWidth()); + b.setThumb(getVisibleWidth()); + b.setSelection(getContentsX()); + b.setPageIncrement(getVisibleWidth()); + b.setIncrement(fHorScrollbarIncrement); + } + // update "hidden" horizontal bar too + b = fViewControl.getHorizontalBar(); + if (b != null) { + b.setMinimum(0); + b.setMaximum(getContentsWidth()); + b.setThumb(getVisibleWidth()); + b.setSelection(getContentsX()); + b.setPageIncrement(getVisibleWidth()); + b.setIncrement(fHorScrollbarIncrement); + } + } + + /** + * Change the control used in the bottom right corner (between two scrollbar), if control is null reset previous + * corner control. This control is visible only if at least one scrollbar is visible. Given control will be disposed + * by ScrollView, at dispose() time, at next setCornetControl() call or when calling setOverviewEnabled(). Pay + * attention calling this reset overview feature until setOverviewEnabled(true) if called. + * @param control The control for the overview + */ + public void setCornerControl(Control control) { + if (fCornerControl != null) { + fCornerControl.dispose(); + } + fCornerControl = control; + if (fCornerControl != null) { + ScrollBar vb = getVerticalBar(); + ScrollBar hb = getHorizontalBar(); + boolean vis = vb.getVisible() || hb.getVisible(); + fCornerControl.setVisible(vis); + } + } + + /** + * Transform (x,y) point in widget coordinates to contents coordinates. + * + * @param x The x widget coordinate. + * @param y The y widget coordinate. + * @return org.eclipse.swt.graphics.Point with content coordinates. + */ + public final Point viewToContents(int x, int y) { + return new Point(viewToContentsX(x), viewToContentsY(y)); + } + + /** + * Transform x in widget coordinates to contents coordinates + * + * @param x The y widget coordinate. + * @return the x content coordinate. + */ + public int viewToContentsX(int x) { + return fContentsX + x; + } + + /** + * Transform y in widget coordinates to contents coordinates + * + * @param y The y widget coordinate. + * @return the y content coordinate. + */ + public int viewToContentsY(int y) { + return fContentsY + y; + } + + /** + * Transform (x,y) point from contents coordinates, to widget coordinates. + * + * @param x The x content coordinate. + * @param y The y content coordinate. + * @return coordinates widget area as. + */ + public final Point contentsToView(int x, int y) { + return new Point(contentsToViewX(x), contentsToViewY(y)); + } + + /** + * Transform X axis coordinates from contents to widgets. + * + * @param x contents coordinate to transform. + * @return x coordinate in widget area + */ + public int contentsToViewX(int x) { + return x - fContentsX; + } + + /** + * Transform Y axis coordinates from contents to widgets. + * + * @param y contents coordinate to transform + * @return y coordinate in widget area + */ + public int contentsToViewY(int y) { + return y - fContentsY; + } + + /** + * Return the visible height of scroll view, might be > contentsHeight + * + * @return the visible height of scroll view, might be > contentsHeight() + */ + public int getVisibleHeight() { + return fViewControl.getClientArea().height; + } + + /** + * Return int the visible width of scroll view, might be > contentsWidth(). + * + * @return int the visible width of scroll view, might be > contentsWidth() + */ + public int getVisibleWidth() { + return fViewControl.getClientArea().width; + } + + /** + * Add support for arrow key, scroll the ... scroll view. But you can + * redefine this method for your convenience. + * + * @param event + * Keyboard event + */ + protected void keyPressedEvent(KeyEvent event) { + switch (event.keyCode) { + case SWT.ARROW_UP: + scrollBy(0, -getVisibleHeight()); + break; + case SWT.ARROW_DOWN: + scrollBy(0, +getVisibleHeight()); + break; + case SWT.ARROW_LEFT: + scrollBy(-getVisibleWidth(), 0); + break; + case SWT.ARROW_RIGHT: + scrollBy(+getVisibleWidth(), 0); + break; + default: + break; + } + } + + /** + * Redefine this method at your convenience + * + * @param event The key event. + */ + protected void keyReleasedEvent(KeyEvent event) { + } + + /** + * Returns vertical bar width, even if bar isn't visible. + * + * @return vertical bar width, even if bar isn't visible + */ + public int getVerticalBarWidth() { + // include vertical bar width and trimming of scrollable used + int bw = fVertScrollBar.computeTrim(0, 0, 0, 0).width; + return bw + 1; + } + + /** + * Returns horizontal bar height even if bar isn't visible. + * + * @return horizontal bar height even if bar isn't visible + */ + public int getHorizontalBarHeight() { + // include horiz. bar height and trimming of scrollable used + int bh = fHorScrollBar.computeTrim(0, 0, 0, 0).height; + // +1 because win32 H.bar need 1 pixel canvas size to appear ! (strange no ?) + return bh + 1; + } + + @Override + public Rectangle computeTrim(int x, int y, int w, int h) { + Rectangle r = new Rectangle(x, y, w, h); + int barVis = computeBarVisibility(w, h, false, false); + if ((barVis & VBAR) != 0) { + r.width += getVerticalBarWidth(); + } + if ((barVis & HBAR) != 0) { + r.height += getHorizontalBarHeight(); + } + return r; + } + + /** + * Internal layout for ScrollView, handle scrollbars, drawzone and corner control + */ + protected class SVLayout extends Layout { + + private static final int DEFAULT_X = 250; + private static final int DEFAULT_Y = 250; + private static final int MAX_SEEK = 10; + private static final int MIN_SEEK = 0; + + /** + * The seek value + */ + private int seek = 0; + /** + * The do-it-not flag + */ + private boolean dontLayout = false; + + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point p = new Point(DEFAULT_X, DEFAULT_Y); + if (fContentsWidth < p.x) { + p.x = fContentsWidth; + } + if (fContentsHeight < p.y) { + p.y = fContentsHeight; + } + return p; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + if (dontLayout) { + return; + } + seek++; + if (seek > MAX_SEEK) { + dontLayout = true; + } + + Point cs = composite.getSize(); + int barVis = computeBarVisibility(cs.x, cs.y, false, false); + boolean vbVis = (barVis & VBAR) != 0; + boolean hbVis = (barVis & HBAR) != 0; + fVertScrollBar.setVisible(vbVis); + fHorScrollBar.setVisible(hbVis); + int vbw = getVerticalBarWidth(); + int hbh = getHorizontalBarHeight(); + int wb = vbVis ? vbw : 0; + int hb = hbVis ? hbh : 0; + int cww = 0, cwh = 0; + + if (fCornerControl != null && (vbVis || hbVis)) { // corner_control_.getVisible()) + fCornerControl.setVisible(true); + cww = vbw; + cwh = hbh; + if (wb == 0) { + wb = vbw; + } + if (hb == 0) { + hb = hbh; + } + } else if (vbVis && hbVis) { + if (fCornerControl != null) { + fCornerControl.setVisible(false); + } + cww = vbw; + cwh = hbh; + } + if (vbVis || hbVis) { + updateScrollBarsValues(); + } + + int vw = cs.x - (vbVis ? vbw : 0); + int vh = cs.y - (hbVis ? hbh : 0); + int vbx = cs.x - wb; + int hby = cs.y - hb; + + fViewControl.setBounds(0, 0, vw, vh); + + if (vbVis) { + fVertScrollBar.setBounds(vbx, 0, wb, cs.y - cwh); + } + if (hbVis) { + fHorScrollBar.setBounds(0, hby, cs.x - cww, hb); + } + if (fCornerControl != null && fCornerControl.getVisible()) { + fCornerControl.setBounds(vbx, hby, vbw, hbh); + } + updateScrollBarsValues(); + + seek--; + if (seek == MIN_SEEK) { + dontLayout = false; + } + } + + boolean isDontLayout() { + return dontLayout; + } + + void setSontLayout(boolean dontLayout) { + this.dontLayout = dontLayout; + } + } + + // static must take place here... cursor is created once. + private volatile static Cursor fOverviewCursor; + + /** Support for click-and-see overview shell on this ScrollView */ + protected class Overview { + + private static final int REFRESH_FREQ = 4; + + /** + * factor for X from real and overview sizes, for mouse move speed. + */ + private float fOverviewFactorX; + + /** + * factor for Y from real and overview sizes, for mouse move speed. + */ + private float fOverviewFactorY; + /** + * shell use to show overview + */ + private Shell fOverview; + /** + * save mouse X cursor location for disappear(); + */ + private int fSaveCursorX; + /** + * save mouse Y cursor location for disappear(); + */ + private int fSaveCursorY; + + /** + * Apply overview support on a control. Replace existing corner_widget + * + * @param control + * The control to use + */ + public void useControl(Control control) { + final Point pos = control.getLocation(); + control.addMouseListener(new MouseListener() { + @Override + public void mouseDoubleClick(MouseEvent e) { + } + + @Override + public void mouseDown(MouseEvent e) { + overviewAppear(e.x, e.y); + } + + @Override + public void mouseUp(MouseEvent e) { + overviewDisappear(); + } + }); + + control.addFocusListener(new FocusListener() { + + @Override + public void focusGained(FocusEvent e) { + } + + @Override + public void focusLost(FocusEvent e) { + if (overviewing()) { + overviewDisappear(false); + } + } + + }); + control.addKeyListener(new KeyListener() { + + @Override + public void keyPressed(KeyEvent event) { + if (event.keyCode == 32 && !overviewing()) { + overviewAppear(pos.x, pos.y); + } else if (event.keyCode == 32) { + overviewDisappear(); + } + if (event.keyCode == SWT.ARROW_DOWN) { + overviewMove(0, 1, event); + } + + if (event.keyCode == SWT.ARROW_UP) { + overviewMove(0, -1, event); + } + + if (event.keyCode == SWT.ARROW_RIGHT) { + overviewMove(1, 0, event); + } + + if (event.keyCode == SWT.ARROW_LEFT) { + overviewMove(-1, 0, event); + } + } + + @Override + public void keyReleased(KeyEvent e) { + } + }); + control.addMouseMoveListener(new MouseMoveListener() { + private int refReshCount = 0; + @Override + public void mouseMove(MouseEvent event) { + if (overviewing()) { + // Slow down the refresh + if (refReshCount % REFRESH_FREQ == 0) { + overviewMove(event); + } + refReshCount++; + } + } + }); + } + + /** + * Dispose controls of overview + */ + public void dispose() { + if (fOverview != null) { + fOverview.dispose(); + } + } + + /** + * @return true if overview is currently on screen + */ + protected boolean overviewing() { + return (fOverview != null && fOverview.isVisible()); + } + + /** + * Process overview appear + * + * @param mx + * X coordinate + * @param my + * Y coordinate + */ + protected void overviewAppear(int mx, int my) { + if (fOverview == null) { + fOverview = new Shell(getShell(), SWT.ON_TOP | SWT.NO_BACKGROUND); + fOverview.addPaintListener(new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + drawOverview(e.gc, fOverview.getClientArea()); + } + }); + } + // always the same.. + fOverview.setForeground(fViewControl.getForeground()); + + // get location of shell (in screeen coordinates) + Point p = toGlobalCoordinates(fCornerControl, 0, 0); + int x = p.x; + int y = p.y; + int w, h; + h = fOverviewSize; + w = h; + Rectangle scr = getDisplay().getBounds(); + Point ccs = fCornerControl.getSize(); + try { + if (fContentsWidth > fContentsHeight) { + float ratio = fContentsHeight / (float) fContentsWidth; + h = (int) (w * ratio); + if (h < ccs.y) { + h = ccs.y; + } else if (h >= scr.height / 2) { + h = scr.height / 2; + } + } else { + float ratio = fContentsWidth / (float) fContentsHeight; + w = (int) (h * ratio); + if (w < ccs.x) { + w = ccs.x; + } else if (w >= scr.width / 2) { + w = scr.width / 2; + } + } + fOverviewFactorX = fContentsWidth / (float) w; + fOverviewFactorY = fContentsHeight / (float) h; + } + // no contents size set ? + catch (java.lang.ArithmeticException e) { + } + + // try pop-up on button, extending to bottom right, + if (x <= 0) { + x = 1; + } + if (y <= 0) { + y = 1; + } + x = x - w + ccs.x; + y = y - h + ccs.y; + fOverview.setBounds(x, y, w, h); + fOverview.setVisible(true); + fOverview.redraw(); + // mouse cursor disappear, so set invisible mouse cursor ... + if (fOverviewCursor == null) { + RGB rgb[] = { new RGB(0, 0, 0), new RGB(255, 0, 0) }; + PaletteData palette = new PaletteData(rgb); + int s = 1; + byte src[] = new byte[s * s]; + byte msk[] = new byte[s * s]; + for (int i = 0; i < s * s; ++i) { + src[i] = (byte) 0xFF; + } + ImageData i_src = new ImageData(s, s, 1, palette, 1, src); + ImageData i_msk = new ImageData(s, s, 1, palette, 1, msk); + fOverviewCursor = new Cursor(null, i_src, i_msk, 0, 0); + } + fCornerControl.setCursor(fOverviewCursor); + // convert to global coordinates + p = toGlobalCoordinates(fCornerControl, mx, my); + fSaveCursorX = p.x; + fSaveCursorY = p.y; + + Rectangle r = fOverview.getClientArea(); + int cx = (int) (r.width * fContentsX / (float) fContentsWidth); + int cy = (int) (r.height * fContentsY / (float) fContentsHeight); + + // cx,cy to display's global coordinates + p = toGlobalCoordinates(fOverview.getParent(), cx, cy); + } + + /** + * Process disappear of overview + */ + protected void overviewDisappear() { + overviewDisappear(true); + } + + /** + * Process disappear of overview + * @param restoreCursorLoc A flag to restore cursor location + */ + protected void overviewDisappear(boolean restoreCursorLoc) { + if (fOverview == null) { + return; + } + fOverview.setVisible(false); + fCornerControl.setCursor(null); + if (restoreCursorLoc) { + getDisplay().setCursorLocation(fSaveCursorX, fSaveCursorY); + } + fOverview.dispose(); + fOverview = null; + } + + /** + * Process mouse move in overview + * @param event The mouse event + */ + protected void overviewMove(MouseEvent event) { + Point p = toGlobalCoordinates(fCornerControl, event.x, event.y); + int dx = p.x - fSaveCursorX; + int dy = p.y - fSaveCursorY; + overviewMove(dx, dy, event); + } + + /** + * Process mouse move event when overviewing + * + * @param dx The x coordinates delta + * @param dy The y coordinates delta + * @param event The typed event + */ + protected void overviewMove(int dx, int dy, TypedEvent event) { + boolean ctrl = false; + boolean shift = false; + + if (event instanceof MouseEvent) { + MouseEvent e = (MouseEvent) event; + getDisplay().setCursorLocation(fSaveCursorX, fSaveCursorY); + ctrl = (e.stateMask & SWT.CONTROL) != 0; + shift = (e.stateMask & SWT.SHIFT) != 0; + } else if (event instanceof KeyEvent) { + KeyEvent e = (KeyEvent) event; + ctrl = (e.stateMask & SWT.CONTROL) != 0; + shift = (e.stateMask & SWT.SHIFT) != 0; + } + + int cx = fContentsX; + int cy = fContentsY; + float fx = fOverviewFactorX; + float fy = fOverviewFactorY; + + if (ctrl && shift) { + if ((fx * 0.25f > 1) && (fy * 0.25 > 1)) { + fx = fy = 1.0f; + } else { + fx *= 0.1f; + fy *= 0.1f; + } + } else if (ctrl) { + fx *= 0.5f; + fy *= 0.5f; + } else if (shift) { + fx *= 0.5f; + fy *= 0.5f; + } + scrollBy((int) (fx * dx), (int) (fy * dy)); + if (cx != fContentsX || cy != fContentsY) { + fOverview.redraw(); + fOverview.update(); // draw now ! + } + } + + /** + * Convert overview coordinates to global coordinates. + * + * @param loc + * the control reference + * @param x + * The x coordinate to convert + * @param y + * The y coordinate to convert + * @return The new converted Point + */ + protected Point toGlobalCoordinates(Control loc, int x, int y) { + Point p = new Point(x, y); + for (Control c = loc; c != null; c = c.getParent()) { + // control might have client area with 'decorations' + int trimX = 0, trimY = 0; + // other kind of widget with trimming ?? + if (c instanceof Scrollable) { + Scrollable s = (Scrollable) c; + Rectangle rr = s.getClientArea(); + Rectangle tr = s.computeTrim(rr.x, rr.y, rr.width, rr.height); + trimX = rr.x - tr.x; + trimY = rr.y - tr.y; + } + p.x += c.getLocation().x + trimX; + p.y += c.getLocation().y + trimY; + } + return p; + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/TimeCompressionBar.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/TimeCompressionBar.java new file mode 100755 index 0000000000..b7e72adecb --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/TimeCompressionBar.java @@ -0,0 +1,1028 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.ACC; +import org.eclipse.swt.accessibility.Accessible; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleControlAdapter; +import org.eclipse.swt.accessibility.AccessibleControlEvent; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +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.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessageReturn; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.BaseMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.ExecutionOccurrence; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Frame; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.ITimeRange; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Metrics; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SDTimeEvent; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.impl.ColorImpl; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.TimeEventComparator; + +/** + * <p> + * The time compression bar implementation. + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class TimeCompressionBar extends ScrollView implements DisposeListener { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + private static final int BASE_RED_VALUE = 255; + private static final int BASE_GREEN_BLUE_VALUE = 225; + private static final int COLOR_STEP = 25; + private static final int NUMBER_STEPS = 10; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The listener list + */ + private List<ITimeCompressionListener> fListenerList = null; + /** + * The current frame displayed. + */ + private Frame fFrame = null; + /** + * List of time events. + */ + private List<SDTimeEvent> fNodeList = null; + /** + * The minimum time delta. + */ + private ITmfTimestamp fMinTime = new TmfTimestamp(); + /** + * The maximum time delta. + */ + private ITmfTimestamp fMaxTime = new TmfTimestamp(); + /** + * The current zoom value. + */ + private float fZoomValue = 1; + /** + * The tooltip to display. + */ + private DrawableToolTip fTooltip = null; + /** + * Array of colors for displaying wight of time deltas. + */ + private ColorImpl[] fColors; + /** + * The accessible object reference. + */ + private Accessible fAccessible = null; + /** + * The focused widget reference. + */ + private int fFocusedWidget = -1; + /** + * The current lifeline. + */ + private Lifeline fLifeline = null; + /** + * The current start event value. + */ + private int fLifelineStart = 0; + /** + * The current number of events. + */ + private int fLifelineNumEvents = 0; + /** + * The Current color of range to display. + */ + private IColor fLifelineColor = null; + /** + * The next graph node y coordinate. + */ + private int fNextNodeY = 0; + /** + * The previous graph node y coordinate. + */ + private int fPrevNodeY = 0; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Standard constructor + * + * @param parent The parent composite + * @param s The style bits + */ + public TimeCompressionBar(Composite parent, int s) { + super(parent, s | SWT.NO_BACKGROUND, false); + setVScrollBarMode(ScrollView.ALWAYS_OFF); + setHScrollBarMode(ScrollView.ALWAYS_OFF); + fListenerList = new ArrayList<>(); + fColors = new ColorImpl[NUMBER_STEPS]; + int greenBlue = BASE_GREEN_BLUE_VALUE; + final int step = COLOR_STEP; + for (int i = 0; i < fColors.length; i++) { + fColors[i] = new ColorImpl(Display.getDefault(), BASE_RED_VALUE, greenBlue, greenBlue); + greenBlue -= step; + } + super.addDisposeListener(this); + + fAccessible = getViewControl().getAccessible(); + + fAccessible.addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + // Case toolTip + if (e.childID == 0) { + if (fTooltip != null) { + e.result = fTooltip.getAccessibleText(); + } + } else if (e.childID == 1) { + createFakeTooltip(); + e.result = fTooltip.getAccessibleText(); + } + } + }); + + fAccessible.addAccessibleControlListener(new AccessibleControlAdapter() { + @Override + public void getFocus(AccessibleControlEvent e) { + if (fFocusedWidget == -1) { + e.childID = ACC.CHILDID_SELF; + } + else { + e.childID = fFocusedWidget; + } + } + + @Override + public void getRole(AccessibleControlEvent e) { + switch (e.childID) { + case ACC.CHILDID_SELF: + e.detail = ACC.ROLE_CLIENT_AREA; + break; + case 0: + e.detail = ACC.ROLE_TOOLTIP; + break; + case 1: + e.detail = ACC.ROLE_LABEL; + break; + default: + break; + } + } + + @Override + public void getState(AccessibleControlEvent e) { + e.detail = ACC.STATE_FOCUSABLE; + if (e.childID == ACC.CHILDID_SELF) { + e.detail |= ACC.STATE_FOCUSED; + } else { + e.detail |= ACC.STATE_SELECTABLE; + if (e.childID == fFocusedWidget) { + e.detail |= ACC.STATE_FOCUSED | ACC.STATE_SELECTED | ACC.STATE_CHECKED; + } + } + } + }); + + getViewControl().addTraverseListener(new LocalTraverseListener()); + + addTraverseListener(new LocalTraverseListener()); + + getViewControl().addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + redraw(); + } + + @Override + public void focusLost(FocusEvent e) { + redraw(); + } + }); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Sets the focus widget + * + * @param newFocusShape widget reference to set + */ + void setFocus(int newFocusShape) { + fFocusedWidget = newFocusShape; + if (fFocusedWidget == -1) { + getViewControl().getAccessible().setFocus(ACC.CHILDID_SELF); + } else { + getViewControl().getAccessible().setFocus(fFocusedWidget); + } + } + + /** + * Sets the current frame. + * + * @param theFrame The frame to set + */ + public void setFrame(Frame theFrame) { + fFrame = theFrame; + fMinTime = fFrame.getMinTime(); + fMaxTime = fFrame.getMaxTime(); + } + + @Override + protected void drawContents(GC gc, int clipx, int clipy, int clipw, int cliph) { + if (fFrame == null) { + return; + } + fNodeList = new ArrayList<>(); + int messageArraysStep = 1; + + if ((Metrics.getMessageFontHeigth() + Metrics.MESSAGES_NAME_SPACING * 2) * fZoomValue < Metrics.MESSAGE_SIGNIFICANT_VSPACING + 1) { + messageArraysStep = Math.round(Metrics.MESSAGE_SIGNIFICANT_VSPACING + 1 / ((Metrics.getMessageFontHeigth() + Metrics.MESSAGES_NAME_SPACING * 2) * fZoomValue)); + } + + int firstVisible = fFrame.getFirstVisibleSyncMessage(); + if (firstVisible > 0) { + firstVisible = firstVisible - 1; + } + for (int i = firstVisible; i < fFrame.syncMessageCount(); i = i + messageArraysStep) { + SyncMessage m = fFrame.getSyncMessage(i); + if (m.hasTimeInfo()) { + SDTimeEvent t = new SDTimeEvent(m.getStartTime(), m.getEventOccurrence(), m); + fNodeList.add(t); + if (m.getY() * fZoomValue > getContentsY() + getVisibleHeight()) { + break; + } + } + } + + firstVisible = fFrame.getFirstVisibleSyncMessageReturn(); + if (firstVisible > 0) { + firstVisible = firstVisible - 1; + } + for (int i = firstVisible; i < fFrame.syncMessageReturnCount(); i = i + messageArraysStep) { + SyncMessage m = fFrame.getSyncMessageReturn(i); + if (m.hasTimeInfo()) { + SDTimeEvent t = new SDTimeEvent(m.getStartTime(), m.getEventOccurrence(), m); + fNodeList.add(t); + if (m.getY() * fZoomValue > getContentsY() + getVisibleHeight()) { + break; + } + } + } + + firstVisible = fFrame.getFirstVisibleAsyncMessage(); + if (firstVisible > 0) { + firstVisible = firstVisible - 1; + } + for (int i = firstVisible; i < fFrame.asyncMessageCount(); i = i + messageArraysStep) { + AsyncMessage m = fFrame.getAsyncMessage(i); + if (m.hasTimeInfo()) { + SDTimeEvent t = new SDTimeEvent(m.getStartTime(), m.getStartOccurrence(), m); + fNodeList.add(t); + t = new SDTimeEvent(m.getEndTime(), m.getEndOccurrence(), m); + fNodeList.add(t); + if (m.getY() * fZoomValue > getContentsY() + getVisibleHeight()) { + break; + } + } + } + + firstVisible = fFrame.getFirstVisibleAsyncMessageReturn(); + if (firstVisible > 0) { + firstVisible = firstVisible - 1; + } + for (int i = firstVisible; i < fFrame.asyncMessageReturnCount(); i = i + messageArraysStep) { + AsyncMessageReturn m = fFrame.getAsyncMessageReturn(i); + if (m.hasTimeInfo()) { + SDTimeEvent t = new SDTimeEvent(m.getStartTime(), m.getStartOccurrence(), m); + fNodeList.add(t); + t = new SDTimeEvent(m.getEndTime(), m.getEndOccurrence(), m); + fNodeList.add(t); + if (m.getY() * fZoomValue > getContentsY() + getVisibleHeight()) { + break; + } + } + } + + List<SDTimeEvent> executionOccurrencesWithTime = fFrame.getExecutionOccurrencesWithTime(); + if (executionOccurrencesWithTime != null) { + fNodeList.addAll(executionOccurrencesWithTime); + } + + SDTimeEvent[] temp = fNodeList.toArray(new SDTimeEvent[fNodeList.size()]); + Arrays.sort(temp, new TimeEventComparator()); + fNodeList = Arrays.asList(temp); + + Image dbuffer = new Image(getDisplay(), getClientArea().width, getClientArea().height); + GC gcim = new GC(dbuffer); + + for (int i = 0; i < fNodeList.size() - 1; i++) { + SDTimeEvent m1 = fNodeList.get(i); + SDTimeEvent m2 = fNodeList.get(i + 1); + + if ((SDViewPref.getInstance().excludeExternalTime()) && ((m1.getGraphNode() instanceof BaseMessage) && (m2.getGraphNode() instanceof BaseMessage))) { + BaseMessage mes1 = (BaseMessage) m1.getGraphNode(); + BaseMessage mes2 = (BaseMessage) m2.getGraphNode(); + if ((mes2.getStartLifeline() == null) || (mes1.getEndLifeline() == null)) { + continue; + } + } + + fMinTime = fFrame.getMinTime(); + fMaxTime = fFrame.getMaxTime(); + ITmfTimestamp minMaxdelta = fMaxTime.getDelta(fMinTime); + double gr = (minMaxdelta.getValue()) / (double) NUMBER_STEPS; + + ITmfTimestamp delta = m2.getTime().getDelta(m1.getTime()).getDelta(fMinTime); + long absDelta = Math.abs(delta.getValue()); + + ColorImpl color; + if (gr != 0) { + int colIndex = Math.round((float) (absDelta / gr)); + if (colIndex < fColors.length && colIndex > 0) { + color = fColors[colIndex - 1]; + } else if (colIndex <= 0) { + color = fColors[0]; + } else { + color = fColors[fColors.length - 1]; + } + } else { + color = fColors[0]; + } + + if (color.getColor() instanceof Color) { + gcim.setBackground((Color) color.getColor()); + } + int y1 = ((GraphNode) m1.getGraphNode()).getY(); + int y2 = ((GraphNode) m2.getGraphNode()).getY(); + if (m1.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m1.getGraphNode(); + if (as.getEndTime() == m1.getTime()) { + y1 += as.getHeight(); + } + } + if (m2.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m2.getGraphNode(); + if (as.getEndTime() == m2.getTime()) { + y2 += as.getHeight(); + } + } + if (m1.getGraphNode() instanceof ExecutionOccurrence) { + + ExecutionOccurrence eo = (ExecutionOccurrence) m1.getGraphNode(); + if (m1.getEvent() == eo.getEndOccurrence()) { + y1 += eo.getHeight(); + } + + if (m2.getGraphNode() instanceof ExecutionOccurrence) { + + ExecutionOccurrence eo2 = (ExecutionOccurrence) m2.getGraphNode(); + if (m2.getEvent() == eo2.getEndOccurrence()) { + y2 += eo2.getHeight(); + } + + } + } + gcim.fillRectangle(contentsToViewX(0), contentsToViewY(Math.round(y1 * fZoomValue)), 10, Math.round((y2 - y1) * fZoomValue) + 1); + if (messageArraysStep == 1) { + Color backupColor = gcim.getForeground(); + gcim.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE)); + gcim.drawRectangle(contentsToViewX(0), contentsToViewY(Math.round(y1 * fZoomValue)), 9, Math.round((y2 - y1) * fZoomValue)); + gcim.setForeground(backupColor); + } + } + if (getViewControl().isFocusControl() || isFocusControl()) { + gcim.drawFocus(contentsToViewX(0), contentsToViewY(Math.round(fPrevNodeY * fZoomValue)), contentsToViewX(10), Math.round((fNextNodeY - fPrevNodeY) * fZoomValue)); + } + try { + gc.drawImage(dbuffer, 0, 0, getClientArea().width, getClientArea().height, 0, 0, getClientArea().width, getClientArea().height); + } catch (Exception e) { + Activator.getDefault().logError("Error drawing image", e); //$NON-NLS-1$ + } + gcim.dispose(); + dbuffer.dispose(); + gc.dispose(); + } + + /** + * Checks for focus of children. + * + * @param children + * Control to check + * @return true if child is on focus else false + */ + protected boolean checkFocusOnChilds(Control children) { + if (children instanceof Composite) { + Control[] child = ((Composite) children).getChildren(); + for (int i = 0; i < child.length; i++) { + if (child[i].isFocusControl()) { + return true; + } + checkFocusOnChilds(child[i]); + } + } + return false; + } + + @Override + public boolean isFocusControl() { + Control[] child = getChildren(); + for (int i = 0; i < child.length; i++) { + if (child[i].isFocusControl()) { + return true; + } + checkFocusOnChilds(child[i]); + } + return false; + } + + @Override + protected void contentsMouseMoveEvent(MouseEvent event) { + if (fTooltip != null) { + fTooltip.hideToolTip(); + } + super.contentsMouseMoveEvent(event); + if (!isFocusControl() || getViewControl().isFocusControl()) { + Control[] child = getParent().getChildren(); + for (int i = 0; i < child.length; i++) { + if (child[i].isFocusControl()) { + break; + } + } + } + setFocus(-1); + } + + @Override + protected void contentsMouseHover(MouseEvent e) { + if (fTooltip == null) { + fTooltip = new DrawableToolTip(this); + } + if (fFrame != null) { + setFocus(0); + for (int i = 0; i < fNodeList.size() - 1; i++) { + SDTimeEvent m1 = fNodeList.get(i); + SDTimeEvent m2 = fNodeList.get(i + 1); + + if ((SDViewPref.getInstance().excludeExternalTime()) && ((m1.getGraphNode() instanceof BaseMessage) && (m2.getGraphNode() instanceof BaseMessage))) { + BaseMessage mes1 = (BaseMessage) m1.getGraphNode(); + BaseMessage mes2 = (BaseMessage) m2.getGraphNode(); + if ((mes2.getStartLifeline() == null) || (mes1.getEndLifeline() == null)) { + continue; + } + } + + int y1 = ((GraphNode) m1.getGraphNode()).getY(); + int y2 = ((GraphNode) m2.getGraphNode()).getY(); + + if (m1.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m1.getGraphNode(); + if (as.getEndTime() == m1.getTime()) { + y1 += as.getHeight(); + } + } + if (m2.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m2.getGraphNode(); + if (as.getEndTime() == m2.getTime()) { + y2 += as.getHeight(); + } + } + if (m1.getGraphNode() instanceof ExecutionOccurrence) { + ExecutionOccurrence eo = (ExecutionOccurrence) m1.getGraphNode(); + if (m1.getEvent() == eo.getEndOccurrence()) { + y1 += eo.getHeight(); + } + + if (m2.getGraphNode() instanceof ExecutionOccurrence) { + + ExecutionOccurrence eo2 = (ExecutionOccurrence) m2.getGraphNode(); + if (m2.getEvent() == eo2.getEndOccurrence()) { + y2 += eo2.getHeight(); + } + } + } + int m1Y = Math.round(y1 * fZoomValue); + int m2Y = Math.round(y2 * fZoomValue); + if ((m1Y < e.y) && (m2Y >= e.y)) { + ITmfTimestamp delta = m2.getTime().getDelta(m1.getTime()); + fTooltip.showToolTip(delta, fMinTime, fMaxTime); + } + } + } + setFocus(0); + } + + @Override + protected void contentsMouseExit(MouseEvent e) { + if (fTooltip != null) { + fTooltip.hideToolTip(); + } + } + + @Override + protected void contentsMouseUpEvent(MouseEvent event) { + selectTimeDelta(event.y, 0); + setFocus(); + super.contentsMouseUpEvent(event); + } + + /** + * Force the time compression bar to highlight the event occurrences between + * the two given messages. The event occurrences are highlighted on the + * first message's end lifeline + * + * @param mes1 + * the first message + * @param mes2 + * the second message + */ + public void highlightRegion(BaseMessage mes1, BaseMessage mes2) { + BaseMessage localMes1 = mes1; + BaseMessage localMes2 = mes2; + + if (fFrame == null) { + return; + } + if (!(localMes1 instanceof ITimeRange)) { + return; + } + if (!(localMes2 instanceof ITimeRange)) { + return; + } + ITimeRange t1 = (ITimeRange) localMes1; + ITimeRange t2 = (ITimeRange) localMes2; + + ITmfTimestamp time1 = t1.getStartTime(); + ITmfTimestamp time2 = t2.getStartTime(); + int event1 = localMes1.getEventOccurrence(); + int event2 = localMes2.getEventOccurrence(); + + if (localMes1 instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) localMes1; + time1 = as.getEndTime(); + event1 = as.getEndOccurrence(); + } + if (localMes2 instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) localMes2; + if (as.getEndOccurrence() > as.getStartOccurrence()) { + time1 = as.getEndTime(); + event1 = as.getEndOccurrence(); + } else { + time1 = as.getStartTime(); + event1 = as.getStartOccurrence(); + } + } + + if (event1 > event2) { + BaseMessage tempMes = localMes2; + localMes2 = localMes1; + localMes1 = tempMes; + + t1 = (ITimeRange) localMes1; + t2 = (ITimeRange) localMes2; + + time1 = t1.getStartTime(); + time2 = t2.getStartTime(); + event1 = localMes1.getEventOccurrence(); + event2 = localMes2.getEventOccurrence(); + + if (localMes1 instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) localMes1; + time1 = as.getEndTime(); + event1 = as.getEndOccurrence(); + } + if (localMes2 instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) localMes2; + if (as.getEndOccurrence() > as.getStartOccurrence()) { + time1 = as.getEndTime(); + event1 = as.getEndOccurrence(); + } else { + time1 = as.getStartTime(); + event1 = as.getStartOccurrence(); + } + } + } + + ITmfTimestamp minMaxdelta = fMaxTime.getDelta(fMinTime); + double gr = (minMaxdelta.getValue()) / (double) NUMBER_STEPS; + + ITmfTimestamp delta = time2.getDelta(time1).getDelta(fMinTime); + long absDelta = Math.abs(delta.getValue()); + + int colIndex = 0; + if (gr != 0) { + colIndex = Math.round((float) (absDelta / gr)); + if (colIndex >= fColors.length) { + colIndex = fColors.length - 1; + } else if (colIndex < 0) { + colIndex = 0; + } + } else { + colIndex = 0; + } + for (int j = 0; j < fListenerList.size(); j++) { + ITimeCompressionListener list = fListenerList.get(j); + if (localMes1.getEndLifeline() != null) { + list.deltaSelected(localMes1.getEndLifeline(), event1, event2 - event1, fColors[colIndex]); + } else if (localMes2.getStartLifeline() != null) { + list.deltaSelected(localMes2.getStartLifeline(), event1, event2 - event1, fColors[colIndex]); + } else { + list.deltaSelected(localMes1.getStartLifeline(), event1, event2 - event1, fColors[colIndex]); + } + } + } + + /** + * Force the time compression bar to highlight the event occurrences between the two given messages. The event + * occurrences are highlighted on the first message's end lifeline + * + * @param mes1 the first message + * @param mes2 the second message + */ + public void highlightRegionSync(final BaseMessage mes1, final BaseMessage mes2) { + getDisplay().syncExec(new Runnable() { + @Override + public void run() { + highlightRegion(mes1, mes2); + } + }); + } + + @Override + public void scrollBy(int x, int y) { + } + + /** + * Sets the zoom value. + * + * @param value The zoom value to set. + */ + public void setZoom(float value) { + fZoomValue = value; + redraw(); + } + + /** + * Adds a listener to the time compression listener list to be notified about selected deltas. + * + * @param listener The listener to add + */ + public void addTimeCompressionListener(ITimeCompressionListener listener) { + if (!fListenerList.contains(listener)) { + fListenerList.add(listener); + } + } + + /** + * Removes a time compression listener. + * + * @param listener The listener to remove. + */ + public void removeSelectionChangedListener(ITimeCompressionListener listener) { + fListenerList.remove(listener); + } + + @Override + public void widgetDisposed(DisposeEvent e) { + if (fTooltip != null) { + fTooltip.dispose(); + } + super.removeDisposeListener(this); + for (int i = 0; i < fColors.length; i++) { + fColors[i].dispose(); + } + } + + @Override + protected void keyPressedEvent(KeyEvent event) { + if (fTooltip != null) { + fTooltip.hideToolTip(); + } + if (!isFocusControl() || getViewControl().isFocusControl()) { + Control[] child = getParent().getChildren(); + for (int i = 0; i < child.length; i++) { + if (child[i].isFocusControl()) { + break; + } + } + } + setFocus(-1); + + boolean top = false; + if (fNextNodeY == 0) { + top = true; + } + if ((fFrame != null) && (fNextNodeY == 0)) { + for (int i = 0; i < fNodeList.size() - 1 && i < 1; i++) { + SDTimeEvent m1 = fNodeList.get(i); + SDTimeEvent m2 = fNodeList.get(i + 1); + if ((SDViewPref.getInstance().excludeExternalTime()) && ((m1.getGraphNode() instanceof BaseMessage) && (m2.getGraphNode() instanceof BaseMessage))) { + BaseMessage mes1 = (BaseMessage) m1.getGraphNode(); + BaseMessage mes2 = (BaseMessage) m2.getGraphNode(); + if ((mes2.getStartLifeline() == null) || (mes1.getEndLifeline() == null)) { + continue; + } + } + + int y1 = ((GraphNode) m1.getGraphNode()).getY(); + int y2 = ((GraphNode) m2.getGraphNode()).getY(); + if (m1.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m1.getGraphNode(); + if (as.getEndTime() == m1.getTime()) { + y1 += as.getHeight(); + } + } + if (m2.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m2.getGraphNode(); + if (as.getEndTime() == m2.getTime()) { + y2 += as.getHeight(); + } + } + if (m1.getGraphNode() instanceof ExecutionOccurrence) { + ExecutionOccurrence eo = (ExecutionOccurrence) m1.getGraphNode(); + if (m1.getEvent() == eo.getEndOccurrence()) { + y1 += eo.getHeight(); + } + + if (m2.getGraphNode() instanceof ExecutionOccurrence) { + + ExecutionOccurrence eo2 = (ExecutionOccurrence) m2.getGraphNode(); + if (m2.getEvent() == eo2.getEndOccurrence()) { + y2 += eo2.getHeight(); + } + } + } + fPrevNodeY = Math.round(y1 * fZoomValue); + fNextNodeY = Math.round(y2 * fZoomValue); + } + } + + if (fLifeline != null) { + for (int j = 0; j < fListenerList.size(); j++) { + ITimeCompressionListener list = fListenerList.get(j); + list.deltaSelected(fLifeline, fLifelineStart, fLifelineNumEvents, fLifelineColor); + } + } + + if (event.keyCode == SWT.ARROW_DOWN) { + if (!top) { + selectTimeDelta(fNextNodeY + 1, 1); + } else { + selectTimeDelta(fPrevNodeY + 1, 1); + } + setFocus(1); + } else if (event.keyCode == SWT.ARROW_UP) { + selectTimeDelta(fPrevNodeY - 1, 2); + setFocus(1); + } else if (event.keyCode == SWT.ARROW_RIGHT) { + selectTimeDelta(fPrevNodeY, 1); + setFocus(1); + } + super.keyPressedEvent(event); + } + + /** + * Selects the time delta for given delta y coordinate and direction. + * + * @param dy The delta in y coordinate. + * @param direction 0 no direction, 1 = down, 2 = up + */ + protected void selectTimeDelta(int dy, int direction) { + SDTimeEvent lastM1 = null; + SDTimeEvent lastM2 = null; + int lastY1 = 0; + int lastY2 = 0; + boolean done = false; + if (fFrame != null) { + for (int i = 0; i < fNodeList.size() - 1; i++) { + SDTimeEvent m1 = fNodeList.get(i); + SDTimeEvent m2 = fNodeList.get(i + 1); + if ((SDViewPref.getInstance().excludeExternalTime()) && ((m1.getGraphNode() instanceof BaseMessage) && (m2.getGraphNode() instanceof BaseMessage))) { + BaseMessage mes1 = (BaseMessage) m1.getGraphNode(); + BaseMessage mes2 = (BaseMessage) m2.getGraphNode(); + if ((mes2.getStartLifeline() == null) || (mes1.getEndLifeline() == null)) { + continue; + } + } + + int y1 = ((GraphNode) m1.getGraphNode()).getY(); + int y2 = ((GraphNode) m2.getGraphNode()).getY(); + if (m1.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m1.getGraphNode(); + if (as.getEndTime() == m1.getTime()) { + y1 += as.getHeight(); + } + } + if (m2.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m2.getGraphNode(); + if (as.getEndTime() == m2.getTime()) { + y2 += as.getHeight(); + } + } + if (m1.getGraphNode() instanceof ExecutionOccurrence) { + ExecutionOccurrence eo = (ExecutionOccurrence) m1.getGraphNode(); + if (m1.getEvent() == eo.getEndOccurrence()) { + y1 += eo.getHeight(); + } + + if (m2.getGraphNode() instanceof ExecutionOccurrence) { + ExecutionOccurrence eo2 = (ExecutionOccurrence) m2.getGraphNode(); + if (m2.getEvent() == eo2.getEndOccurrence()) { + y2 += eo2.getHeight(); + } + } + } + int m1Y = Math.round(y1 * fZoomValue); + int m2Y = Math.round(y2 * fZoomValue); + + if ((m1Y < dy) && (m2Y > dy) || (!done && m2Y > dy && direction == 1 && lastM1 != null) || (!done && m1Y > dy && direction == 2 && lastM1 != null)) { + if (m1Y > dy && direction == 2) { + m1 = lastM1; + m2 = lastM2; + m1Y = lastY1; + m2Y = lastY2; + } + done = true; + fPrevNodeY = m1Y; + fNextNodeY = m2Y; + ITmfTimestamp minMaxdelta = fMaxTime.getDelta(fMinTime); + double gr = (minMaxdelta.getValue()) / (double) NUMBER_STEPS; + + ITmfTimestamp delta = m2.getTime().getDelta(m1.getTime()).getDelta(fMinTime); + long absDelta = Math.abs(delta.getValue()); + + int colIndex = 0; + if (gr != 0) { + colIndex = Math.round((float) (absDelta / gr)); + if (colIndex >= fColors.length) { + colIndex = fColors.length - 1; + } else if (colIndex < 0) { + colIndex = 0; + } + } else { + colIndex = 0; + } + if (m1.getGraphNode() instanceof BaseMessage) { + BaseMessage mes1 = (BaseMessage) m1.getGraphNode(); + if (mes1.getEndLifeline() != null) { + fLifeline = mes1.getEndLifeline(); + fLifelineStart = m1.getEvent(); + fLifelineNumEvents = m2.getEvent() - m1.getEvent(); + fLifelineColor = fColors[colIndex]; + } else if (m2.getGraphNode() instanceof BaseMessage && ((BaseMessage) m2.getGraphNode()).getStartLifeline() != null) { + fLifeline = ((BaseMessage) m2.getGraphNode()).getStartLifeline(); + fLifelineStart = m1.getEvent(); + fLifelineNumEvents = m2.getEvent() - m1.getEvent(); + fLifelineColor = fColors[colIndex]; + } else { + fLifeline = mes1.getStartLifeline(); + fLifelineStart = m1.getEvent(); + fLifelineNumEvents = m2.getEvent() - m1.getEvent(); + fLifelineColor = fColors[colIndex]; + } + } else if (m1.getGraphNode() instanceof ExecutionOccurrence) { + if (m2.getGraphNode() instanceof ExecutionOccurrence) { + ExecutionOccurrence eo = (ExecutionOccurrence) m2.getGraphNode(); + fLifeline = eo.getLifeline(); + fLifelineStart = m1.getEvent(); + fLifelineNumEvents = m2.getEvent() - m1.getEvent(); + fLifelineColor = fColors[colIndex]; + } else { + ExecutionOccurrence eo = (ExecutionOccurrence) m1.getGraphNode(); + fLifeline = eo.getLifeline(); + fLifelineStart = m1.getEvent(); + fLifelineNumEvents = m2.getEvent() - m1.getEvent(); + fLifelineColor = fColors[colIndex]; + } + } + for (int j = 0; j < fListenerList.size(); j++) { + ITimeCompressionListener list = fListenerList.get(j); + list.deltaSelected(fLifeline, fLifelineStart, fLifelineNumEvents, fLifelineColor); + } + break; + } + lastM1 = m1; + lastM2 = m2; + lastY1 = m1Y; + lastY2 = m2Y; + } + } + } + + /** + * Creates a fake tool tip. + */ + protected void createFakeTooltip() { + if (fTooltip == null) { + fTooltip = new DrawableToolTip(this); + } + + if (fFrame != null) { + setFocus(0); + for (int i = 0; i < fNodeList.size() - 1; i++) { + SDTimeEvent m1 = fNodeList.get(i); + SDTimeEvent m2 = fNodeList.get(i + 1); + + if ((SDViewPref.getInstance().excludeExternalTime()) && ((m1.getGraphNode() instanceof BaseMessage) && (m2.getGraphNode() instanceof BaseMessage))) { + BaseMessage mes1 = (BaseMessage) m1.getGraphNode(); + BaseMessage mes2 = (BaseMessage) m2.getGraphNode(); + if ((mes2.getStartLifeline() == null) || (mes1.getEndLifeline() == null)) { + continue; + } + } + + int y1 = ((GraphNode) m1.getGraphNode()).getY(); + int y2 = ((GraphNode) m2.getGraphNode()).getY(); + + if (m1.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m1.getGraphNode(); + if (as.getEndTime() == m1.getTime()) { + y1 += as.getHeight(); + } + } + if (m2.getGraphNode() instanceof AsyncMessage) { + AsyncMessage as = (AsyncMessage) m2.getGraphNode(); + if (as.getEndTime() == m2.getTime()) { + y2 += as.getHeight(); + } + } + if (m1.getGraphNode() instanceof ExecutionOccurrence) { + ExecutionOccurrence eo = (ExecutionOccurrence) m1.getGraphNode(); + if (m1.getEvent() == eo.getEndOccurrence()) { + y1 += eo.getHeight(); + } + + if (m2.getGraphNode() instanceof ExecutionOccurrence) { + + ExecutionOccurrence eo2 = (ExecutionOccurrence) m2.getGraphNode(); + if (m2.getEvent() == eo2.getEndOccurrence()) { + y2 += eo2.getHeight(); + } + } + } + int m1Y = Math.round(y1 * fZoomValue); + int m2Y = Math.round(y2 * fZoomValue); + if ((m1Y < fPrevNodeY + 1) && (m2Y >= fPrevNodeY + 1)) { + ITmfTimestamp delta = m2.getTime().getDelta(m1.getTime()); + fTooltip.showToolTip(delta, fMinTime, fMaxTime); + fTooltip.hideToolTip(); + } + } + } + } + + /** + * Traverse Listener implementation. + */ + protected static class LocalTraverseListener implements TraverseListener { + @Override + public void keyTraversed(TraverseEvent e) { + if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) { + e.doit = true; + } + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/AsyncMessage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/AsyncMessage.java new file mode 100755 index 0000000000..2355b482be --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/AsyncMessage.java @@ -0,0 +1,469 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import java.util.Comparator; + +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.SortAsyncForBackward; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.SortAsyncMessageComparator; + +/** + * A AsyncMessage is a asynchronous message which appear at two different event occurrences on each lifeline ends (sender + * and receiver).<br> + * <br> + * <br> + * Usage example: + * + * <pre> + * Frame frame; + * Lifeline lifeLine1; + * Lifeline lifeLine2; + * + * AsyncMessage message = new AsyncMessage(); + * // Create a new event occurrence on each lifeline + * lifeline1.getNewOccurrenceIndex(); + * lifeline2.getNewOccurrenceIndex(); + * // Set the message sender and receiver + * message.setStartLifeline(lifeLine1); + * message.setEndLifline(lifeline2); + * message.setName("Message label"); + * // add the message to the frame + * frame.addMessage(message); + * </pre> + * + * @see Lifeline Lifeline for more event occurence details + * @version 1.0 + * @author sveyrier + * @since 2.0 + */ +public class AsyncMessage extends BaseMessage implements ITimeRange { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The grahNode ID constant + */ + public static final String ASYNC_MESS_TAG = "AsyncMessage"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * Flag whether message has time information or not. + */ + private boolean fHasTime = false; + /** + * The time when the message begin + */ + private ITmfTimestamp fEndTime = new TmfTimestamp(); + /** + * The time when the message end + */ + private ITmfTimestamp fStartTime = new TmfTimestamp(); + /** + * The associated message. + */ + protected AsyncMessageReturn fMessageReturn = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public AsyncMessage() { + setColorPrefId(ISDPreferences.PREF_ASYNC_MESS); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int getX() { + int x = super.getX(true); + int activationWidth = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + if ((getStartLifeline() != null) && (getEndLifeline() != null) && (getStartLifeline().getX() > getEndLifeline().getX())) { + activationWidth = -activationWidth; + } + + if (isMessageStartInActivation(getStartOccurrence())) { + x = x + activationWidth; + } + return x; + } + + @Override + public int getY() { + if ((getStartLifeline() != null) && (getEndLifeline() != null)) { + return getEndLifeline().getY() + getEndLifeline().getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getStartOccurrence(); + } + return super.getY(); + } + + @Override + public int getWidth() { + int width = super.getWidth(true); + int activationWidth = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + if ((getStartLifeline() != null) && (getEndLifeline() != null) && (getStartLifeline().getX() > getEndLifeline().getX())) { + activationWidth = -activationWidth; + } + + if (isMessageStartInActivation(getStartOccurrence())) { + width = width - activationWidth; + } + + if (isMessageEndInActivation(getEndOccurrence())) { + width = width - activationWidth; + } + + return width; + } + + @Override + public int getHeight() { + if ((getStartLifeline() != null) && (getEndLifeline() != null)) { + return (getEndLifeline().getY() + getEndLifeline().getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getEndOccurrence()) - getY(); + } + return super.getHeight(); + } + + /** + * Set the message return associated with this message. + * + * @param message the message return to associate + */ + protected void setMessageReturn(AsyncMessageReturn message) { + fMessageReturn = message; + } + + /** + * Set the event occurrence attached to this message for its end lifeline + * + * @param occurrence the event occurrence to set + */ + @Override + public void setEndOccurrence(int occurrence) { + super.setEndOccurrence(occurrence); + if (getStartLifeline() == null) { + setStartOccurrence(occurrence); + } + informFrame(getEndLifeline(), occurrence); + } + + /** + * Informs the given lifeline about the maximum occurrence if applicable. + * + * @param lifeLine + * Concerned lifeline + * @param occurrence + * Occurrence number + */ + protected void informFrame(Lifeline lifeLine, int occurrence) { + if ((lifeLine != null) && (lifeLine.getFrame() != null) && (lifeLine.getFrame().getMaxEventOccurrence() < occurrence)) { + lifeLine.getFrame().setMaxEventOccurrence(occurrence); + } + } + + /** + * Set the event occurrence attached to this message for its start lifeline + * + * @param occurrence the event occurrence to set + */ + @Override + public void setStartOccurrence(int occurrence) { + super.setStartOccurrence(occurrence); + if (getEndLifeline() == null) { + setEndOccurrence(getStartOccurrence()); + } + informFrame(getStartLifeline(), occurrence); + } + + /** + * Set the lifeLine which has sent the message.<br> + * A new EventOccurence will be create on this lifeLine.<br> + * + * @param lifeline the message sender + */ + public void autoSetStartLifeline(Lifeline lifeline) { + lifeline.getNewEventOccurrence(); + setStartLifeline(lifeline); + } + + /** + * Set the lifeLine which has received the message.<br> + * A new EventOccurence will be create on this lifeLine.<br> + * + * @param lifeline the message receiver + */ + public void autoSetEndLifeline(Lifeline lifeline) { + lifeline.getNewEventOccurrence(); + setEndLifeline(lifeline); + } + + @Override + public void setStartLifeline(Lifeline lifeline) { + super.setStartLifeline(lifeline); + setStartOccurrence(getStartLifeline().getEventOccurrence()); + if (getEndLifeline() == null) { + setEndOccurrence(getStartOccurrence()); + } + } + + @Override + public void setEndLifeline(Lifeline lifeline) { + super.setEndLifeline(lifeline); + setEventOccurrence(getEndLifeline().getEventOccurrence()); + } + + /** + * Returns true if the point C is on the segment defined with the point A and B + * + * @param xA point A x coordinate + * @param yA point A y coordinate + * @param xB point B x coordinate + * @param yB point B y coordinate + * @param xC point C x coordinate + * @param yC point C y coordinate + * @return Return true if the point C is on the segment defined with the point A and B, else otherwise + */ + protected boolean isNearSegment(int xA, int yA, int xB, int yB, int xC, int yC) { + if ((xA > xB) && (xC > xA)) { + return false; + } + if ((xA < xB) && (xC > xB)) { + return false; + } + if ((xA < xB) && (xC < xA)) { + return false; + } + if ((xA > xB) && (xC < xB)) { + return false; + } + double distAB = Math.sqrt((xB - xA) * (xB - xA) + (yB - yA) * (yB - yA)); + double scalar = ((xB - xA) * (xC - xA) + (yB - yA) * (yC - yA)) / distAB; + double distAC = Math.sqrt((xC - xA) * (xC - xA) + (yC - yA) * (yC - yA)); + double distToSegment = Math.sqrt(Math.abs(distAC * distAC - scalar * scalar)); + if (distToSegment <= Metrics.MESSAGE_SELECTION_TOLERANCE) { + return true; + } + return false; + } + + @Override + public boolean contains(int x, int y) { + // Is it a self message? + if (getStartLifeline() == getEndLifeline()) { + return super.contains(x, y); + } + if (isNearSegment(getX(), getY(), getX() + getWidth(), getY() + getHeight(), x, y)) { + return true; + } + int messageMaxWidth = Metrics.swimmingLaneWidth() - Metrics.EXECUTION_OCCURRENCE_WIDTH; + int nameWidth = getName().length() * Metrics.getAverageCharWidth(); + if (getName().length() * Metrics.getAverageCharWidth() > messageMaxWidth) { + if (GraphNode.contains(getX(), getY() - Metrics.MESSAGES_NAME_SPACING - Metrics.getMessageFontHeigth(), messageMaxWidth, Metrics.getMessageFontHeigth(), x, y)) { + return true; + } + } else { + if (GraphNode.contains(getX() + (messageMaxWidth - nameWidth) / 2, getY() + getHeight() / 2 - Metrics.MESSAGES_NAME_SPACING - Metrics.getMessageFontHeigth(), nameWidth, Metrics.getMessageFontHeigth(), x, y)) { + return true; + } + } + return false; + } + + /** + * Draws the asynchronous message using giving graphical context. + * + * @param context A graphical context to draw in. + */ + protected void drawAsyncMessage(IGC context) { + if (getStartLifeline() != null && getEndLifeline() != null && getStartLifeline() == getEndLifeline() && (getStartOccurrence() != getEndOccurrence())) { + int x = getX(); + int y = getY(); + int height = getHeight(); + int tempx = 0; + boolean startInActivation = isMessageStartInActivation(getStartOccurrence()); + boolean endInActivation = isMessageEndInActivation(getEndOccurrence()); + + if (endInActivation && !startInActivation) { + tempx = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + } + if (startInActivation && !endInActivation) { + tempx = -Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + } + + int tempy = Metrics.INTERNAL_MESSAGE_WIDTH / 2; + if (getHeight() <= Metrics.INTERNAL_MESSAGE_WIDTH) { + tempy = getHeight() / 2; + } + + context.drawLine(x, y, x + Metrics.INTERNAL_MESSAGE_WIDTH / 2, y); + context.drawLine(x + Metrics.INTERNAL_MESSAGE_WIDTH, y + tempy, x + Metrics.INTERNAL_MESSAGE_WIDTH, y + height - tempy); + context.drawLine(x + tempx, y + height, x + Metrics.INTERNAL_MESSAGE_WIDTH / 2, y + height); + + Double xt = Double.valueOf(Math.cos(0.75) * 7); + Double yt = Double.valueOf(Math.sin(0.75) * 7); + + context.drawLine(x + xt.intValue() + tempx, y + height + yt.intValue(), x + tempx, y + height); + context.drawArc(x, y, Metrics.INTERNAL_MESSAGE_WIDTH, 2 * tempy, 0, 90); + context.drawArc(x, y + height, Metrics.INTERNAL_MESSAGE_WIDTH, -2 * tempy, 0, -90); + context.drawLine(x + xt.intValue() + tempx, y + height - yt.intValue(), x + tempx, y + height); + + context.drawTextTruncated(getName(), x + Metrics.INTERNAL_MESSAGE_WIDTH + Metrics.INTERNAL_MESSAGE_V_MARGIN, y, Metrics.swimmingLaneWidth() - Metrics.EXECUTION_OCCURRENCE_WIDTH + -Metrics.INTERNAL_MESSAGE_WIDTH, + +Metrics.MESSAGES_NAME_SPACING - Metrics.getMessageFontHeigth(), !isSelected()); + } else { + super.draw(context); + } + } + + @Override + public void draw(IGC context) { + if (!isVisible()) { + return; + } + + ISDPreferences pref = SDViewPref.getInstance(); + + // Draw it selected? + if (isSelected() && (getStartLifeline() != null && getEndLifeline() != null && getStartLifeline() == getEndLifeline() && (getStartOccurrence() != getEndOccurrence()))) { + /* + * Draw it twice First time, bigger inverting selection colors Second time, regular drawing using selection + * colors This create the highlight effect + */ + context.setForeground(pref.getBackGroundColorSelection()); + context.setLineWidth(Metrics.SELECTION_LINE_WIDTH); + drawAsyncMessage(context); + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + // Second drawing is done after the else + } else { + context.setBackground(pref.getBackGroundColor(getColorPrefId())); + context.setForeground(pref.getForeGroundColor(getColorPrefId())); + } + if (hasFocus()) { + context.setDrawTextWithFocusStyle(true); + } + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + drawAsyncMessage(context); + if (hasFocus()) { + context.setDrawTextWithFocusStyle(false); + } + } + + /** + * Set the time when the message end + * + * @param time the time when the message end + * @since 2.0 + */ + public void setEndTime(ITmfTimestamp time) { + fEndTime = time; + fHasTime = true; + if (getStartLifeline() != null && getStartLifeline().getFrame() != null) { + getStartLifeline().getFrame().setHasTimeInfo(true); + } else if (getEndLifeline() != null && getEndLifeline().getFrame() != null) { + getEndLifeline().getFrame().setHasTimeInfo(true); + } + } + + /** + * Set the time when the message start + * + * @param time the time when the message start + * @since 2.0 + */ + public void setStartTime(ITmfTimestamp time) { + fStartTime = time; + fHasTime = true; + if (getStartLifeline() != null && getStartLifeline().getFrame() != null) { + getStartLifeline().getFrame().setHasTimeInfo(true); + } else if (getEndLifeline() != null && getEndLifeline().getFrame() != null) { + getEndLifeline().getFrame().setHasTimeInfo(true); + } + } + + /** + * @since 2.0 + */ + @Override + public ITmfTimestamp getEndTime() { + return fEndTime; + } + + /** + * @since 2.0 + */ + @Override + public ITmfTimestamp getStartTime() { + return fStartTime; + } + + @Override + public boolean hasTimeInfo() { + return fHasTime; + } + + /** + * @return message return instance or null + * @since 2.0 + */ + public AsyncMessageReturn getMessageReturn() { + return fMessageReturn; + } + + @Override + public boolean isVisible(int x, int y, int width, int height) { + int toDrawY = getY(); + int toDrawHeight = getHeight(); + if ((toDrawY > y + height + Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth()) && (toDrawY + toDrawHeight > y + height + Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth())) { + return false; + } + if (toDrawY < y && (toDrawY + toDrawHeight < y)) { + return false; + } + return super.isVisible(x, y, width, height); + } + + @Override + public Comparator<GraphNode> getComparator() { + return new SortAsyncMessageComparator(); + } + + @Override + public String getArrayId() { + return ASYNC_MESS_TAG; + } + + @Override + public Comparator<GraphNode> getBackComparator() { + return new SortAsyncForBackward(); + } + + @Override + public boolean positiveDistanceToPoint(int x, int y) { + int mY = getY(); + int mH = getHeight(); + if ((mY > y) || (mY + mH > y)) { + return true; + } + return false; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/AsyncMessageReturn.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/AsyncMessageReturn.java new file mode 100755 index 0000000000..dbc5160bca --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/AsyncMessageReturn.java @@ -0,0 +1,112 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * The message return graph node implementation.<br> + * This class differs on the AsynMessage class only on the drawing line style (dashed instead of plain line).<br> + * Message return are generally associated to a message. This means, they are connected to the same lifelines than the + * associated message but in the opposite direction and for a different event occurrence.<br> + * <br> + * WARNING: The association validity is not checked, it is not necessary to provide a valid association, not even needed + * to set an association to drawn a message with a message return style.<br> + * + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessage AsyncMessage for usage example + * @version 1.0 + * @author sveyrier + * + */ +public class AsyncMessageReturn extends AsyncMessage { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The grahNode ID constant + */ + public static final String ASYNC_MESS_RET_TAG = "AsyncMessageRet"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The corresponding asynchronous message. + */ + protected AsyncMessage fMessage; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor. + */ + public AsyncMessageReturn() { + setColorPrefId(ISDPreferences.PREF_ASYNC_MESS_RET); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Set the associated message (the message it is the return).<br> + * Setting the association will activate the navigation in the default sequence diagram implementation to the + * message when the user right click on this message return.<br> + * + * @param parentMessage the message to associate + */ + public void setMessage(AsyncMessage parentMessage) { + fMessage = parentMessage; + } + + /** + * Returns the associated message (the message it is the return).<br> + * + * @return parentMessage the message to associate + * @since 2.0 + */ + public AsyncMessage getMessage() { + return fMessage; + } + + @Override + public void draw(IGC context) { + if (!isVisible()) { + return; + } + + ISDPreferences pref = SDViewPref.getInstance(); + + setColorPrefId(ISDPreferences.PREF_ASYNC_MESS_RET); + int oldStyle = context.getLineStyle(); + // Message return are dashed + context.setLineStyle(context.getLineDotStyle()); + if (!isSelected()) { + context.setBackground(pref.getBackGroundColor(getColorPrefId())); + context.setForeground(pref.getForeGroundColor(getColorPrefId())); + } + super.draw(context); + // restore the context + context.setLineStyle(oldStyle); + } + + @Override + public String getArrayId() { + return ASYNC_MESS_RET_TAG; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BaseMessage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BaseMessage.java new file mode 100755 index 0000000000..532cb51e90 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BaseMessage.java @@ -0,0 +1,724 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * The base UML2 syncMessages implementation.<br> + * This abstract class only define one event occurrence to attach to the message.<br> + * Usually a message has two event occurrences attached, one for both ends. But some syncMessages(like synchronous + * syncMessages) only need one event occurrence to represent the time when they appear. Others kind of message + * representations (like asynchronous syncMessages) will be responsible to define the missing second eventOccurrence + * property.<br> + * <br> + * + * @see Lifeline Lifeline for more event occurence details + * @version 1.0 + * @author sveyrier + */ +public abstract class BaseMessage extends GraphNode { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The lifeline which send the message + */ + private Lifeline fStartLifeline = null; + /** + * The lifeline which receive the message + */ + private Lifeline fEndLifeline = null; + /** + * The visiblitiy flag. + */ + private boolean fVisible = true; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int getX() { + // returns the exact x coordinate + return getX(false); + } + + @Override + public int getY() { + /* + * Note: lifeline.getY() return the y coordinate of the top left corner of the rectangle which contain the + * lifeline name getHeight return the height of this rectangle The message y coordinate is then relative to this + * position depending of its eventOccurrence Space between syncMessages is constant + */ + if ((fStartLifeline != null) && (fEndLifeline != null)) { + /* + * Regular message, both ends are attached to a lifeline + */ + return fEndLifeline.getY() + fEndLifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getEndOccurrence(); + + } + /* + * UML2 lost message kind + */ + if (fStartLifeline != null) { + return fStartLifeline.getY() + fStartLifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getEndOccurrence(); + } + + /* + * UML2 found message kind + */ + if (fEndLifeline != null) { + return fEndLifeline.getY() + fEndLifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getEndOccurrence(); + } + // return 0 by default + return 0; + } + + @Override + public int getWidth() { + // Returns the exact width + return getWidth(false); + } + + @Override + public int getHeight() { + return 0; + } + + /** + * Returns the graph node x coordinate.<br> + * Depending on the quick parameter a approximative or exact value is return.<br> + * The approximative value does not take into account if both message ends are connected to a Lifeline Execution + * Occurrence.<br> + * Execution occurrence on a lifeline increase the vertical line width which represent the lifeline, this directly + * affect the message x coordinate and width.<br> + * <br> + * This method is typically used to faster execute none graphical operation like tooltip lookup.<br> + * <br> + * + * @param quick true to get an approximative value<br> + * false to get the exact x value<br> + * @return the graph node x coordinate + */ + protected int getX(boolean quick) { + int x = 0; + int activationWidth = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + if ((fStartLifeline != null) && (fEndLifeline != null)) { + x = fStartLifeline.getX() + Metrics.getLifelineWidth() / 2; + } else { + if (fStartLifeline != null) { + x = fStartLifeline.getX() + Metrics.getLifelineWidth() / 2; + } + + if (fEndLifeline != null) { + x = fEndLifeline.getX() - Metrics.LIFELINE_SPACING / 2; + } + } + + if (quick) { + return x; + } + + if ((fStartLifeline != null) && (fEndLifeline != null) && (fStartLifeline.getX() > fEndLifeline.getX())) { + activationWidth = -activationWidth; + } + + if (isMessageStartInActivation(getEndOccurrence())) { + x = x + activationWidth; + } + + return x; + } + + /** + * Returns the graph node width.<br> + * Depending on the quick parameter a approximative or exact value is returned.<br> + * The approximative value does not take into account if both message ends are connected to a Lifeline Execution + * Occurrence.<br> + * Execution occurrence on a lifeline increase the vertical line width which represent the lifeline, this directly + * affect the message x coordinate and width.<br> + * <br> + * This method is typically used to faster execute none graphical operation like tooltip lookup.<br> + * <br> + * + * @param quick true to get an approximative value<br> + * false to get the exact x value + * @return the graph node width + */ + protected int getWidth(boolean quick) { + int width = 0; + int activationWidth = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + if ((fStartLifeline != null) && (fEndLifeline != null)) { + if (fStartLifeline == fEndLifeline) { + width = Metrics.INTERNAL_MESSAGE_WIDTH + Metrics.EXECUTION_OCCURRENCE_WIDTH; + } else { + width = fEndLifeline.getX() + Metrics.getLifelineWidth() / 2 - getX(true); + } + } else { + if (fStartLifeline != null) { + width = Metrics.swimmingLaneWidth() / 2; + } + if (fEndLifeline != null) { + width = Metrics.swimmingLaneWidth() / 2; + } + } + + if (quick) { + return width; + } + + if ((fStartLifeline != null) && (fEndLifeline != null) && (fStartLifeline.getX() > fEndLifeline.getX())) { + activationWidth = -activationWidth; + } + + if (isMessageStartInActivation(getEndOccurrence())) { + width = width - activationWidth; + } + + if (isMessageEndInActivation(getEndOccurrence())) { + width = width - activationWidth; + } + + return width; + } + + @Override + public boolean isVisible(int x, int y, int width, int height) { + // ***Common*** syncMessages visibility + // draw the message only if at least one end is visible + if (fEndLifeline != null && (fEndLifeline.isVisible(x, y, width, height)) || (fStartLifeline != null && fStartLifeline.isVisible(x, y, width, height))) { + return true; + } + // In this case it can be a message which cross the whole visible area + else if (fEndLifeline != null && (!fEndLifeline.isVisible(x, y, width, height)) && (fStartLifeline != null && !fStartLifeline.isVisible(x, y, width, height))) { + if (fEndLifeline.getX() > x + width && fStartLifeline.getX() < x) { + return true; + } else if (fStartLifeline.getX() > x + width && fEndLifeline.getX() < x) { + return true; + } + } + return false; + } + + /** + * Sets the visibility value. + * + * @param value The visibility to set. + */ + public void setVisible(boolean value) { + fVisible = value; + } + + /** + * @return the visibility value. + */ + public boolean isVisible() { + return fVisible; + } + + /** + * Set the lifeline from which this message has been sent. + * + * @param lifeline - the message sender + */ + public void setStartLifeline(Lifeline lifeline) { + fStartLifeline = lifeline; + } + + /** + * Returns the lifeline from which this message has been sent. + * + * @return the message sender + */ + public Lifeline getStartLifeline() { + return fStartLifeline; + } + + /** + * Returns the lifeline which has received this message. + * + * @return the message receiver + */ + public Lifeline getEndLifeline() { + return fEndLifeline; + } + + /** + * Set the lifeline which has receive this message. + * + * @param lifeline the message receiver + */ + public void setEndLifeline(Lifeline lifeline) { + fEndLifeline = lifeline; + } + + /** + * Set the event occurrence when this message occurs.<br> + * + * @param occurrence the event occurrence to assign to this message.<br> + * @see Lifeline Lifeline for more event occurence details + */ + protected void setEventOccurrence(int occurrence) { + setEndOccurrence(occurrence); + } + + /** + * Returns the event occurence when is message occurs.<br> + * + * @return the event occurrence assigned to this message.<br> + * @see Lifeline Lifeline for more event occurence details + */ + public int getEventOccurrence() { + return getEndOccurrence(); + } + + /** + * Determines if the given eventOccurence occurs on a executionOccurence owned by the sending lifeline.<br> + * WARNING: this method will return a valid result only for execution occurrences which are visible in the View.<br> + * As consequence this method is only used for drawing purpose, especially to determine the exact message x + * coordinate and width.<br> + * + * @see BaseMessage#getX(boolean) + * @param event the event occurrence to test + * @return true if occurs on a execution occurrence owned by the sending lifeine, false otherwise + */ + protected boolean isMessageStartInActivation(int event) { + boolean inActivation = false; + if ((fStartLifeline != null) && (fStartLifeline.getExecutions() != null)) { + // int acIndex=startLifeline.getExecOccurrenceDrawIndex(); + // acIndex = first visible execution occurrence + // for drawing speed reason with only search on the visivle subset + int thisY = getY(); + for (int i = 0; i < fStartLifeline.getExecutions().size(); i++) { + BasicExecutionOccurrence toDraw = (BasicExecutionOccurrence) fStartLifeline.getExecutions().get(i); + if ((event >= toDraw.getStartOccurrence()) && (event <= toDraw.getEndOccurrence())) { + inActivation = true; + } + // if we are outside the visible area we stop right now + // This works because execution occurrences are ordered along the Y axis + if (toDraw.getY() > thisY) { + break; + } + } + } + return inActivation; + } + + /** + * Determines if the given event occurrence occurs on a execution occurrence owned by the receiving lifeline.<br> + * WARNING: this method will return a valid result only for execution occurrences which are visible in the View.<br> + * As consequence this method is only used for drawing purpose, especially to determine the exact message x + * coordinate and width.<br> + * + * @see BaseMessage#getX(boolean) + * @param event the event occurrence to test + * @return true if occurs on a execution occurrence owned by the receiving lifeline, false otherwise + */ + protected boolean isMessageEndInActivation(int event) { + boolean inActivation = false; + if ((fEndLifeline != null) && (fEndLifeline.getExecutions() != null)) { + // acIndex = first visible execution occurrence + // for drawing speed reason with only search on the visivle subset + for (int i = 0; i < fEndLifeline.getExecutions().size(); i++) { + BasicExecutionOccurrence toDraw = (BasicExecutionOccurrence) fEndLifeline.getExecutions().get(i); + if ((event >= toDraw.getStartOccurrence()) && (event <= toDraw.getEndOccurrence())) { + inActivation = true; + } + // if we are outside the visible area we stop right now + // This works because execution occurrences are ordered along the Y axis + if (toDraw.getY() > getY()) { + break; + } + } + } + return inActivation; + } + + @Override + public boolean contains(int xValue, int yValue) { + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); + + // Used to create a rectangle which contains the message label to allow selection when clicking the label + int tempHeight = Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth(); + + // Is it a self message? + if (fStartLifeline == fEndLifeline) { + /* + * Rectangle.contains(x,y, width, height) does not works with negative height or width We check here if the + * rectangle width is negative. + */ + if (getName().length() * Metrics.getAverageCharWidth() > Metrics.swimmingLaneWidth() - Metrics.EXECUTION_OCCURRENCE_WIDTH / 2 + -Metrics.INTERNAL_MESSAGE_WIDTH) { + if (GraphNode.contains(x + Metrics.INTERNAL_MESSAGE_WIDTH + 10, y, Metrics.swimmingLaneWidth() - Metrics.EXECUTION_OCCURRENCE_WIDTH / 2 + -Metrics.INTERNAL_MESSAGE_WIDTH, Metrics.getMessageFontHeigth(), xValue, yValue)) { + return true; + } + } else { + if (GraphNode.contains(x + Metrics.INTERNAL_MESSAGE_WIDTH + 10, y, getName().length() * Metrics.getAverageCharWidth(), Metrics.getMessageFontHeigth(), xValue, yValue)) { + return true; + } + } + + // Test if the point is in part 1 of the self message + // see: "private void drawMessage (NGC context)" method for self message drawing schema + if (GraphNode.contains(x, y - Metrics.MESSAGE_SELECTION_TOLERANCE / 2, Metrics.INTERNAL_MESSAGE_WIDTH / 2, Metrics.MESSAGE_SELECTION_TOLERANCE, xValue, yValue)) { + return true; + } + + // Test if the point is in part 3 of the self message + if (GraphNode.contains(x + Metrics.INTERNAL_MESSAGE_WIDTH - Metrics.MESSAGE_SELECTION_TOLERANCE / 2, y, Metrics.MESSAGE_SELECTION_TOLERANCE, height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT, xValue, yValue)) { + return true; + } + + // Test if the point is in part 5 of the self message + if (GraphNode.contains(x, y + height - Metrics.MESSAGE_SELECTION_TOLERANCE / 2 + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT, Metrics.INTERNAL_MESSAGE_WIDTH / 2, Metrics.MESSAGE_SELECTION_TOLERANCE, xValue, yValue)) { + return true; + } + + // false otherwise + return false; + } + if (GraphNode.contains(x, y - tempHeight, width, tempHeight, xValue, yValue)) { + return true; + } + // false otherwise + return false; + } + + /** + * Method to draw the message using the graphical context. + * + * @param context A graphical context to draw in. + */ + protected void drawMessage(IGC context) { + int fX = 0; + int fY = 0; + int fW = 0; + int fH = 0; + + // temporary store the coordinates to avoid more methods calls + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); + + ISDPreferences pref = SDViewPref.getInstance(); + + // UML2 found message (always drawn from left to right) + // or UML2 lost message (always drawn from left to right) + if ((fStartLifeline == null || fEndLifeline == null) && fStartLifeline != fEndLifeline) { + // Draw the message label above the message and centered + // The label is truncated if it cannot fit between the two message end + // 2*Metrics.MESSAGES_NAME_SPACING = space above the label + space below the label + IColor temp = context.getForeground(); + context.setForeground(pref.getFontColor(getColorPrefId())); + context.drawTextTruncatedCentred(getName(), x, y - Metrics.getMessageFontHeigth() - 2 * Metrics.MESSAGES_NAME_SPACING, width, 2 * Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth(), !isSelected()); + context.setForeground(temp); + int margin = 0; + if (fEndLifeline == null) { + margin = Metrics.MESSAGE_CIRCLE_RAY; + } + + // Draw the message main line + context.drawLine(x, y, x + width, y + height); + // Draw the two little lines which make a arrow part of the message + Double xt = Double.valueOf(Math.cos(0.75) * 7); + Double yt = Double.valueOf(Math.sin(0.75) * 7); + if (context.getLineStyle() == context.getLineSolidStyle()) { + IColor backcolor = context.getBackground(); + context.setBackground(context.getForeground()); + int[] points = { x + width - margin, y + height, x + width - xt.intValue() - margin, y + height - yt.intValue(), x + width - xt.intValue() - margin, y + height + yt.intValue(), x + width - margin, y + height }; + context.fillPolygon(points); + context.drawPolygon(points); + context.setBackground(backcolor); + } else { + int currentStyle = context.getLineStyle(); + int currentWidth = context.getLineWidth(); + context.setLineWidth(currentWidth + 2); + context.setLineStyle(context.getLineSolidStyle()); + context.drawLine(x + width - xt.intValue() - margin, y + height - yt.intValue(), x + width - margin, y + height); + context.drawLine(x + width - xt.intValue() - margin, y + height + yt.intValue(), x + width - margin, y + height); + context.setLineStyle(currentStyle); + context.setLineWidth(currentWidth); + } + IColor storedColor = context.getBackground(); + context.setBackground(context.getForeground()); + + // Draw a circle at the message end (endLifeline side) + int ray = Metrics.MESSAGE_CIRCLE_RAY; + if (context.getLineWidth() != Metrics.NORMAL_LINE_WIDTH) { + ray = ray + Metrics.SELECTION_LINE_WIDTH - Metrics.NORMAL_LINE_WIDTH; + } + if (fStartLifeline == null) { + context.fillOval(x - ray, y - ray, ray * 2, ray * 2); + } else { + context.fillOval(x + width - ray, y + height - ray, ray * 2, ray * 2); + } + context.setBackground(storedColor); + context.setForeground(pref.getFontColor(getColorPrefId())); + fX = x; + fY = y - yt.intValue(); + fW = width; + fH = height + 2 * yt.intValue(); + } + // it is self message (always drawn at the left side of the owning lifeLifeline) + else if (fStartLifeline != null && fEndLifeline != null && fStartLifeline == fEndLifeline) { + /* + * Self syncMessages are drawn in 5 parts 1 -----------+ + 2 + | | | 3 | + 5 + 4 -----------+ + */ + int tempy = Metrics.INTERNAL_MESSAGE_WIDTH / 2; + if (Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT <= Metrics.INTERNAL_MESSAGE_WIDTH) { + tempy = Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT / 2; + } + + // Part 1 + context.drawLine(x, y, x + Metrics.INTERNAL_MESSAGE_WIDTH / 2, y); + // Part 3 + context.drawLine(x + Metrics.INTERNAL_MESSAGE_WIDTH, y + tempy, x + Metrics.INTERNAL_MESSAGE_WIDTH, y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT - tempy); + // Part 5 + context.drawLine(x, y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT, x + Metrics.INTERNAL_MESSAGE_WIDTH / 2, y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT); + + Double xt = Double.valueOf(Math.cos(0.75) * 7); + Double yt = Double.valueOf(Math.sin(0.75) * 7); + + fX = x; + fY = y; + fW = Metrics.INTERNAL_MESSAGE_WIDTH; + fH = height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT; + + // Draw the two little lines which make a arrow part of the message + if (context.getLineStyle() == context.getLineSolidStyle()) { + IColor backcolor = context.getBackground(); + context.setBackground(context.getForeground()); + int[] points = { x, y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT, x + xt.intValue(), y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT + yt.intValue(), x + xt.intValue(), + y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT - yt.intValue(), x, y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT }; + context.fillPolygon(points); + context.drawPolygon(points); + context.setBackground(backcolor); + } else { + int currentStyle = context.getLineStyle(); + int currentWidth = context.getLineWidth(); + context.setLineWidth(currentWidth + 2); + context.setLineStyle(context.getLineSolidStyle()); + context.drawLine(x + xt.intValue(), y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT + yt.intValue(), x, y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT); + context.drawLine(x + xt.intValue(), y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT - yt.intValue(), x, y + height + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT); + context.setLineStyle(currentStyle); + context.setLineWidth(currentWidth); + } + + // Part 2 + context.drawArc(x, y, Metrics.INTERNAL_MESSAGE_WIDTH, 2 * tempy, 0, 90); + // Part 4 + context.drawArc(x, y + Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT, Metrics.INTERNAL_MESSAGE_WIDTH, -2 * tempy, 0, -90); + + // Draw the message label above the message and centered + // The label is truncated if it cannot fit between the two message end + // 2*Metrics.MESSAGES_NAME_SPACING = space above the label + space below the label + + // the space available for the text is sorter if are drawing internal message on the last lifeline + context.setForeground(pref.getFontColor(getColorPrefId())); + if (fStartLifeline.getIndex() == fStartLifeline.getFrame().getHorizontalIndex()) { + context.drawTextTruncated(getName(), x + width + Metrics.INTERNAL_MESSAGE_V_MARGIN / 2, y, Metrics.swimmingLaneWidth() / 2 - Metrics.EXECUTION_OCCURRENCE_WIDTH + -Metrics.INTERNAL_MESSAGE_WIDTH, +Metrics.MESSAGES_NAME_SPACING + - Metrics.getMessageFontHeigth(), !isSelected()); + } else { + context.drawTextTruncated(getName(), x + width + Metrics.INTERNAL_MESSAGE_V_MARGIN / 2, y, Metrics.swimmingLaneWidth() - Metrics.EXECUTION_OCCURRENCE_WIDTH + -Metrics.INTERNAL_MESSAGE_WIDTH, + +Metrics.MESSAGES_NAME_SPACING - Metrics.getMessageFontHeigth(), !isSelected()); + } + } + // it is regular message + else if (fStartLifeline != null && fEndLifeline != null) { + // Draw the message main line + context.drawLine(x, y, x + width, y + height); + + int spaceBTWStartEnd = fEndLifeline.getX() - fStartLifeline.getX(); + + double a = height; + double b = width; + double angle = Math.atan(a / b); + // Compute the coordinates of the two little lines which make the arrow part of the message + int sign = 1; + if (spaceBTWStartEnd < 0) { + sign = -1; + } + Double x1 = Double.valueOf(sign * Math.cos(angle - 0.75) * 7); + Double y1 = Double.valueOf(sign * Math.sin(angle - 0.75) * 7); + Double x2 = Double.valueOf(sign * Math.cos(angle + 0.75) * 7); + Double y2 = Double.valueOf(sign * Math.sin(angle + 0.75) * 7); + + fX = getX(); + fY = y + height - y2.intValue(); + fW = getWidth(); + fH = y2.intValue() - y1.intValue() + 1; + if (fW < 0) { + fW = -fW; + fX = fX - fW; + } + + if (fH < 0) { + fH = -fH; + fY = fY - fH; + } + + // Draw the two little lines which make a arrow part of the message + if (context.getLineStyle() == context.getLineSolidStyle()) { + IColor backcolor = context.getBackground(); + context.setBackground(context.getForeground()); + int[] points = { x + width - x1.intValue(), y + height - y1.intValue(), x + width, y + height, x + width - x2.intValue(), y + height - y2.intValue(), x + width - x1.intValue(), y + height - y1.intValue() }; + context.fillPolygon(points); + context.drawPolygon(points); + context.setBackground(backcolor); + } else { + int currentStyle = context.getLineStyle(); + int currentWidth = context.getLineWidth(); + context.setLineWidth(currentWidth + 2); + context.setLineStyle(context.getLineSolidStyle()); + context.drawLine(x + width - x1.intValue(), y + height - y1.intValue(), x + width, y + height); + context.drawLine(x + width - x2.intValue(), y + height - y2.intValue(), x + width, y + height); + context.setLineStyle(currentStyle); + context.setLineWidth(currentWidth); + } + + // Draw the message label above the message and centered + // The label is truncated if it cannot fit between the two message end + // 2*Metrics.MESSAGES_NAME_SPACING = space above the label + space below the label + context.setForeground(pref.getFontColor(getColorPrefId())); + if (spaceBTWStartEnd > 0) { + context.drawTextTruncatedCentred(getName(), x, y + height / 2 - (2 * Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth()), width, 2 * Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth(), !isSelected()); + } else { + context.drawTextTruncatedCentred(getName(), x + width, y + height / 2 - (2 * Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth()), -width, 2 * Metrics.MESSAGES_NAME_SPACING + +Metrics.getMessageFontHeigth(), !isSelected()); + } + } + } + + @Override + public void draw(IGC context) { + if (!isVisible()) { + return; + } + + // Draw it selected?*/ + if (isSelected()) { + ISDPreferences pref = SDViewPref.getInstance(); + /* + * Draw it twice First time, bigger inverting selection colors Second time, regular drawing using selection + * colors This create the highlight effect + */ + context.setForeground(pref.getBackGroundColorSelection()); + context.setLineWidth(Metrics.SELECTION_LINE_WIDTH); + drawMessage(context); + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + // Second drawing is done after + } + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + if (hasFocus()) { + context.setDrawTextWithFocusStyle(true); + } + drawMessage(context); + int oldStyle = context.getLineStyle(); + if (hasFocus()) { + context.setDrawTextWithFocusStyle(false); + drawFocus(context); + } + // restore the context + context.setLineStyle(oldStyle); + } + + /** + * Determine if two messages are identical. This default implementation considers that overlapping messages with + * same coordinates are identical. + * + * @param message - the message to compare with + * @return true if identical false otherwise + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode#isSameAs(org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode) + */ + @Override + public boolean isSameAs(GraphNode message) { + if (message == null) { + return false; + } + if (!(message instanceof BaseMessage)) { + return super.isSameAs(message); + } + return ((getX() == message.getX()) && (getY() == message.getY()) && (getWidth() == message.getWidth()) && (getHeight() == message.getHeight())); + } + + /** + * Method drawRot. + * + * @param x A x coordinate + * @param y A y coordinate + * @param w A width + * @param h A height + * @param context A graphical context + */ + public void drawRot(int x, int y, int w, int h, IGC context) { + double angleA = Math.atan2(getHeight(), getWidth()); + double cosA = Math.cos(angleA); + double sinA = Math.sin(angleA); + + int gx = getX(); + int gy = getY(); + + int localHeight = h; + localHeight = localHeight / 2; + + double cw = Math.sqrt(w * w + getHeight() * getHeight()); + + int x1 = Math.round((float) ((x - gx) * cosA - (y - gy) * sinA)); + int y1 = Math.round((float) ((x - gx) * sinA + (y - gy) * cosA)); + + int x2 = Math.round((float) (cw * cosA - (y - gy) * sinA)); + int y2 = Math.round((float) (cw * sinA + (y - gy) * cosA)); + + int x3 = Math.round((float) (cw * cosA - (localHeight) * sinA)); + int y3 = Math.round((float) (cw * sinA + (localHeight) * cosA)); + + int x4 = Math.round((float) ((x - gx) * cosA - (localHeight) * sinA)); + int y4 = Math.round((float) ((x - gx) * sinA + (localHeight) * cosA)); + + int[] points = { x1 + getX(), y1 + getY(), x2 + getX(), y2 + getY(), x3 + getX(), y3 + getY(), x4 + getX(), y4 + getY() }; + context.drawPolygon(points); + } + + @Override + public void drawFocus(IGC context) { + + ISDPreferences pref = SDViewPref.getInstance(); + + if ((fStartLifeline != fEndLifeline) && (getStartOccurrence() == getEndOccurrence())) { + context.setLineStyle(context.getLineDotStyle()); + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + context.drawFocus(getX(), getY() - 3, getWidth(), getHeight() + 6); + } else if ((fStartLifeline == fEndLifeline) && (getStartOccurrence() == getEndOccurrence())) { + context.drawFocus(getX(), getY() - 3, getWidth(), Metrics.SYNC_INTERNAL_MESSAGE_HEIGHT + 6); + } else if ((fStartLifeline != fEndLifeline) && (getStartOccurrence() != getEndOccurrence())) { + context.setLineStyle(context.getLineDotStyle()); + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_LIFELINE_HEADER)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_LIFELINE_HEADER)); + drawRot(getX(), getY() - 5, getWidth(), 10, context); + } else { + super.drawFocus(context); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BasicExecutionOccurrence.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BasicExecutionOccurrence.java new file mode 100755 index 0000000000..a1e94c5a8e --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BasicExecutionOccurrence.java @@ -0,0 +1,275 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * BasicExecutionOccurrence is the UML2 execution occurrence graphical representation. It is attached to one Lifeline, + * the event occurrence "duration" along the lifeline is defined by two event occurrences + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline Lifeline for more event occurence details + * @version 1.0 + * @author sveyrier + * + */ +public class BasicExecutionOccurrence extends GraphNode { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The grahNode ID constant + */ + public static final String EXEC_OCC_TAG = "Execution_Occ"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The corresponding lifeline. + */ + private Lifeline fLifeline = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructore + */ + public BasicExecutionOccurrence() { + setColorPrefId(ISDPreferences.PREF_EXEC); + } + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + @Override + public int getX() { + if (fLifeline == null) { + return 0; + } + return fLifeline.getX() + Metrics.getLifelineWidth() / 2 - Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + } + + @Override + public int getY() { + if (fLifeline == null) { + return 0; + } + return fLifeline.getY() + fLifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getStartOccurrence(); + } + + @Override + public int getWidth() { + if (fLifeline == null) { + return 0; + } + return Metrics.EXECUTION_OCCURRENCE_WIDTH; + } + + @Override + public int getHeight() { + if (fLifeline == null) { + return 0; + } + return ((Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing())) * (getEndOccurrence() - getStartOccurrence()); + } + + @Override + public boolean contains(int xValue, int yValue) { + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); + + if (GraphNode.contains(x, y, width, height, xValue, yValue)) { + return true; + } + + if (getNodeAt(xValue, yValue) != null) { + return true; + } + return false; + } + + @Override + public String getName() { + if (super.getName() == null || super.getName().equals("")) { //$NON-NLS-1$ + return fLifeline.getToolTipText(); + } + return super.getName(); + } + + /** + * Set the lifeline on which the execution occurrence appears. + * + * @param theLifeline - the parent lifeline + */ + public void setLifeline(Lifeline theLifeline) { + fLifeline = theLifeline; + } + + /** + * Get the lifeline on which the execution occurrence appears. + * + * @return - the parent lifeline + */ + public Lifeline getLifeline() { + return fLifeline; + } + + /** + * Get the execution start event occurrence + * + * @return the start event occurrence to set + */ + @Override + public int getStartOccurrence() { + return super.getStartOccurrence(); + } + + /** + * Set the execution end event occurrence + * + * @return the end event occurrence to set + */ + @Override + public int getEndOccurrence() { + return super.getEndOccurrence(); + } + + /** + * Set the execution start event occurrence + * + * @param occurrence the start event occurrence to set + */ + @Override + public void setStartOccurrence(int occurrence) { + super.setStartOccurrence(occurrence); + } + + /** + * Set the execution end event occurrence + * + * @param occurrence the end event occurrence to set + */ + @Override + public void setEndOccurrence(int occurrence) { + super.setEndOccurrence(occurrence); + } + + @Override + public void draw(IGC context) { + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); + IColor tempFillColor = null; + IColor tempStrokeColor = null; + + ISDPreferences pref = SDViewPref.getInstance(); + + // The execution occurrence is selected + // if the owning lifeline is selected + if (fLifeline.isSelected() || isSelected()) { + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + } else { + tempFillColor = setUnselectedFillColor(context); + } + if (pref.useGradienColor()) { + context.fillGradientRectangle(x, y, width, height, false); + } else { + context.fillRectangle(x, y, width, height); + } + tempStrokeColor = setUnselectedStrokeColor(context); + context.drawRectangle(x, y, width, height); + if (tempFillColor != null) { + tempFillColor.dispose(); + } + if (tempStrokeColor != null) { + tempStrokeColor.dispose(); + } + if (hasFocus()) { + drawFocus(context); + } + super.drawChildenNodes(context); + } + + /** + * Rewrite this method in your extension in order to support customized fill colors + * + * @param context Graphics context + * @return IColor + */ + protected IColor setUnselectedFillColor(IGC context) { + + ISDPreferences pref = SDViewPref.getInstance(); + + if (pref.useGradienColor()) { + context.setGradientColor(pref.getBackGroundColor(ISDPreferences.PREF_EXEC)); + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_FRAME)); + } else { + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_EXEC)); + } + return null; + } + + /** + * Rewrite this method in your extension in order to support customized stroke colors + * + * @param context Graphics context + * @return IColor + */ + protected IColor setUnselectedStrokeColor(IGC context) { + context.setForeground(SDViewPref.getInstance().getForeGroundColor(ISDPreferences.PREF_EXEC)); + return null; + } + + @Override + public String getArrayId() { + return EXEC_OCC_TAG; + } + + @Override + public boolean positiveDistanceToPoint(int x, int y) { + if (getY() + getHeight() > y) { + return true; + } + return false; + } + + @Override + public boolean isVisible(int x, int y, int width, int height) { + if ((getLifeline() != null) && (getLifeline().isVisible(x, y, width, height))) { + int ly = getY(); + int lh = getHeight(); + if (ly >= y && ly < y + height) { + return true; + } + if (ly + lh > y && ly + lh <= y + height) { + return true; + } + if ((ly < y) && (ly + lh > y + height)) { + return true; + } + } + return false; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BasicFrame.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BasicFrame.java new file mode 100755 index 0000000000..ecea18c6d2 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/BasicFrame.java @@ -0,0 +1,633 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * The Frame class is the base sequence diagram graph nodes container.<br> + * For instance, only one frame can be drawn in the View.<br> + * Lifelines, Messages and Stop which are supposed to represent a Sequence diagram are drawn in a Frame.<br> + * Only the graph node added to their representing list will be drawn. + * + * The lifelines are appended along the X axsis when added in a frame.<br> + * The syncMessages are ordered along the Y axsis depending on the event occurrence they are attached to.<br> + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline Lifeline for more event occurence details + * @author sveyrier + * @version 1.0 + */ +public class BasicFrame extends GraphNode { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * Contains the max elapsed time between two consecutive messages in the whole frame + */ + private ITmfTimestamp fMaxTime = new TmfTimestamp(0); + /** + * Contains the min elapsed time between two consecutive messages in the whole frame + */ + private ITmfTimestamp fMinTime = new TmfTimestamp(0); + /** + * Indicate if the min and max elapsed time between two consecutive messages in the whole frame need to be computed + */ + private boolean fComputeMinMax = true; + /** + * Store the preference set by the user regarding the external time. This flag is used determine if the min and max + * need to be recomputed in case this preference is changed. + */ + private boolean fLastExternalTimePref = SDViewPref.getInstance().excludeExternalTime(); + /** + * The greater event occurrence created on graph nodes drawn in this Frame This directly impact the Frame height + */ + private int fVerticalIndex = 0; + /** + * The index along the x axis where the next lifeline will is drawn This directly impact the Frame width + */ + private int fHorizontalIndex = 0; + /** + * The time information flag. + */ + private boolean fHasTimeInfo = false; + /** + * The current Frame visible area - x coordinates + */ + private int fVisibleAreaX; + /** + * The current Frame visible area - y coordinates + */ + private int fVisibleAreaY; + /** + * The current Frame visible area - width + */ + private int fVisibleAreaWidth; + /** + * The current Frame visible area - height + */ + private int fVisibleAreaHeight; + /** + * The event occurrence spacing (-1 for none) + */ + private int fForceEventOccurrenceSpacing = -1; + /** + * Flag to indicate customized minumum and maximum. + */ + private boolean fCustomMinMax = false; + /** + * The minimum time between messages of the sequence diagram frame. + */ + private ITmfTimestamp fMinSDTime = new TmfTimestamp(); + /** + * The maximum time between messages of the sequence diagram frame. + */ + private ITmfTimestamp fMaxSDTime = new TmfTimestamp(); + /** + * Flag to indicate that initial minimum has to be computed. + */ + private boolean fInitSDMin = true; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Creates an empty frame. + */ + public BasicFrame() { + Metrics.setForcedEventSpacing(fForceEventOccurrenceSpacing); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * + * Returns the greater event occurence known by the Frame + * + * @return the greater event occurrence + */ + protected int getMaxEventOccurrence() { + return fVerticalIndex; + } + + /** + * Set the greater event occurrence created in GraphNodes included in the frame + * + * @param eventOccurrence the new greater event occurrence + */ + protected void setMaxEventOccurrence(int eventOccurrence) { + fVerticalIndex = eventOccurrence; + } + + /** + * This method increase the lifeline place holder The return value is usually assign to a lifeline. This can be used + * to set the lifelines drawing order. Also, calling this method two times and assigning only the last given index + * to a lifeline will increase this lifeline draw spacing (2 times the default spacing) from the last added + * lifeline. + * + * @return a new lifeline index + */ + protected int getNewHorizontalIndex() { + return ++fHorizontalIndex; + } + + /** + * Returns the current horizontal index + * + * @return the current horizontal index + * @see Frame#getNewHorizontalIndex() for horizontal index description + */ + protected int getHorizontalIndex() { + return fHorizontalIndex; + } + + @Override + public void addNode(GraphNode nodeToAdd) { + setComputeMinMax(true); + super.addNode(nodeToAdd); + } + + @Override + public int getX() { + return Metrics.FRAME_H_MARGIN; + } + + @Override + public int getY() { + return Metrics.FRAME_V_MARGIN; + } + + @Override + public int getWidth() { + if (fHorizontalIndex == 0) { + return 3 * Metrics.swimmingLaneWidth() + Metrics.LIFELINE_H_MAGIN * 2 - Metrics.FRAME_H_MARGIN - Metrics.LIFELINE_SPACING / 2; + } + return fHorizontalIndex * Metrics.swimmingLaneWidth() + Metrics.LIFELINE_H_MAGIN * 2 + 1 - Metrics.LIFELINE_SPACING; + } + + @Override + public int getHeight() { + // The Frame height depends on the maximum number of messages added to a lifeline + if (fVerticalIndex == 0) { + return 5 * (Metrics.getMessagesSpacing() + Metrics.getMessageFontHeigth()) + Metrics.LIFELINE_NAME_H_MARGIN + Metrics.FRAME_NAME_H_MARGIN + Metrics.getFrameFontHeigth() + Metrics.LIFELINE_VT_MAGIN + Metrics.LIFELINE_VB_MAGIN + + Metrics.LIFELINE_NAME_H_MARGIN + Metrics.FRAME_NAME_H_MARGIN + Metrics.getLifelineFontHeigth() * 2; + } + if (fForceEventOccurrenceSpacing >= 0) { + Metrics.setForcedEventSpacing(fForceEventOccurrenceSpacing); + } + return fVerticalIndex * (Metrics.getMessagesSpacing() + Metrics.getMessageFontHeigth()) + Metrics.LIFELINE_NAME_H_MARGIN + Metrics.FRAME_NAME_H_MARGIN + Metrics.getFrameFontHeigth() + Metrics.LIFELINE_VT_MAGIN + Metrics.LIFELINE_VB_MAGIN + + Metrics.LIFELINE_NAME_H_MARGIN + Metrics.FRAME_NAME_H_MARGIN + Metrics.getLifelineFontHeigth() * 2; + } + + /** + * @return true if mininum and maximum time needs to be calculated else false + * @since 2.0 + */ + protected boolean isComputeMinMax() { + return fComputeMinMax; + } + + /** + * @return true if mininum and maximum time needs to be calculated else false + * @since 2.0 + */ + protected boolean isCustomMinMax() { + return fCustomMinMax; + } + + /** + * gets the initialization flag for SD minimum. + * + * @return the initialization flag for SD minimum + * @since 2.0 + */ + protected boolean getInitSDMin() { + return fInitSDMin; + } + + /** + * Returns the graph node which contains the point given in parameter for the given graph node list and starting the + * iteration at the given index<br> + * WARNING: Only graph nodes with smaller coordinates than the current visible area can be returned.<br> + * + * @param x the x coordinate of the point to test + * @param y the y coordinate of the point to test + * @param list the list to search in + * @param fromIndex list browsing starting point + * @return the graph node containing the point given in parameter, null otherwise + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode#getNodeFromListAt(int, int, java.util.List, int) + */ + @Override + protected GraphNode getNodeFromListAt(int x, int y, List<GraphNode> list, int fromIndex) { + if (list == null) { + return null; + } + for (int i = fromIndex; i < list.size(); i++) { + GraphNode node = list.get(i); + // only lifeline list is x ordered + // Stop browsing the list if the node is outside the visible area + // all others nodes will be not visible + if ((node instanceof Lifeline) && (node.getX() > fVisibleAreaX + fVisibleAreaWidth)) { + break; + } + if (node.getHeight() < 0) { + if (node.getY() + node.getHeight() > fVisibleAreaY + fVisibleAreaHeight) { + break; + } + } else { + if (node.getY() > fVisibleAreaY + fVisibleAreaHeight) { + break; + } + } + if (node.contains(x, y)) { + return node; + } + } + return null; + } + + /** + * Draw the Frame rectangle + * + * @param context the context to draw to + */ + protected void drawFrame(IGC context) { + + ISDPreferences pref = SDViewPref.getInstance(); + + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_FRAME)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_FRAME)); + + int x = getX(); + int y = getY(); + int w = getWidth(); + int h = getHeight(); + + // Draw the frame main rectangle + context.fillRectangle(x, y, w, h); + context.drawRectangle(x, y, w, h); + + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_FRAME_NAME)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_FRAME_NAME)); + context.setFont(pref.getFont(ISDPreferences.PREF_FRAME_NAME)); + + int nameWidth = context.textExtent(getName()) + 2 * Metrics.FRAME_NAME_V_MARGIN; + int nameHeight = Metrics.getFrameFontHeigth() + +Metrics.FRAME_NAME_H_MARGIN * 2; + + // Draw the frame name area + if (nameWidth > w) { + nameWidth = w; + } + + int[] points = { x, y, x + nameWidth, y, x + nameWidth, y - 11 + nameHeight, x - 11 + nameWidth, y + nameHeight, x, y + nameHeight, x, y + nameHeight }; + context.fillPolygon(points); + context.drawPolygon(points); + context.drawLine(x, y, x, y + nameHeight); + + context.setForeground(pref.getFontColor(ISDPreferences.PREF_FRAME_NAME)); + context.drawTextTruncatedCentred(getName(), x, y, nameWidth - 11, nameHeight, false); + + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_FRAME)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_FRAME)); + } + + @Override + public void draw(IGC context) { + draw(context, true); + } + + /** + * Draws the Frame on the given context.<br> + * This method start width GraphNodes ordering if needed.<br> + * After, depending on the visible area, only visible GraphNodes are drawn.<br> + * + * @param context the context to draw to + * @param drawFrame indicate if the frame rectangle need to be redrawn + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode#draw(IGC) + */ + protected void draw(IGC context, boolean drawFrame) { + fVisibleAreaHeight = context.getVisibleHeight(); + fVisibleAreaWidth = context.getVisibleWidth(); + fVisibleAreaX = context.getContentsX(); + fVisibleAreaY = context.getContentsY(); + + if (fForceEventOccurrenceSpacing >= 0) { + Metrics.setForcedEventSpacing(fForceEventOccurrenceSpacing); + } else { + Metrics.setForcedEventSpacing(-1); + } + + super.drawChildenNodes(context); + } + + /** + * Sets the event occurrence spacing (-1 for none) + * + * @param space A spacing to set. + */ + public void forceEventOccurrenceSpacing(int space) { + fForceEventOccurrenceSpacing = space; + } + + /** + * Return the X coordinates of the frame visible area + * + * @return the X coordinates of the frame visible area + */ + public int getVisibleAreaX() { + return fVisibleAreaX; + } + + /** + * Return the frame visible area width + * + * @return the frame visible area width + */ + public int getVisibleAreaWidth() { + return fVisibleAreaWidth; + } + + /** + * Return the frame visible area height + * + * @return the frame visible area height + */ + public int getVisibleAreaHeight() { + return fVisibleAreaHeight; + } + + /** + * Return the X coordinates of the frame visible area + * + * @return the X coordinates of the frame visible area + */ + public int getVisibleAreaY() { + return fVisibleAreaY; + } + + /** + * Return the minimum time stored in the frame taking all GraphNodes into account + * + * @return the minimum GraphNode time + * @since 2.0 + */ + public ITmfTimestamp getMinTime() { + if (fLastExternalTimePref != SDViewPref.getInstance().excludeExternalTime()) { + fLastExternalTimePref = SDViewPref.getInstance().excludeExternalTime(); + setComputeMinMax(true); + } + if ((fComputeMinMax) && (!fCustomMinMax)) { + computeMinMax(); + setComputeMinMax(false); + } + return fMinTime; + } + + /** + * Set the minimum timestamp of the frame. + * + * @param min + * The minimum timestamp + * @since 2.0 + */ + public void setMin(ITmfTimestamp min) { + fMinTime = min; + fCustomMinMax = true; + } + + /** + * Set the maximum timestamp of the frame. + * + * @param max + * The maximum timestamp + * @since 2.0 + */ + public void setMax(ITmfTimestamp max) { + fMaxTime = max; + fCustomMinMax = true; + } + + /** + * Reset min/max timestamp values to the default ones. + */ + public void resetCustomMinMax() { + fCustomMinMax = false; + setComputeMinMax(true); + } + + /** + * Return the maximum time stored in the frame taking all GraphNodes into account + * + * @return the maximum GraphNode time + * @since 2.0 + */ + public ITmfTimestamp getMaxTime() { + if (fLastExternalTimePref != SDViewPref.getInstance().excludeExternalTime()) { + fLastExternalTimePref = SDViewPref.getInstance().excludeExternalTime(); + setComputeMinMax(true); + } + if (fComputeMinMax) { + computeMinMax(); + setComputeMinMax(false); + } + return fMaxTime; + } + + /** + * Computes the minimum and maximum time between consecutive messages within the frame. + */ + protected void computeMaxMinTime() { + if (!fInitSDMin) { + return; + } + + List<SDTimeEvent> timeArray = buildTimeArray(); + + if ((timeArray == null) || timeArray.isEmpty()) { + return; + } + for (int i = 0; i < timeArray.size(); i++) { + SDTimeEvent m = timeArray.get(i); + + if (m.getTime().compareTo(fMaxSDTime, true) > 0) { + fMaxSDTime = m.getTime(); + } + + if ((m.getTime().compareTo(fMinSDTime, true) < 0) || fInitSDMin) { + fMinSDTime = m.getTime(); + fInitSDMin = false; + } + } + } + + /** + * Returns the minimum time between consecutive messages. + * + * @return the minimum time between consecutive messages + * @since 2.0 + */ + public ITmfTimestamp getSDMinTime() { + computeMaxMinTime(); + return fMinSDTime; + } + + /** + * Returns the maximum time between consecutive messages. + * + * @return the maximum time between consecutive messages + * @since 2.0 + */ + public ITmfTimestamp getSDMaxTime() { + computeMaxMinTime(); + return fMaxSDTime; + } + + /** + * Browse all the GraphNode to compute the min and max times store in the Frame + */ + protected void computeMinMax() { + List<SDTimeEvent> timeArray = buildTimeArray(); + + if ((timeArray == null) || timeArray.isEmpty()) { + return; + } + for (int i = 0; i < timeArray.size() - 1; i++) { + SDTimeEvent m1 = timeArray.get(i); + SDTimeEvent m2 = timeArray.get(i + 1); + + updateMinMax(m1, m2); + } + } + + /** + * Updates the minimum and maximum time between consecutive message within the frame based on the given values. + * + * @param m1 A first SD time event. + * @param m2 A second SD time event. + */ + protected void updateMinMax(SDTimeEvent m1, SDTimeEvent m2) { + ITmfTimestamp delta = m2.getTime().getDelta(m1.getTime()); + if (fComputeMinMax) { + fMinTime = delta; + if (fMinTime.compareTo(TmfTimestamp.ZERO, false) < 0) { + fMinTime = new TmfTimestamp(0, m1.getTime().getScale(), m1.getTime().getPrecision()); + } + fMaxTime = fMinTime; + setComputeMinMax(false); + } + + if ((delta.compareTo(fMinTime, true) < 0) && (delta.compareTo(TmfTimestamp.ZERO, false) > 0)) { + fMinTime = delta; + } + + if ((delta.compareTo(fMaxTime, true) > 0) && (delta.compareTo(TmfTimestamp.ZERO, false) > 0)) { + fMaxTime = delta; + } + } + + /** + * Builds the time array based on the list of graph nodes. + * + * @return the time array else empty list. + */ + protected List<SDTimeEvent> buildTimeArray() { + if (!hasChildren()) { + return new ArrayList<>(); + } + + Iterator<String> it = getForwardSortMap().keySet().iterator(); + List<SDTimeEvent> timeArray = new ArrayList<>(); + while (it.hasNext()) { + String nodeType = it.next(); + List<GraphNode> list = getNodeMap().get(nodeType); + for (int i = 0; i < list.size(); i++) { + Object timedNode = list.get(i); + if ((timedNode instanceof ITimeRange) && ((ITimeRange) timedNode).hasTimeInfo()) { + int event = list.get(i).getStartOccurrence(); + ITmfTimestamp time = ((ITimeRange) list.get(i)).getStartTime(); + SDTimeEvent f = new SDTimeEvent(time, event, (ITimeRange) list.get(i)); + timeArray.add(f); + if (event != list.get(i).getEndOccurrence()) { + event = (list.get(i)).getEndOccurrence(); + time = ((ITimeRange) list.get(i)).getEndTime(); + f = new SDTimeEvent(time, event, (ITimeRange) list.get(i)); + timeArray.add(f); + } + } + } + } + return timeArray; + } + + @Override + public String getArrayId() { + return null; + } + + @Override + public boolean contains(int x, int y) { + return false; + } + + /** + * @return true if frame has time info else false + * @since 2.0 + */ + public boolean hasTimeInfo() { + return fHasTimeInfo; + } + + /** + * Sets the flag whether the frame has time info or not + * @since 2.0 + * @param hasTimeInfo + * true if frame has time info else false + */ + public void setHasTimeInfo(boolean hasTimeInfo) { + fHasTimeInfo = hasTimeInfo; + } + + /** + * Sets the flag for minimum and maximum computation. + * @param computeMinMax + * true if mininum and maximum time needs to be calculated else false + * @since 2.0 + */ + public void setComputeMinMax(boolean computeMinMax) { + fComputeMinMax = computeMinMax; + } + + /** + * Sets the initialization flag for SD minimum. + * + * @param initSDMin + * the flag to set + * @since 2.0 + */ + public void setInitSDMin(boolean initSDMin) { + fInitSDMin = initSDMin; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/EllipsisMessage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/EllipsisMessage.java new file mode 100755 index 0000000000..4291ddcca0 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/EllipsisMessage.java @@ -0,0 +1,141 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * Class to draw Ellipsis Message. + * + * @version 1.0 + * @author sveyrier + * + */ +public class EllipsisMessage extends AsyncMessage { + + @Override + public int getX() { + if (getStartLifeline() == null) { + return super.getX() + super.getWidth() - 16; + } + return super.getX(); + } + + @Override + public int getY() { + return super.getY() + 3; + } + + @Override + public int getWidth() { + return 16; + } + + @Override + protected void drawMessage(IGC context) { + // temporary store the coordinates to avoid more methods calls + int x = super.getX(); + int y = getY(); + int width = super.getWidth(); + int height = getHeight(); + + // UML2 found message (always drawn from left to right) + if (getStartLifeline() == null && getEndLifeline() != null) { + // Draw the message label above the message and centered + // The label is truncated if it cannot fit between the two message end + // 2*Metrics.MESSAGES_NAME_SPACING = space above the label + space below the label + context.drawTextTruncatedCentred(getName(), x, y - Metrics.getMessageFontHeigth() - 2 * Metrics.MESSAGES_NAME_SPACING, width, 2 * Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth(), !isSelected()); + + int currentStyle = context.getLineStyle(); + context.setLineStyle(context.getLineSolidStyle()); + // Draw the message main line + context.drawRectangle(x + width - 5, y, x + width - 6, y + height); + context.drawRectangle(x + width - 10, y, x + width - 11, y + height); + context.drawRectangle(x + width - 15, y, x + width - 16, y + height); + context.setLineStyle(currentStyle); + + IColor storedColor = context.getBackground(); + context.setBackground(context.getForeground()); + context.fillRectangle(x + width - 5, y, x + width - 6, y + height); + context.fillRectangle(x + width - 10, y, x + width - 11, y + height); + context.fillRectangle(x + width - 15, y, x + width - 16, y + height); + context.setBackground(storedColor); + } + // UML2 lost message (always drawn from left to right) + else if (getEndLifeline() == null && getStartLifeline() != null) { + // Draw the message label above the message and centered + // The label is truncated if it cannot fit between the two message end + // 2*Metrics.MESSAGES_NAME_SPACING = space above the label + space below the label + context.drawTextTruncatedCentred(getName(), x, y - Metrics.getMessageFontHeigth() - 2 * Metrics.MESSAGES_NAME_SPACING, width, 2 * Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth(), !isSelected()); + + int currentStyle = context.getLineStyle(); + context.setLineStyle(context.getLineSolidStyle()); + // Draw the message main line + context.drawRectangle(x + 5, y, 1, 1); + context.drawRectangle(x + 10, y, 1, 1); + context.drawRectangle(x + 15, y, 1, 1); + + context.setLineStyle(currentStyle); + + IColor storedColor = context.getBackground(); + context.setBackground(context.getForeground()); + context.fillRectangle(x + 5, y, 1, 1); + context.fillRectangle(x + 10, y, 1, 1); + context.fillRectangle(x + 15, y, 1, 1); + + context.setBackground(storedColor); + + } else { + super.draw(context); + } + } + + @Override + public void draw(IGC context) { + if (!isVisible()) { + return; + } + + ISDPreferences pref = SDViewPref.getInstance(); + + // Draw it selected?*/ + if (isSelected()) { + + /* + * Draw it twice First time, bigger inverting selection colors Second time, regular drawing using selection + * colors This create the highlight effect + */ + context.setForeground(pref.getBackGroundColorSelection()); + context.setLineWidth(Metrics.SELECTION_LINE_WIDTH); + drawMessage(context); + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + // Second drawing is done after the else + } else { + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_ASYNC_MESS)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_ASYNC_MESS)); + } + if (hasFocus()) { + context.setDrawTextWithFocusStyle(true); + } + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + drawMessage(context); + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + if (hasFocus()) { + context.setDrawTextWithFocusStyle(false); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/ExecutionOccurrence.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/ExecutionOccurrence.java new file mode 100755 index 0000000000..df27ae6b1d --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/ExecutionOccurrence.java @@ -0,0 +1,267 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import java.util.Arrays; + +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IImage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * ExecutionOccurrence is the UML2 execution occurrence graphical representation. It is a BasicExecutionOccurrence on + * which you can customize fill and/or. + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline Lifeline for more event occurence details + * @version 1.0 + * @author sveyrier + * + */ +public class ExecutionOccurrence extends BasicExecutionOccurrence implements ITimeRange { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * Set the red, green and blue value of the optional color to be used for filling the execution occurrence. + */ + private int[] fFillRGB; + /** + * Set the red, green and blue value of the optional color to be used for drawing the execution occurrence + */ + private int[] fStrokeRGB; + /** + * The occurrence image. + */ + private IImage fImage; + /** + * The top ellipses image. + */ + private IImage fEllipsesImage; + /** + * The start time stamp. + */ + private ITmfTimestamp fStartTime; + /** + * The end time stamp; + */ + private ITmfTimestamp fEndTime; + /** + * Flag to indicate whether time information is available or not. + */ + private boolean fHasTimeInfo; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void setLifeline(Lifeline theLifeline) { + super.setLifeline(theLifeline); + if (getLifeline() != null && fHasTimeInfo) { + getLifeline().setTimeInfo(true); + if (getLifeline().getFrame() != null) { + getLifeline().getFrame().setHasTimeInfo(true); + } + } + } + + /** + * Set the red, green and blue value of the optional color to be used for filling the execution occurrence. + * + * @param red A value for red. + * @param green A green value for green. + * @param blue A value blue. + */ + public void setFillColor(int red, int green, int blue) { + fFillRGB = new int[3]; + fFillRGB[0] = red; + fFillRGB[1] = green; + fFillRGB[2] = blue; + } + + /** + * Set the red, green and blue value of the optional color to be used for drawing the execution occurrence + * + * @param red A value for red. + * @param green A green value for green. + * @param blue A value blue. + */ + public void setStrokeColor(int red, int green, int blue) { + fStrokeRGB = new int[3]; + fStrokeRGB[0] = red; + fStrokeRGB[1] = green; + fStrokeRGB[2] = blue; + } + + /** + * Set the corresponding image. + * + * @param image A image to set. + */ + public void setImage(IImage image) { + fImage = image; + } + + /** + * Set the top ellipses image. + * + * @param image A image to set. + */ + public void setTopEllipsesImage(IImage image) { + fEllipsesImage = image; + } + + /** + * Set the time when the execution occurrence starts. + * + * @param time the time when the execution occurrence starts + * @since 2.0 + */ + public void setStartTime(ITmfTimestamp time) { + fStartTime = time; + fHasTimeInfo = true; + if (getLifeline() != null) { + getLifeline().setTimeInfo(true); + } + } + + /** + * Set the time when the execution occurrence ends. + * + * @param time the time when the execution occurrence ends + * @since 2.0 + */ + public void setEndTime(ITmfTimestamp time) { + fEndTime = time; + fHasTimeInfo = true; + if (getLifeline() != null) { + getLifeline().setTimeInfo(true); + } + } + + /** + * @since 2.0 + */ + @Override + public ITmfTimestamp getStartTime() { + return fStartTime; + } + + /** + * @since 2.0 + */ + @Override + public ITmfTimestamp getEndTime() { + return fEndTime; + } + + @Override + public boolean hasTimeInfo() { + return fHasTimeInfo; + } + + /** + * @return the RGB of the occurrence filler. + * @since 2.0 + */ + public int[] getFillRGB() { + if (fFillRGB == null) { + return null; + } + return Arrays.copyOf(fFillRGB, fFillRGB.length); + } + + /** + * @return the RGB of the occurrence filler. + * @since 2.0 + */ + public int[] getStrokeRGB() { + if (fStrokeRGB == null) { + return null; + } + return Arrays.copyOf(fStrokeRGB, fStrokeRGB.length); + } + + /** + * @return the image. + * @since 2.0 + */ + protected IImage getImage() { + return fImage; + } + + /** + * @return the image. + * @since 2.0 + */ + protected IImage getEllipsesImage() { + return fEllipsesImage; + } + + @Override + public void draw(IGC context) { + super.draw(context); + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); + if (fImage != null) { + context.drawImage(fImage, x + width - 4, y + height - 11, 8, 11); + } + if (fEllipsesImage != null) { + context.drawImage(fEllipsesImage, x + width, y, 40, 10); + } + } + + @Override + protected IColor setUnselectedFillColor(IGC context) { + ISDPreferences pref = SDViewPref.getInstance(); + if (fFillRGB != null) { + IColor tempFillColor = context.createColor(fFillRGB[0], fFillRGB[1], fFillRGB[2]); + if (pref.useGradienColor()) { + context.setGradientColor(tempFillColor); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_EXEC)); + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_FRAME)); + } else { + context.setBackground(tempFillColor); + } + return tempFillColor; + } + return super.setUnselectedFillColor(context); + } + + @Override + protected IColor setUnselectedStrokeColor(IGC context) { + if (fStrokeRGB != null) { + IColor tempStrokeColor = context.createColor(fStrokeRGB[0], fStrokeRGB[1], fStrokeRGB[2]); + context.setForeground(tempStrokeColor); + return tempStrokeColor; + } + return super.setUnselectedStrokeColor(context); + } + + /** + * Sets the flag whether the frame has time info or not + * @since 2.0 + * @param hasTimeInfo + * true if frame has time info else false + */ + public void setHasTimeInfo(boolean hasTimeInfo) { + fHasTimeInfo = hasTimeInfo; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Frame.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Frame.java new file mode 100755 index 0000000000..d5dcf6f3da --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Frame.java @@ -0,0 +1,1215 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.TimeEventComparator; + +/** + * The Frame class is the base sequence diagram graph nodes container.<br> + * For instance, only one frame can be drawn in the View.<br> + * Lifelines, Messages and Stop which are supposed to represent a Sequence diagram are drawn in a Frame.<br> + * Only the graph node added to their representing list will be drawn. + * + * The lifelines are appended along the X axsis when added in a frame.<br> + * The syncMessages are ordered along the Y axsis depending on the event occurrence they are attached to.<br> + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline Lifeline for more event occurence details + * @author sveyrier + * @version 1.0 + */ +public class Frame extends BasicFrame { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The lifeline that is current highlighted. + */ + private Lifeline fHighlightLifeline = null; + /** + * The value of the start event. + */ + private int fStartEvent = 0; + /** + * The number of events in the frame. + */ + private int fNbEvent = 0; + /** + * The color for highlighting. + */ + private IColor fHighlightColor = null; + /** + * The list of time events of the corresponding execution occurrences. + */ + private List<SDTimeEvent> fExecutionOccurrencesWithTime; + /** + * The Array of lifeline categories. + */ + private LifelineCategories[] fLifelineCategories = null; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Returns a list of all lifelines known by this frame. Known lifelines are the only one which can be displayed on + * screen. + * + * @return the lifelines list + */ + protected List<GraphNode> getLifelines() { + if (!hasChildren()) { + return null; + } + return getNodeMap().get(Lifeline.LIFELINE_TAG); + } + + /** + * Returns the number of lifelines stored in the frame + * + * @return the number of lifelines + */ + public int lifeLinesCount() { + List<GraphNode> lifelines = getLifelines(); + if (lifelines != null) { + return lifelines.size(); + } + return 0; + } + + /** + * Returns the lifeline at the given index in the lifelines array + * + * @param index the position in the lifeline array + * @return the lifeline or <code>null</code> + */ + public Lifeline getLifeline(int index) { + if ((getLifelines() != null) && (index >= 0) && (index < lifeLinesCount())) { + return (Lifeline) getLifelines().get(index); + } + return null; + } + + /** + * Returns a list of syncMessages known by this frame. Known syncMessages are the only on which can be displayed on + * screen + * + * @return the syncMessages list + */ + protected List<GraphNode> getSyncMessages() { + if (!hasChildren()) { + return null; + } + return getNodeMap().get(SyncMessage.SYNC_MESS_TAG); + } + + /** + * Returns the number of syncMessages stored in the frame + * + * @return the number of syncMessage + */ + public int syncMessageCount() { + if (getSyncMessages() != null) { + return getSyncMessages().size(); + } + return 0; + } + + /** + * Returns the syncMessage at the given index in the syncMessages array + * + * @param index the position in the syncMessages array + * @return the syncMessage or <code>null</code> + */ + public SyncMessage getSyncMessage(int index) { + if ((getSyncMessages() != null) && (index >= 0) && (index < getSyncMessages().size())) { + return (SyncMessage) getSyncMessages().get(index); + } + return null; + } + + /** + * Returns a list of asyncMessages known by this frame. Known asyncMessages are the only on which can be displayed + * on screen + * + * @return the asyncMessages list or <code>null</code> + */ + protected List<GraphNode> getAsyncMessages() { + if (!hasChildren()) { + return null; + } + return getNodeMap().get(AsyncMessage.ASYNC_MESS_TAG); + } + + /** + * Returns the number of asyncMessage stored in the frame + * + * @return the number of asyncMessage + */ + public int asyncMessageCount() { + if (getAsyncMessages() != null) { + return getAsyncMessages().size(); + } + return 0; + } + + /** + * Returns the asyncMessage at the given index in the asyncMessage array + * + * @param index the position in the asyncMessage array + * @return the asyncMessage or <code>null</code> + */ + public AsyncMessage getAsyncMessage(int index) { + if ((getAsyncMessages() != null) && (index >= 0) && (index < getAsyncMessages().size())) { + return (AsyncMessage) getAsyncMessages().get(index); + } + return null; + } + + /** + * Returns a list of syncMessages return known by this frame. Known syncMessages return are the only on which can be + * displayed on screen + * + * @return the syncMessages return list or <code>null</code> + */ + protected List<GraphNode> getSyncMessagesReturn() { + if (!hasChildren()) { + return null; + } + return getNodeMap().get(SyncMessageReturn.SYNC_MESS_RET_TAG); + } + + /** + * Returns the number of syncMessageReturn stored in the frame + * + * @return the number of syncMessageReturn + */ + public int syncMessageReturnCount() { + if (getSyncMessagesReturn() != null) { + return getSyncMessagesReturn().size(); + } + return 0; + } + + /** + * Returns the syncMessageReturn at the given index in the syncMessageReturn array + * + * @param index the position in the syncMessageReturn array + * @return the syncMessageReturn or <code>null</code> + */ + public SyncMessageReturn getSyncMessageReturn(int index) { + if ((getSyncMessagesReturn() != null) && (index >= 0) && (index < getSyncMessagesReturn().size())) { + return (SyncMessageReturn) getSyncMessagesReturn().get(index); + } + return null; + } + + /** + * Returns a list of asyncMessageRetun known by this frame. Known asyncMessageRetun are the only on which can be + * displayed on screen + * + * @return the asyncMessageRetun list or <code>null</code> + */ + protected List<GraphNode> getAsyncMessagesReturn() { + if (!hasChildren()) { + return null; + } + return getNodeMap().get(AsyncMessageReturn.ASYNC_MESS_RET_TAG); + } + + /** + * Returns the number of asyncMessageReturn stored in the frame + * + * @return the number of asyncMessageReturn + */ + public int asyncMessageReturnCount() { + if (getAsyncMessagesReturn() != null) { + return getAsyncMessagesReturn().size(); + } + return 0; + } + + /** + * Returns the asyncMessageReturn at the given index in the asyncMessageReturn array + * + * @param index the position in the asyncMessageReturn array + * @return the asyncMessageReturn or <code>null</code> + */ + public AsyncMessageReturn getAsyncMessageReturn(int index) { + if ((getAsyncMessagesReturn() != null) && (index >= 0) && (index < getAsyncMessagesReturn().size())) { + return (AsyncMessageReturn) getAsyncMessagesReturn().get(index); + } + return null; + } + + /** + * Adds a lifeline to the frame lifelines list. The lifeline X drawing order depends on the lifeline addition order + * into the frame lifelines list. + * + * @param lifeline the lifeline to add + */ + public void addLifeLine(Lifeline lifeline) { + setComputeMinMax(true); + if (lifeline == null) { + return; + } + // set the lifeline parent frame + lifeline.setFrame(this); + // Increate the frame lifeline counter + // and set the lifeline drawing order + lifeline.setIndex(getNewHorizontalIndex()); + if (lifeline.hasTimeInfo()) { + setHasTimeInfo(true); + } + // add the lifeline to the lifelines list + addNode(lifeline); + } + + /** + * Returns the first visible lifeline drawn in the view + * + * @return the first visible lifeline index + */ + public int getFirstVisibleLifeline() { + if (!hasChildren()) { + return 0; + } else if (getIndexes().get(Lifeline.LIFELINE_TAG) != null) { + return getIndexes().get(Lifeline.LIFELINE_TAG).intValue(); + } + return 0; + } + + /** + * Returns the first visible synchronous message drawn in the view + * + * @return the first visible synchronous message index + */ + public int getFirstVisibleSyncMessage() { + if (!hasChildren()) { + return 0; + } else if (getIndexes().get(SyncMessage.SYNC_MESS_TAG) != null) { + return getIndexes().get(SyncMessage.SYNC_MESS_TAG).intValue(); + } + return 0; + } + + /** + * Returns the first visible synchronous message return drawn in the view + * + * @return the first visible synchronous message return index + */ + public int getFirstVisibleSyncMessageReturn() { + if (!hasChildren()) { + return 0; + } else if (getIndexes().get(SyncMessageReturn.SYNC_MESS_RET_TAG) != null) { + return getIndexes().get(SyncMessageReturn.SYNC_MESS_RET_TAG).intValue(); + } + return 0; + } + + /** + * Returns the first visible synchronous message drawn in the view + * + * @return the first visible synchronous message index + */ + public int getFirstVisibleAsyncMessage() { + if (!hasChildren()) { + return 0; + } else if (getIndexes().get(AsyncMessage.ASYNC_MESS_TAG) != null) { + return getIndexes().get(AsyncMessage.ASYNC_MESS_TAG).intValue(); + } + return 0; + } + + /** + * Returns the first visible synchronous message return drawn in the view + * + * @return the first visible synchronous message return index + */ + public int getFirstVisibleAsyncMessageReturn() { + if (!hasChildren()) { + return 0; + } else if (getIndexes().get(AsyncMessageReturn.ASYNC_MESS_RET_TAG) != null) { + return getIndexes().get(AsyncMessageReturn.ASYNC_MESS_RET_TAG).intValue(); + } + return 0; + } + + /** + * Returns the list of execution occurrences. + * + * @return the list of execution occurrences + */ + public List<SDTimeEvent> getExecutionOccurrencesWithTime() { + return fExecutionOccurrencesWithTime; + } + + /** + * Inserts a lifeline after a given lifeline. + * + * @param toInsert A lifeline to insert + * @param after A lifelife the toInsert-lifeline will be inserted after. + */ + public void insertLifelineAfter(Lifeline toInsert, Lifeline after) { + if ((toInsert == null)) { + return; + } + if (toInsert == after) { + return; + } + int insertPoint = 0; + if (after != null) { + insertPoint = after.getIndex(); + } + int removePoint = toInsert.getIndex() - 1; + if (removePoint >= insertPoint) { + getLifelines().remove(removePoint); + } + getLifelines().add(insertPoint, toInsert); + if (removePoint < insertPoint) { + getLifelines().remove(removePoint); + } + + if (removePoint >= insertPoint) { + toInsert.setIndex(insertPoint + 1); + } else { + toInsert.setIndex(insertPoint - 1); + } + + insertPoint++; + if (removePoint >= insertPoint) { + for (int i = insertPoint; i < getLifelines().size(); i++) { + getLifeline(i).setIndex(i + 1); + } + } else { + for (int i = 0; i < insertPoint && i < getLifelines().size(); i++) { + getLifeline(i).setIndex(i + 1); + } + } + } + + /** + * Inserts a lifeline before a given lifeline. + * + * @param toInsert + * A lifeline to insert + * @param before + * A lifeline the toInsert-lifeline will be inserted before. + */ + public void insertLifelineBefore(Lifeline toInsert, Lifeline before) { + if ((toInsert == null)) { + return; + } + if (toInsert == before) { + return; + } + int insertPoint = 0; + if (before != null) { + insertPoint = before.getIndex() - 1; + } + int removePoint = toInsert.getIndex() - 1; + if (removePoint >= insertPoint) { + getLifelines().remove(removePoint); + } + getLifelines().add(insertPoint, toInsert); + if (removePoint < insertPoint) { + getLifelines().remove(removePoint); + } + + if (removePoint >= insertPoint) { + toInsert.setIndex(insertPoint + 1); + } else { + toInsert.setIndex(insertPoint - 1); + } + + insertPoint++; + if (removePoint >= insertPoint) { + for (int i = insertPoint; i < getLifelines().size(); i++) { + getLifeline(i).setIndex(i + 1); + } + } else { + for (int i = 0; i < insertPoint && i < getLifelines().size(); i++) { + getLifeline(i).setIndex(i + 1); + } + } + } + + /** + * Gets the closer life line to the given x-coordinate. + * + * @param x A x coordinate + * @return the closer lifeline + */ + public Lifeline getCloserLifeline(int x) { + int index = (x - Metrics.FRAME_H_MARGIN + Metrics.LIFELINE_H_MAGIN) / Metrics.swimmingLaneWidth() - 1; + if (index < 0) { + index = 0; + } + if (index >= getLifelines().size()) { + index = getLifelines().size() - 1; + } + Lifeline node1, node2, node3; + int dist1, dist2, dist3; + node1 = node2 = node3 = getLifeline(index); + dist1 = dist2 = dist3 = Math.abs(node1.getX() + node1.getWidth() / 2 - x); + if (index > 0) { + node2 = getLifeline(index - 1); + dist2 = Math.abs(node2.getX() + node2.getWidth() / 2 - x); + } + if (index < getLifelines().size() - 1) { + node3 = getLifeline(index + 1); + dist3 = Math.abs(node3.getX() + node3.getWidth() / 2 - x); + } + if (dist1 <= dist2 && dist1 <= dist3) { + return node1; + } else if (dist2 <= dist1 && dist2 <= dist3) { + return node2; + } + return node3; + } + + /** + * Re-orders the given list of lifelines. + * + * @param list A list of lifelines to reorder. + */ + public void reorder(List<?> list) { + for (int i = 0; i < list.size(); i++) { + if (list.get(i) instanceof Lifeline[]) { + Lifeline temp[] = (Lifeline[]) list.get(i); + if (temp.length == 2) { + if (temp[1] == null) { + insertLifelineAfter(temp[0], getLifeline(lifeLinesCount() - 1)); + } else { + insertLifelineBefore(temp[0], temp[1]); + } + } + } + } + } + + /** + * Resets the time compression information. + */ + public void resetTimeCompression() { + fHighlightLifeline = null; + this.fStartEvent = 0; + this.fNbEvent = 0; + fHighlightColor = null; + } + + @Override + protected void computeMinMax() { + List<SDTimeEvent> timeArray = buildTimeArray(); + if ((timeArray == null) || timeArray.isEmpty()) { + return; + } + for (int i = 0; i < timeArray.size() - 1; i++) { + SDTimeEvent m1 = timeArray.get(i); + SDTimeEvent m2 = timeArray.get(i + 1); + if (SDViewPref.getInstance().excludeExternalTime() && ((m1.getGraphNode() instanceof BaseMessage) && (m2.getGraphNode() instanceof BaseMessage))) { + BaseMessage mes1 = (BaseMessage) m1.getGraphNode(); + BaseMessage mes2 = (BaseMessage) m2.getGraphNode(); + if ((mes2.getStartLifeline() == null) || (mes1.getEndLifeline() == null)) { + continue; + } + } + + updateMinMax(m1, m2); + } + } + + /** + * Find the two graph nodes that are closest to this date, one just earlier, second just later. If date is before + * any graph node, bounds[0] is null and bounds[1] is the earliest. If date is after any graph node, bounds[1] is + * null and bounds[0] is the latest. + * + * @param dateToFind date to be found + * @param bounds a two items array that will receive bounds if found + * @return true if both bounds not null + * @since 2.0 + */ + public boolean findDateBounds(ITmfTimestamp dateToFind, ITimeRange bounds[]) { + if (hasTimeInfo()) { + List<SDTimeEvent> timeArray = buildTimeArray(); + + if ((timeArray == null) || timeArray.isEmpty()) { + return false; + } + + bounds[0] = null; + bounds[1] = null; + for (int i = 0; i < timeArray.size(); i++) { + SDTimeEvent m = timeArray.get(i); + if (m.getTime().compareTo(dateToFind, true) > 0) { + bounds[1] = m.getGraphNode(); + if (i > 0) { + bounds[0] = timeArray.get(i - 1).getGraphNode(); + return true; + } + return false; + } + } + bounds[0] = timeArray.get(timeArray.size() - 1).getGraphNode(); + } + return false; + } + + /** + * Highlights the time compression. + * + * @param lifeline A lifeline to highlight + * @param startEvent A start event number + * @param nbEvent A number of events + * @param color A color for highlighting + */ + public void highlightTimeCompression(Lifeline lifeline, int startEvent, int nbEvent, IColor color) { + fHighlightLifeline = lifeline; + this.fStartEvent = startEvent; + this.fNbEvent = nbEvent; + fHighlightColor = color; + } + + /** + * Set the lifeline categories which will be use during the lifelines creation + * + * @see Lifeline#setCategory(int) + * @param categories the lifeline categories array + */ + public void setLifelineCategories(LifelineCategories[] categories) { + fLifelineCategories = Arrays.copyOf(categories, categories.length); + } + + /** + * Returns the lifeline categories array set for the this frame + * + * @return the lifeline categories array or null if not set + */ + public LifelineCategories[] getLifelineCategories() { + return Arrays.copyOf(fLifelineCategories, fLifelineCategories.length); + } + + /** + * Adds a message to the Frame message list. Four kinds of syncMessages can be added:<br> + * - synchronous syncMessages<br> + * - synchronous syncMessages return<br> + * - asynchronous syncMessages<br> + * - asynchronous syncMessages return<br> + * For drawing performance reason, it is recommended to add synchronous syncMessages in the same order they should + * appear along the Y axis in the Frame. + * + * @param message the message to add + */ + public void addMessage(BaseMessage message) { + addNode(message); + } + + @Override + public void draw(IGC context) { + drawFrame(context); + if (!hasChildren()) { + return; + } + + if (fHighlightLifeline != null) { + IColor backupColor = context.getBackground(); + context.setBackground(SDViewPref.getInstance().getTimeCompressionSelectionColor()); + int gy = fHighlightLifeline.getY() + fHighlightLifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * fStartEvent; + context.fillRectangle(Metrics.FRAME_H_MARGIN + 1, gy, fHighlightLifeline.getX() + Metrics.getLifelineWidth() / 2 - Metrics.FRAME_H_MARGIN, (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * fNbEvent); + context.setBackground(backupColor); + } + super.draw(context, false); + int lifelineArryStep = 1; + if (Metrics.swimmingLaneWidth() * context.getZoom() < Metrics.LIFELINE_SIGNIFICANT_HSPACING) { + lifelineArryStep = Math.round(Metrics.LIFELINE_SIGNIFICANT_HSPACING / (Metrics.swimmingLaneWidth() * context.getZoom())); + } + if (getIndexes().size() == 0) { + return; + } + int lifeLineDrawIndex = getIndexes().get(Lifeline.LIFELINE_TAG).intValue(); + for (int i = lifeLineDrawIndex; i < getNodeMap().get(Lifeline.LIFELINE_TAG).size(); i = i + lifelineArryStep) { + Lifeline toDraw = (Lifeline) getNodeMap().get(Lifeline.LIFELINE_TAG).get(i); + if (toDraw.getX() - Metrics.LIFELINE_SPACING / 2 > context.getContentsX() + context.getVisibleWidth()) { + break; + } + toDraw.drawName(context); + + if (fHighlightLifeline != null) { + if (toDraw == fHighlightLifeline) { + toDraw.highlightExecOccurrenceRegion(context, fStartEvent, fNbEvent, fHighlightColor); + } else if ((toDraw.getIndex() < fHighlightLifeline.getIndex()) || ((toDraw.getIndex() < fHighlightLifeline.getIndex()))) { + + int acIndex = toDraw.getExecOccurrenceDrawIndex(); + // acIndex = first visible execution occurrence + // for drawing speed reason with only search on the visible subset + if (toDraw.getExecutions() != null) { + for (int index = acIndex; index < toDraw.getExecutions().size(); index++) { + BasicExecutionOccurrence exec = (BasicExecutionOccurrence) toDraw.getExecutions().get(index); + int tempEvent = fStartEvent; + for (int j = 0; j < fNbEvent; j++) { + if (((tempEvent >= exec.getStartOccurrence()) && (tempEvent <= exec.getEndOccurrence()) && (tempEvent + 1 >= exec.getStartOccurrence()) && (tempEvent + 1 <= exec.getEndOccurrence()))) { + toDraw.highlightExecOccurrenceRegion(context, tempEvent, 1, SDViewPref.getInstance().getTimeCompressionSelectionColor()); + } + tempEvent = tempEvent + 1; + } + // if we are outside the visible area we stop right now + // This works because execution occurrences are ordered along the Y axis + if (exec.getY() > getY()) { + break; + } + } + } + } + } + } + } + + @Override + protected List<SDTimeEvent> buildTimeArray() { + + if (!hasChildren()) { + return new ArrayList<>(); + } + + List<SDTimeEvent> timeArray = super.buildTimeArray(); + fExecutionOccurrencesWithTime = null; + if (getLifelines() != null) { + for (int i = 0; i < getNodeMap().get(Lifeline.LIFELINE_TAG).size(); i++) { + Lifeline lifeline = (Lifeline) getNodeMap().get(Lifeline.LIFELINE_TAG).get(i); + if (lifeline.hasTimeInfo() && lifeline.getExecutions() != null) { + for (Iterator<GraphNode> j = lifeline.getExecutions().iterator(); j.hasNext();) { + GraphNode o = j.next(); + if (o instanceof ExecutionOccurrence) { + ExecutionOccurrence eo = (ExecutionOccurrence) o; + if (eo.hasTimeInfo()) { + int event = eo.getStartOccurrence(); + ITmfTimestamp time = eo.getStartTime(); + SDTimeEvent f = new SDTimeEvent(time, event, eo); + timeArray.add(f); + if (fExecutionOccurrencesWithTime == null) { + fExecutionOccurrencesWithTime = new ArrayList<>(); + } + fExecutionOccurrencesWithTime.add(f); + event = eo.getEndOccurrence(); + time = eo.getEndTime(); + f = new SDTimeEvent(time, event, eo); + timeArray.add(f); + fExecutionOccurrencesWithTime.add(f); + } + } + } + } + } + } + + if (fExecutionOccurrencesWithTime != null) { + SDTimeEvent[] temp = fExecutionOccurrencesWithTime.toArray(new SDTimeEvent[fExecutionOccurrencesWithTime.size()]); + Arrays.sort(temp, new TimeEventComparator()); + fExecutionOccurrencesWithTime = Arrays.asList(temp); + } + SDTimeEvent[] temp = timeArray.toArray(new SDTimeEvent[timeArray.size()]); + Arrays.sort(temp, new TimeEventComparator()); + timeArray = Arrays.asList(temp); + return timeArray; + } + + /** + * Get the closer leaving message. + * + * @param lifeline A lifeline reference + * @param message A message reference + * @param list A list of graph nodes + * @param smallerEvent A smaller event flag + * @return the closer leaving message. + */ + protected GraphNode getCloserLeavingMessage(Lifeline lifeline, BaseMessage message, List<GraphNode> list, boolean smallerEvent) { + if (list == null) { + return null; + } + + if (!smallerEvent) { + int event = 0; + if (message != null) { + event = message.getEventOccurrence(); + } + for (int i = 0; i < list.size(); i++) { + GraphNode node = list.get(i); + if (node instanceof SyncMessage) { + SyncMessage syncNode = (SyncMessage) node; + if ((syncNode.getEventOccurrence() > event) && (syncNode.getStartLifeline() == lifeline) && !syncNode.isSameAs(message)) { + return node; + } + } else if (node instanceof AsyncMessage) { + AsyncMessage asyncNode = (AsyncMessage) node; + if ((asyncNode.getStartOccurrence() > event) && (asyncNode.getStartLifeline() == lifeline) && !asyncNode.isSameAs(message)) { + return node; + } + } + } + } else { + int event = getMaxEventOccurrence(); + if (message != null) { + if (message instanceof AsyncMessage) { + event = ((AsyncMessage) message).getStartOccurrence(); + } else { + event = message.getEventOccurrence(); + } + } + for (int i = list.size() - 1; i >= 0; i--) { + GraphNode node = list.get(i); + if (node instanceof SyncMessage) { + SyncMessage syncNode = (SyncMessage) node; + if ((syncNode.getEventOccurrence() < event) && (syncNode.getStartLifeline() == lifeline) && !syncNode.isSameAs(message)) { + return node; + } + } else if (node instanceof AsyncMessage) { + AsyncMessage asyncNode = (AsyncMessage) node; + if ((asyncNode.getStartOccurrence() < event) && (asyncNode.getStartLifeline() == lifeline) && !asyncNode.isSameAs(message)) { + return node; + } + } + } + } + return null; + } + + + /** + * Get the closer entering message. + * + * @param lifeline A lifeline reference + * @param message A message reference + * @param list A list of graph nodes + * @param smallerEvent A smaller event flag + * @return the closer entering message. + */ + protected GraphNode getCloserEnteringMessage(Lifeline lifeline, BaseMessage message, List<GraphNode> list, boolean smallerEvent) { + if (list == null) { + return null; + } + if (!smallerEvent) { + int event = 0; + if (message != null) { + event = message.getEventOccurrence(); + } + for (int i = 0; i < list.size(); i++) { + GraphNode node = list.get(i); + if (node instanceof SyncMessage) { + SyncMessage syncNode = (SyncMessage) node; + if ((syncNode.getEventOccurrence() > event) && (syncNode.getEndLifeline() == lifeline) && !syncNode.isSameAs(message)) { + return node; + } + } else if (node instanceof AsyncMessage) { + AsyncMessage asyncNode = (AsyncMessage) node; + if ((asyncNode.getStartOccurrence() > event) && (asyncNode.getEndLifeline() == lifeline) && !asyncNode.isSameAs(message)) { + return node; + } + } + } + } else { + int event = getMaxEventOccurrence(); + if (message != null) { + if (message instanceof AsyncMessage) { + event = ((AsyncMessage) message).getStartOccurrence(); + } else { + event = message.getEventOccurrence(); + } + } + for (int i = list.size() - 1; i >= 0; i--) { + GraphNode node = list.get(i); + if (node instanceof SyncMessage) { + SyncMessage syncNode = (SyncMessage) node; + if ((syncNode.getEventOccurrence() < event) && (syncNode.getEndLifeline() == lifeline) && !syncNode.isSameAs(message)) { + return node; + } + } else if (node instanceof AsyncMessage) { + AsyncMessage asyncNode = (AsyncMessage) node; + if ((asyncNode.getStartOccurrence() < event) && (asyncNode.getEndLifeline() == lifeline) && !asyncNode.isSameAs(message)) { + return node; + } + } + } + } + return null; + } + + /** + * Get distance of given event from given graph node. + * + * @param node A graph node reference. + * @param event A event number to check. + * @return distance of event from graph node. + */ + protected int distanceFromEvent(GraphNode node, int event) { + int distance = 0; + if (node instanceof SyncMessage) { + distance = ((SyncMessage) node).getEventOccurrence() - event; + } else if (node instanceof AsyncMessage) { + int start = ((AsyncMessage) node).getStartOccurrence(); + int end = ((AsyncMessage) node).getEndOccurrence(); + if ((start - event) < (end - event)) { + distance = start - event; + } else { + distance = end - event; + } + } + return Math.abs(distance); + } + + /** + * Get node from 2 given nodes that is close to event. + * + * @param node1 A first graph node + * @param node2 A second graph node + * @param event A event to check. + * @return graph node that is closer or <code>null</code> + */ + protected GraphNode getCloserToEvent(GraphNode node1, GraphNode node2, int event) { + if ((node1 != null) && (node2 != null)) { + if (distanceFromEvent(node1, event) < distanceFromEvent(node2, event)) { + return node1; + } + return node2; + } else if (node1 != null) { + return node1; + } else if (node2 != null) { + return node2; + } + return null; + } + + /** + * Get called message based on given start message. + * + * @param startMessage A start message to check. + * @return called message (graph node) or <code>null</code> + */ + public GraphNode getCalledMessage(BaseMessage startMessage) { + int event = 0; + GraphNode result = null; + Lifeline lifeline = null; + if (startMessage != null) { + event = startMessage.getEventOccurrence(); + lifeline = startMessage.getEndLifeline(); + if (lifeline == null) { + lifeline = startMessage.getStartLifeline(); + } + } + if (lifeline == null) { + return null; + } + GraphNode message = getCloserLeavingMessage(lifeline, startMessage, getSyncMessages(), false); + GraphNode messageReturn = getCloserLeavingMessage(lifeline, startMessage, getSyncMessagesReturn(), false); + result = getCloserToEvent(message, messageReturn, event); + message = getCloserLeavingMessage(lifeline, startMessage, getAsyncMessages(), false); + result = getCloserToEvent(result, message, event); + messageReturn = getCloserLeavingMessage(lifeline, startMessage, getAsyncMessagesReturn(), false); + result = getCloserToEvent(result, messageReturn, event); + return result; + } + + /** + * Get caller message based on given start message. + * + * @param startMessage A start message to check. + * @return called message (graph node) or <code>null</code> + */ + public GraphNode getCallerMessage(BaseMessage startMessage) { + int event = getMaxEventOccurrence(); + GraphNode result = null; + Lifeline lifeline = null; + if (startMessage != null) { + event = startMessage.getEventOccurrence(); + lifeline = startMessage.getStartLifeline(); + if (lifeline == null) { + lifeline = startMessage.getEndLifeline(); + } + } + if (lifeline == null) { + return null; + } + GraphNode message = getCloserEnteringMessage(lifeline, startMessage, getSyncMessages(), true); + GraphNode messageReturn = getCloserEnteringMessage(lifeline, startMessage, getSyncMessagesReturn(), true); + result = getCloserToEvent(message, messageReturn, event); + message = getCloserEnteringMessage(lifeline, startMessage, getAsyncMessages(), true); + result = getCloserToEvent(result, message, event); + messageReturn = getCloserEnteringMessage(lifeline, startMessage, getAsyncMessagesReturn(), true); + result = getCloserToEvent(result, messageReturn, event); + return result; + } + + /** + * Get next lifeline based on given message. + * + * @param lifeline A lifeline reference + * @param startMessage A start message to check + * @return next lifeline or <code>null</code> + */ + public GraphNode getNextLifelineMessage(Lifeline lifeline, BaseMessage startMessage) { + int event = 0; + if (startMessage != null) { + event = startMessage.getEventOccurrence(); + } + if (lifeline == null) { + return null; + } + GraphNode message = getCloserLeavingMessage(lifeline, startMessage, getSyncMessages(), false); + GraphNode messageReturn = getCloserLeavingMessage(lifeline, startMessage, getSyncMessagesReturn(), false); + GraphNode result = getCloserToEvent(message, messageReturn, event); + message = getCloserLeavingMessage(lifeline, startMessage, getAsyncMessages(), false); + result = getCloserToEvent(result, message, event); + messageReturn = getCloserLeavingMessage(lifeline, startMessage, getAsyncMessagesReturn(), false); + result = getCloserToEvent(result, messageReturn, event); + return result; + } + + /** + * Get previous lifeline based on given message. + * + * @param lifeline A lifeline reference + * @param startMessage A start message to check. + * @return previous lifeline or <code>null</code> + */ + public GraphNode getPrevLifelineMessage(Lifeline lifeline, BaseMessage startMessage) { + int event = getMaxEventOccurrence(); + if (startMessage != null) { + if (startMessage instanceof AsyncMessage) { + event = ((AsyncMessage) startMessage).getStartOccurrence(); + } else { + event = startMessage.getEventOccurrence(); + } + } + if (lifeline == null) { + return null; + } + GraphNode message = getCloserLeavingMessage(lifeline, startMessage, getSyncMessages(), true); + GraphNode messageReturn = getCloserLeavingMessage(lifeline, startMessage, getSyncMessagesReturn(), true); + GraphNode result = getCloserToEvent(message, messageReturn, event); + message = getCloserLeavingMessage(lifeline, startMessage, getAsyncMessages(), true); + result = getCloserToEvent(result, message, event); + messageReturn = getCloserLeavingMessage(lifeline, startMessage, getAsyncMessagesReturn(), true); + result = getCloserToEvent(result, messageReturn, event); + return result; + } + + /** + * Get the first execution occurrence. + * + * @param lifeline A lifeline reference + * @return the first execution occurrence of lifeline or <code>null</code>. + */ + public BasicExecutionOccurrence getFirstExecution(Lifeline lifeline) { + if (lifeline == null) { + return null; + } + List<GraphNode> list = lifeline.getExecutions(); + + if ((list == null) || (list.isEmpty())) { + return null; + } + + BasicExecutionOccurrence result = (BasicExecutionOccurrence) list.get(0); + for (int i = 0; i < list.size(); i++) { + BasicExecutionOccurrence e = (BasicExecutionOccurrence) list.get(i); + if ((e.getStartOccurrence() < result.getEndOccurrence())) { + result = e; + } + } + return result; + } + + /** + * Get the previous execution occurrence relative to a given execution occurrence. + * + * @param exec A execution occurrence reference. + * @return the previous execution occurrence of lifeline or <code>null</code>. + */ + public BasicExecutionOccurrence getPrevExecOccurrence(BasicExecutionOccurrence exec) { + if (exec == null) { + return null; + } + Lifeline lifeline = exec.getLifeline(); + if (lifeline == null) { + return null; + } + List<GraphNode> list = lifeline.getExecutions(); + if (list == null) { + return null; + } + BasicExecutionOccurrence result = null; + for (int i = 0; i < list.size(); i++) { + BasicExecutionOccurrence e = (BasicExecutionOccurrence) list.get(i); + if ((e.getStartOccurrence() < exec.getStartOccurrence()) && (result == null)) { + result = e; + } + if ((e.getStartOccurrence() < exec.getStartOccurrence()) && (result != null) && (e.getStartOccurrence() >= result.getEndOccurrence())) { + result = e; + } + } + return result; + } + + /** + * Get the next execution occurrence relative to a given execution occurrence. + * + * @param exec A execution occurrence reference. + * @return the next execution occurrence of lifeline or <code>null</code>. + */ + public BasicExecutionOccurrence getNextExecOccurrence(BasicExecutionOccurrence exec) { + if (exec == null) { + return null; + } + Lifeline lifeline = exec.getLifeline(); + if (lifeline == null) { + return null; + } + List<GraphNode> list = lifeline.getExecutions(); + if (list == null) { + return null; + } + BasicExecutionOccurrence result = null; + for (int i = 0; i < list.size(); i++) { + BasicExecutionOccurrence e = (BasicExecutionOccurrence) list.get(i); + if ((e.getStartOccurrence() > exec.getStartOccurrence()) && (result == null)) { + result = e; + } + if ((e.getStartOccurrence() > exec.getStartOccurrence()) && (result != null) && (e.getStartOccurrence() <= result.getEndOccurrence())) { + result = e; + } + } + return result; + } + + /** + * Get the last execution occurrence. + * + * @param lifeline A lifeline reference. + * @return the last execution occurrence of lifeline or <code>null</code>. + */ + public BasicExecutionOccurrence getLastExecOccurrence(Lifeline lifeline) { + if (lifeline == null) { + return null; + } + List<GraphNode> list = lifeline.getExecutions(); + if (list == null) { + return null; + } + BasicExecutionOccurrence result = null; + for (int i = 0; i < list.size(); i++) { + BasicExecutionOccurrence e = (BasicExecutionOccurrence) list.get(i); + if (result == null) { + result = e; + } + if (e.getStartOccurrence() > result.getEndOccurrence()) { + result = e; + } + } + return result; + } + + /** + * @return highlighted life line if set else null. + * @since 2.0 + */ + protected Lifeline getHighlightLifeline() { + return fHighlightLifeline; + } + + /** + * @return the start event value. + * @since 2.0 + */ + protected int getStartEvent() { + return fStartEvent; + } + + /** + * Returns the number of events + * + * @return the number of events + * @since 2.0 + */ + protected int getNumberOfEvents() { + return fNbEvent; + } + + /** + * Returns the highlight color. + * @return the highlight color + * @since 2.0 + */ + protected IColor getHighlightColor() { + return fHighlightColor; + } + + /** + * Set the highlighted life line. + * @param lifeline + * The highlighted life line if set else null + * @since 2.0 + */ + protected void setHighlightLifeline(Lifeline lifeline) { + fHighlightLifeline = lifeline; + } + + /** + * Sets the start event value + * @param startEvent + * the start event value. + * @since 2.0 + */ + protected void setStartEvent(int startEvent) { + fStartEvent = startEvent; + } + + /** + * Sets the number of events + * + * @param nbEvents + * The number of events + * @since 2.0 + */ + protected void setNumberOfEvents(int nbEvents) { + fNbEvent = nbEvents; + } + + /** + * Sets the highlight color. + * @param color + * the highlight color + * @since 2.0 + */ + protected void setHighlightColor(IColor color) { + fHighlightColor = color; + } + + /** + * sets the list of execution occurrences. + * + * @param occurences + * the list of execution occurrences + * @since 2.0 + */ + protected void setExecutionOccurrencesWithTime(List<SDTimeEvent> occurences) { + fExecutionOccurrencesWithTime = occurences; + } +}
\ No newline at end of file diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/GraphNode.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/GraphNode.java new file mode 100755 index 0000000000..f0c026a74a --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/GraphNode.java @@ -0,0 +1,906 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.tracecompass.internal.tmf.ui.TmfUiTracer; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * The base class used for all UML2 graph nodes displayed in the Sequence Diagram SDWidget. + * + * @author sveyrier + * @version 1.0 + */ +public abstract class GraphNode { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The start event occurrence. + */ + private int fStartEventOccurrence = 0; + /** + * The event event occurrence. + */ + private int fEndEventOccurrence = 0; + /** + * Preference ColorId to use to draw font + */ + private String fPrefId = ISDPreferences.PREF_SYNC_MESS; + /** + * The selection state of the graph node. + */ + private boolean fSelected = false; + /** + * The focus state of the graph node. + */ + private boolean fFocused = false; + /** + * Flag to indicate whether node has children or not. + */ + private boolean fHasChilden = false; + /** + * The graph node name used to label the graph node in the View. + */ + private String fName = ""; //$NON-NLS-1$ + /** + * A map from node name to graph node. + */ + private Map<String, List<GraphNode>> fNodes; + /** + * A map from node name to graph node for forward sorting + */ + private Map<String, List<GraphNode>> fForwardNodes; + /** + * A map from node name to graph node for backwards sorting. + */ + private Map<String, List<GraphNode>> fBackwardNodes; + /** + * A map from node name to index. + */ + private Map<String, Integer> fIndexes; + /** + * A map from node name to flag for forwards sorting. + */ + private Map<String, Boolean> fForwardSort; + /** + * A map from node name to flag for backwards sorting. + */ + private Map<String, Boolean> fBackwardSort; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Reset the internal index of the first visible GraphNode for each ordered GraphNode lists + */ + public void resetIndex() { + if (!fHasChilden) { + return; + } + + Iterator<String> it = fIndexes.keySet().iterator(); + while (it.hasNext()) { + String nodeType = it.next(); + fIndexes.put(nodeType, Integer.valueOf(0)); + } + } + + /** + * Add a GraphNode into the receiver + * + * @param nodeToAdd the node to add + */ + public void addNode(GraphNode nodeToAdd) { + if (!fHasChilden) { + fNodes = new HashMap<>(2); + fForwardNodes = new HashMap<>(2); + fBackwardNodes = new HashMap<>(2); + fIndexes = new HashMap<>(2); + fBackwardSort = new HashMap<>(2); + fForwardSort = new HashMap<>(2); + fHasChilden = true; + } + + // Nothing to add + if (nodeToAdd == null) { + return; + } + + if (fNodes.get(nodeToAdd.getArrayId()) == null) { + fNodes.put(nodeToAdd.getArrayId(), new ArrayList<GraphNode>(1)); + fIndexes.put(nodeToAdd.getArrayId(), Integer.valueOf(0)); + fForwardNodes.put(nodeToAdd.getArrayId(), new ArrayList<GraphNode>(1)); + fForwardSort.put(nodeToAdd.getArrayId(), Boolean.FALSE); + if (nodeToAdd.getBackComparator() != null) { + fBackwardNodes.put(nodeToAdd.getArrayId(), new ArrayList<GraphNode>(1)); + fBackwardSort.put(nodeToAdd.getArrayId(), Boolean.FALSE); + } + } + + List<GraphNode> fNodeList = fForwardNodes.get(nodeToAdd.getArrayId()); + List<GraphNode> bNodeList = null; + if (fBackwardNodes != null) { + bNodeList = fBackwardNodes.get(nodeToAdd.getArrayId()); + } + if (fNodeList != null && fNodeList.size() > 0) { + // check if the nodes are added y ordered + // if not, tag the list to sort it later (during draw) + GraphNode node = fNodeList.get(fNodeList.size() - 1); + Comparator<GraphNode> fcomp = nodeToAdd.getComparator(); + Comparator<GraphNode> bcomp = nodeToAdd.getBackComparator(); + if ((fcomp != null) && (fcomp.compare(node, nodeToAdd) > 0)) { + fForwardSort.put(nodeToAdd.getArrayId(), Boolean.TRUE); + } + if ((bcomp != null) && (bcomp.compare(node, nodeToAdd) > 0)) { + fBackwardSort.put(nodeToAdd.getArrayId(), Boolean.TRUE); + } + } + + if (fNodeList == null) { + fNodeList = new ArrayList<>(); + } + + fNodeList.add(nodeToAdd); + fNodes.put(nodeToAdd.getArrayId(), fNodeList); + fForwardNodes.put(nodeToAdd.getArrayId(), fNodeList); + if ((bNodeList != null) && (nodeToAdd.getBackComparator() != null)) { + bNodeList.add(nodeToAdd); + fBackwardNodes.put(nodeToAdd.getArrayId(), bNodeList); + } + } + + /** + * Set the graph node name.<br> + * It is the name display in the view to label the graph node. + * + * @param nodeName the name to set + */ + public void setName(String nodeName) { + fName = nodeName; + } + + /** + * Returns the graph node name.<br> + * It is the name display in the view to label the graph node. + * + * @return the graph node name + */ + public String getName() { + return fName; + } + + /** + * Tags the the graph node has selected.<br> + * WARNING: This method is only used to draw the graph node using the system selection colors. <br> + * To use the complete SDViewer selection mechanism (selection management, notification, etc..) see SDWidget class + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget#addSelection(GraphNode) + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget#removeSelection(GraphNode) + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget#clearSelection() + * @param selection - true to set selected, false to set unselected + */ + public void setSelected(boolean selection) { + fSelected = selection; + } + + /** + * Tags the the graph node as focused.<br> + * WARNING: This method is only used to draw the graph node using the system focus style. <br> + * To use the complete SDViewer focus mechanism see SDWidget class + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget#addSelection(GraphNode) + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget#removeSelection(GraphNode) + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget#clearSelection() + * @param focus - true to set focued, false otherwise + */ + public void setFocused(boolean focus) { + fFocused = focus; + } + + /** + * Returns true if the graph node is selected, false otherwise.<br> + * The returned value is used to highlight the graph node in the View. + * + * @return true if selected, false otherwise + */ + public boolean isSelected() { + return fSelected; + } + + /** + * Returns true if the graph node is focused, false otherwise.<br> + * The returned value is used to highlight the graph node in the View. + * + * @return true if focused, false otherwise + */ + public boolean hasFocus() { + return fFocused; + } + + /** + * Returns true if the graph node contains the point given in parameter, + * return false otherwise. + * + * @param x + * the x coordinate of the point to test containment + * @param y + * the y coordinate of the point to test containment + * @return true if contained, false otherwise + */ + abstract public boolean contains(int x, int y); + + /** + * Returns the x coordinate of the graph node + * + * @return the x coordinate + */ + abstract public int getX(); + + /** + * Returns the y coordinate of the graph node + * + * @return the y coordinate + */ + abstract public int getY(); + + /** + * Returns the graph node height + * + * @return the graph node height + */ + abstract public int getHeight(); + + /** + * Returns the graph node width + * + * @return the graph node width + */ + abstract public int getWidth(); + + /** + * Draws the graph node in the given context + * + * @param context the graphical context to draw in + */ + abstract protected void draw(IGC context); + + /** + * Returns the GraphNode visibility for the given visible area. Wrong + * visibility calculation, may strongly impact drawing performance + * + * @param x + * The X coordinate + * @param y + * The Y coordinate + * @param width + * The width of the area + * @param height + * The height of the area + * @return true if visible, false otherwise + */ + public boolean isVisible(int x, int y, int width, int height) { + return true; + } + + /** + * Return a comparator to sort the GraphNode of the same type This + * comparator is used to order the GraphNode array of the given node type. + * (see getArrayId). + * + * @return the comparator + */ + public Comparator<GraphNode> getComparator() { + return null; + } + + /** + * If needed, return a different comparator to backward scan the GraphNode + * array + * + * @return the backward comparator or null if not needed + */ + public Comparator<GraphNode> getBackComparator() { + return null; + } + + /** + * Compare two graphNodes + * + * @param node + * the node to compare to + * @return true if equal false otherwise + */ + public boolean isSameAs(GraphNode node) { + return false; + } + + /** + * Return the node type for all class instances. This id is used to store the same nodes kind in the same ordered + * array. + * + * @return the node type identifier + */ + abstract public String getArrayId(); + + /** + * Return true if the distance from the GraphNode to the given point is positive + * + * @param x the point x coordinate + * @param y the point y coordinate + * @return true if positive false otherwise + */ + public boolean positiveDistanceToPoint(int x, int y) { + return false; + } + + /** + * Returns the graph node which contains the point given in parameter WARNING: Only graph nodes in the current + * visible area can be returned + * + * @param x the x coordinate of the point to test + * @param y the y coordinate of the point to test + * @return the graph node containing the point given in parameter, null otherwise + */ + public GraphNode getNodeAt(int x, int y) { + GraphNode toReturn = null; + + if (!fHasChilden) { + return null; + } + + Iterator<String> it = fNodes.keySet().iterator(); + GraphNode node = null; + while (it.hasNext()) { + Object nodeType = it.next(); + List<GraphNode> list = fNodes.get(nodeType); + int index = fIndexes.get(nodeType).intValue(); + node = getNodeFromListAt(x, y, list, index); + if (toReturn == null) { + toReturn = node; + } + if (node != null) { + GraphNode internalNode = node.getNodeAt(x, y); + if (internalNode != null) { + return internalNode; + } else if (Math.abs(node.getWidth()) < Math.abs(toReturn.getWidth()) || Math.abs(node.getHeight()) < Math.abs(toReturn.getHeight())) { + toReturn = node; + } + } + } + return toReturn; + } + + /** + * Gets node list from node A to node B + + * @param from A from node + * @param to A to node + * @return the list of nodes + */ + public List<GraphNode> getNodeList(GraphNode from, GraphNode to) { + List<GraphNode> result = new ArrayList<>(); + + if (from != null) { + result.add(from); + } else if (to != null) { + result.add(to); + } + + if ((from == null) || (to == null)) { + return result; + } + + if (from == to) { + return result; + } + + int startX = Math.min(from.getX(), Math.min(to.getX(), Math.min(from.getX() + from.getWidth(), to.getX() + to.getWidth()))); + int endX = Math.max(from.getX(), Math.max(to.getX(), Math.max(from.getX() + from.getWidth(), to.getX() + to.getWidth()))); + int startY = Math.min(from.getY(), Math.min(to.getY(), Math.min(from.getY() + from.getHeight(), to.getY() + to.getHeight()))); + int endY = Math.max(from.getY(), Math.max(to.getY(), Math.max(from.getY() + from.getHeight(), to.getY() + to.getHeight()))); + + if (!fHasChilden) { + return result; + } + + Iterator<String> it = fNodes.keySet().iterator(); + while (it.hasNext()) { + Object nodeType = it.next(); + List<GraphNode> nodesList = fNodes.get(nodeType); + if (nodesList == null || nodesList.isEmpty()) { + return null; + } + for (int i = 0; i < nodesList.size(); i++) { + GraphNode node = nodesList.get(i); + int nw = node.getWidth(); + int nh = node.getHeight(); + int nx = node.getX(); + int ny = node.getY(); + if (contains(startX, startY, endX - startX, endY - startY, nx + 1, ny + 1) && contains(startX, startY, endX - startX, endY - startY, nx + nw - 2, ny + nh - 2)) { + result.add(node); + } + result.addAll(node.getNodeList(from, to)); + } + } + + if (!result.contains(to)) { + result.add(to); + } + return result; + } + + /** + * Returns the graph node which contains the point given in parameter for the given graph node list and starting the + * iteration at the given index<br> + * WARNING: Only graph nodes with smaller coordinates than the current visible area can be returned.<br> + * + * @param x the x coordinate of the point to test + * @param y the y coordinate of the point to test + * @param list the list to search in + * @param fromIndex list browsing starting point + * @return the graph node containing the point given in parameter, null otherwise + */ + protected GraphNode getNodeFromListAt(int x, int y, List<GraphNode> list, int fromIndex) { + if (list == null) { + return null; + } + for (int i = fromIndex; i < list.size(); i++) { + GraphNode node = list.get(i); + if (node.contains(x, y)) { + return node; + } + } + return null; + } + + /** + * Returns the start event occurrence attached to this graphNode. + * + * @return the start event occurrence attached to the graphNode + */ + public int getStartOccurrence() { + return fStartEventOccurrence; + } + + /** + * Returns the end event occurrence attached to this graphNode + * + * @return the start event occurrence attached to the graphNode + */ + public int getEndOccurrence() { + return fEndEventOccurrence; + } + + /** + * Computes the index of the first visible GraphNode for each ordered graph node lists depending on the visible area + * given in parameter + * + * @param x visible area top left corner x coordinate + * @param y visible area top left corner y coordinate + * @param width visible area width + * @param height visible area height + */ + public void updateIndex(int x, int y, int width, int height) { + if (!fHasChilden) { + return; + } + if(TmfUiTracer.isIndexTraced()) { + TmfUiTracer.traceIndex("*****************************\n"); //$NON-NLS-1$ + TmfUiTracer.traceIndex("Visible area position in virtual screen (x,y)= " + x + " " + y + "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + Iterator<String> it = fNodes.keySet().iterator(); + while (it.hasNext()) { + String nodeType = it.next(); + int direction = 1; + int drawIndex = fIndexes.get(nodeType).intValue(); + /* + * if (x==0) { drawIndex = 0; indexes.put(nodeType,new Integer(drawIndex)); } + */ + if ((fNodes.get(nodeType) != null) && (fNodes.get(nodeType).size() > 1)) { + if (fNodes.get(nodeType).get(drawIndex).positiveDistanceToPoint(x, y)) { + direction = -1; + } + + if (drawIndex == 0) { + direction = 1; + } + + if ((direction == -1) && (fBackwardNodes.get(nodeType) != null)) { + GraphNode currentNode = fNodes.get(nodeType).get(drawIndex); + drawIndex = Arrays.binarySearch(fBackwardNodes.get(nodeType).toArray(new GraphNode[fBackwardNodes.get(nodeType).size()]), + fNodes.get(nodeType).get(drawIndex), currentNode.getBackComparator()); + fNodes.put(nodeType, fBackwardNodes.get(nodeType)); + if (drawIndex < 0) { + drawIndex = 0; + direction = 1; + } else { + fNodes.put(nodeType, fBackwardNodes.get(nodeType)); + } + } + GraphNode prev = null; + + for (int i = drawIndex; i < fNodes.get(nodeType).size() && i >= 0; i = i + direction) { + drawIndex = i; + fIndexes.put(nodeType, Integer.valueOf(i)); + + GraphNode currentNode = fNodes.get(nodeType).get(i); + + if (prev == null) { + prev = currentNode; + } + + Comparator<GraphNode> comp = currentNode.getComparator(); + Map<String, Boolean> sort = fForwardSort; + + if ((direction == -1) && (currentNode.getBackComparator() != null)) { + comp = currentNode.getBackComparator(); + sort = fBackwardSort; + } + + if (i < fNodes.get(nodeType).size() - 1) { + GraphNode next = fNodes.get(nodeType).get(i + 1); + + if ((comp != null) && (comp.compare(currentNode, next) > 0)) { + sort.put(nodeType, Boolean.TRUE); + } + } + if (direction == 1) { + if (fNodes.get(nodeType).get(i).positiveDistanceToPoint(x, y)) { + break; + } + } else { + if (currentNode.getBackComparator() == null) { + if // (currentNode.isVisible(x,y,width,height) + (!currentNode.positiveDistanceToPoint(x, y)) { + break; + } + } else { + if (currentNode.isVisible(x, y, width, height) && !currentNode.positiveDistanceToPoint(x, y)) { + if ((comp != null) && (comp.compare(currentNode, prev) <= 0)) { + break; + } + } else if ((comp != null) && (comp.compare(currentNode, prev) <= 0)) { + prev = currentNode; + } + } + } + } + + fNodes.put(nodeType, fForwardNodes.get(nodeType)); + if ((fBackwardNodes.get(nodeType) != null) && (direction == -1)) { + // nodes.put(nodeType,fnodes.get(nodeType)); + int index = fIndexes.get(nodeType).intValue(); + List<GraphNode> list = fNodes.get(nodeType); + List<GraphNode> backList = fBackwardNodes.get(nodeType); + GraphNode currentNode = (backList.get(index)); + if (index > 0) { + index = Arrays.binarySearch(list.toArray(new GraphNode[list.size()]), backList.get(index), currentNode.getComparator()); + if (index < 0) { + index = 0; + } + fIndexes.put(nodeType, Integer.valueOf(index)); + } + } + + for (int i = drawIndex; i < fNodes.get(nodeType).size() && i >= 0; i++) { + GraphNode toDraw = fNodes.get(nodeType).get(i); + toDraw.updateIndex(x, y, width, height); + if (!toDraw.isVisible(x, y, width, height)) { + break; + } + } + } + if (TmfUiTracer.isIndexTraced()) { + TmfUiTracer.traceIndex("First drawn " + nodeType + " index = " + drawIndex + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + TmfUiTracer.traceIndex(nodeType + " found in " + 0 + " iterations\n"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + if (TmfUiTracer.isIndexTraced()) { + TmfUiTracer.traceIndex("*****************************\n"); //$NON-NLS-1$ + } + } + + /** + * Draws the children nodes on the given context.<br> + * This method start width GraphNodes ordering if needed.<br> + * After, depending on the visible area, only visible GraphNodes are drawn.<br> + * + * @param context the context to draw to + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode#draw(IGC) + */ + protected void drawChildenNodes(IGC context) { + + if (!fHasChilden) { + return; + } + // If the nodes have not been added ordered, the array is ordered + Iterator<String> it = fForwardSort.keySet().iterator(); + while (it.hasNext()) { + String nodeType = it.next(); + boolean sort = fForwardSort.get(nodeType).booleanValue(); + if (sort) { + GraphNode[] temp = fForwardNodes.get(nodeType).toArray(new GraphNode[fForwardNodes.get(nodeType).size()]); + GraphNode node = fNodes.get(nodeType).get(0); + Arrays.sort(temp, node.getComparator()); + fForwardSort.put(nodeType, Boolean.FALSE); + fNodes.put(nodeType, Arrays.asList(temp)); + fForwardNodes.put(nodeType, Arrays.asList(temp)); + if (TmfUiTracer.isSortingTraced()) { + TmfUiTracer.traceSorting(nodeType + " array sorted\n"); //$NON-NLS-1$ + } + } + } + + Iterator<String> it2 = fBackwardSort.keySet().iterator(); + while (it2.hasNext()) { + String nodeType = it2.next(); + boolean sort = fBackwardSort.get(nodeType).booleanValue(); + if (sort) { + GraphNode[] temp = fBackwardNodes.get(nodeType).toArray(new GraphNode[fBackwardNodes.get(nodeType).size()]); + GraphNode node = fNodes.get(nodeType).get(0); + Arrays.sort(temp, node.getBackComparator()); + fBackwardSort.put(nodeType, Boolean.FALSE); + fBackwardNodes.put(nodeType, Arrays.asList(temp)); + if (TmfUiTracer.isSortingTraced()) { + TmfUiTracer.traceSorting(nodeType + " back array sorted\n"); //$NON-NLS-1$ + } + } + } + + if (TmfUiTracer.isDisplayTraced()) { + TmfUiTracer.traceDisplay("*****************************\n"); //$NON-NLS-1$ + } + + int arrayStep = 1; + if ((Metrics.getMessageFontHeigth() + Metrics.MESSAGES_NAME_SPACING * 2) * context.getZoom() < Metrics.MESSAGE_SIGNIFICANT_VSPACING) { + arrayStep = Math.round(Metrics.MESSAGE_SIGNIFICANT_VSPACING / ((Metrics.getMessageFontHeigth() + Metrics.MESSAGES_NAME_SPACING * 2) * context.getZoom())); + } + + int count = 0; + Iterator<String> it3 = fForwardSort.keySet().iterator(); + while (it3.hasNext()) { + count = 0; + Object nodeType = it3.next(); + GraphNode node = fNodes.get(nodeType).get(0); + context.setFont(SDViewPref.getInstance().getFont(node.fPrefId)); + int index = fIndexes.get(nodeType).intValue(); + count = drawNodes(context, fNodes.get(nodeType), index, arrayStep); + if (TmfUiTracer.isDisplayTraced()) { + TmfUiTracer.traceDisplay(count + " " + nodeType + " drawn, starting from index " + index + "\r\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + if (TmfUiTracer.isDisplayTraced()) { + TmfUiTracer.traceDisplay("*****************************\n"); //$NON-NLS-1$ + } + + } + + /** + * Draw the GraphNode stored in the given list, starting at index startIndex with the given step + * + * @param context the context to draw to + * @param list the GraphNodes list + * @param startIndex the start index + * @param step the step to browse the list + * @return the number of GraphNodes drawn + */ + protected int drawNodes(IGC context, List<GraphNode> list, int startIndex, int step) { + if (!fHasChilden) { + return 0; + } + + GraphNode last = null; + int nodesCount = 0; + if (list.size() < 0) { + return 0; + } + + GraphNode node = list.get(0); + context.setFont(SDViewPref.getInstance().getFont(node.fPrefId)); + Comparator<GraphNode> comparator = node.getComparator(); + for (int i = startIndex; i < list.size(); i = i + step) { + GraphNode toDraw = list.get(i); + if (i < list.size() - 1) { + GraphNode next = list.get(i + 1); + if ((comparator != null) && (comparator.compare(toDraw, next) > 0)) { + fForwardSort.put(next.getArrayId(), Boolean.TRUE); + } + } + int cx = context.getContentsX(); + int cy = context.getContentsY(); + int cw = context.getVisibleWidth(); + int ch = context.getVisibleHeight(); + // The arrays should be ordered, no needs to continue for this one + if (!toDraw.isVisible(cx, cy, cw, ch) && toDraw.positiveDistanceToPoint(cx + cw, cy + ch)) { + break; + } + // ***Common*** nodes visibility + if ((!toDraw.isSameAs(last) || toDraw.isSelected()) && (toDraw.isVisible(context.getContentsX(), context.getContentsY(), context.getVisibleWidth(), context.getVisibleHeight()))) { + nodesCount++; + + toDraw.draw(context); + if (hasFocus()) { + toDraw.drawFocus(context); + } + } + last = toDraw; + } + return nodesCount; + } + + /** + * Draws the focus within the graphical context. + * + * @param context + * The context + */ + public void drawFocus(IGC context) { + context.drawFocus(getX(), getY(), getWidth(), getHeight()); + } + + /** + * Determine if the given point (px,py) is contained in the rectangle (x,y,width,height) + * + * @param x the rectangle x coordinate + * @param y the rectangle y coordinate + * @param width the rectangle width + * @param height the rectangle height + * @param px the x coordinate of the point to test + * @param py the y coordinate of the point to test + * @return true if contained false otherwise + */ + public static boolean contains(int x, int y, int width, int height, int px, int py) { + int locX = x; + int locY = y; + int locWidth = width; + int locHeight = height; + + if (width < 0) { + locX = locX + width; + locWidth = -locWidth; + } + + if (height < 0) { + locY = locY + height; + locHeight = -locHeight; + } + return (px >= locX) && (py >= locY) && ((px - locX) <= locWidth) && ((py - locY) <= locHeight); + } + + /** + * Sets the start event occurrence attached to this graphNode. + * + * @param occurence + * the start event occurrence attached to the graphNode + * @since 2.0 + */ + protected void setStartOccurrence(int occurence) { + fStartEventOccurrence = occurence; + } + + /** + * Sets the end event occurrence attached to this graphNode + * + * @param occurence + * the start event occurrence attached to the graphNode + * @since 2.0 + */ + protected void setEndOccurrence(int occurence) { + fEndEventOccurrence = occurence; + } + + /** + * Sets the color preference id + * @param id + * The color preference id + * @since 2.0 + */ + protected void setColorPrefId(String id) { + fPrefId = id; + } + + /** + * Gets the color preference id + * @return the color preference id + * @since 2.0 + */ + protected String getColorPrefId() { + return fPrefId; + } + + /** + * @return if node has children or not + * @since 2.0 + */ + protected boolean hasChildren() { + return fHasChilden; + } + + /** + * Sets the flag indicating where the node has children or not. + * @param hasChildren + * if node has children or not + * @since 2.0 + */ + protected void hasChildren(boolean hasChildren) { + fHasChilden = hasChildren; + } + /** + * Returns a map from node name to graph node. + * + * @return map with children graph bodes + * @since 2.0 + */ + protected Map<String, List<GraphNode>> getNodeMap() { + return fNodes; + } + /** + * Returns a map from node name to graph node for forward sorting + * + * @return forward sorting map + * @since 2.0 + */ + protected Map<String, List<GraphNode>> getForwardNodes() { + return fForwardNodes; + } + /** + * Returns a map from node name to graph node for backwards sorting. + * + * @return backwards sorting map + * @since 2.0 + */ + protected Map<String, List<GraphNode>> getBackwardNodes() { + return fBackwardNodes; + } + /** + * Returns a map from node name to index. + * + * @return map with node name to index + * @since 2.0 + */ + protected Map<String, Integer> getIndexes() { + return fIndexes; + } + + /** + * Returns a map from node name to sort flag for forwards sorting. + * @return a map from node name to sort flag + * @since 2.0 + */ + protected Map<String, Boolean> getForwardSortMap() { + return fForwardSort; + } + /** + * Returns a map from node name to flag for backwards sorting. + * @return map from node name to flag for backwards sorting. + * @since 2.0 + */ + protected Map<String, Boolean> getBackwardSortMap() { + return fBackwardSort; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/HotSpot.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/HotSpot.java new file mode 100755 index 0000000000..703db9c1f4 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/HotSpot.java @@ -0,0 +1,193 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IImage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * Class to add a hot spot marker. + * + * @version 1.0 + * @author sveyrier + */ +public class HotSpot extends GraphNode { + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The grahNode ID constant + */ + public static final String GLYPH = "Glyph"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The execution occurrence the hot spot marker is for. + */ + private BasicExecutionOccurrence fExecOcc = null; + /** + * The occurrence number. + */ + private int fOccurrence = 0; + /** + * The marker image to display. + */ + private IImage fImage = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor + */ + public HotSpot() { + setColorPrefId(ISDPreferences.PREF_EXEC); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Set the marker image. + * + * @param img A image to set + */ + public void setImage(IImage img) { + fImage = img; + } + + /** + * Returns the marker image. + * + * @return the image + * @since 2.0 + */ + protected IImage getImage() { + return fImage; + } + + @Override + public int getX() { + if (fExecOcc != null) { + return fExecOcc.getX() - 3; + } + return 0; + } + + @Override + public int getY() { + if (fExecOcc != null){ + return fExecOcc.getY(); + } + return 0; + } + + @Override + public int getWidth() { + if (fExecOcc != null) { + return fExecOcc.getWidth() + 7; + } + return 0; + } + + @Override + public int getHeight() { + if (fExecOcc != null) { + return fExecOcc.getWidth() + 10; + } + return 0; + } + + /** + * Set the lifeline on which the execution occurrence appears. + * + * @param occ the parent lifeline + */ + public void setExecution(BasicExecutionOccurrence occ) { + fExecOcc = occ; + fExecOcc.addNode(this); + } + + /** + * Get the lifeline on which the execution occurrence appears. + * + * @return - the parent lifeline + */ + public BasicExecutionOccurrence getExecOcc() { + return fExecOcc; + } + + /** + * Returns the occurrence number. + * + * @return the occurrence number. + */ + public int getOccurrence() { + return fOccurrence; + } + + /** + * Set the occurrence number. + * + * @param occ A number to set. + */ + public void setOccurrence(int occ) { + fOccurrence = occ; + } + + @Override + public void draw(IGC context) { + + ISDPreferences pref = SDViewPref.getInstance(); + + // The execution occurrence is selected + // if the owning lifeline is selected + if (isSelected() || (fExecOcc != null && fExecOcc.isSelected()) || (fExecOcc != null && fExecOcc.getLifeline() != null && fExecOcc.getLifeline().isSelected())) { + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + } else { + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_EXEC)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_EXEC)); + } + context.drawImage(fImage, getX(), getY(), getWidth(), getHeight()); + } + + @Override + public String getArrayId() { + return GLYPH; + } + + @Override + public boolean isVisible(int x, int y, int width, int height) { + return true; + } + + @Override + public boolean contains(int xValue, int yValue) { + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); + + if (GraphNode.contains(x, y, width, height, xValue, yValue)) { + return true; + } + return false; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/ITimeRange.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/ITimeRange.java new file mode 100755 index 0000000000..9ea37acf4a --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/ITimeRange.java @@ -0,0 +1,46 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; + +/** + * A interface for handling time ranges. + * + * @version 1.0 + * @author sveyrier + */ +public interface ITimeRange { + + /** + * Returns the time when the message began. + * @return the time when the message began + * @since 2.0 + */ + ITmfTimestamp getStartTime(); + + /** + * Returns the time when the message ended. + * + * @return the time when the message ended + * @since 2.0 + */ + ITmfTimestamp getEndTime(); + + /** + * Returns flag to indicate whether time information is available or not. + * + * @return flag to indicate whether time information is available or not + */ + boolean hasTimeInfo(); +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Lifeline.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Lifeline.java new file mode 100755 index 0000000000..3bbabf1238 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Lifeline.java @@ -0,0 +1,534 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IImage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * Lifeline is the UML2 lifeline graphical representation.<br> + * Each lifeline owns a set of event occurrences. An event occurrence is the base element in UML2 to set an event in a + * sequence diagram.<br> + * Event occurrence define the drawing order of graph node along a lifeline. In this lifeline implementation, event + * occurrences are just integer index. The event occurrences with the same value on different lifelines will correspond + * the same y coordinate value. + * + * @version 1.0 + * @author sveyrier + * + */ +public class Lifeline extends GraphNode { + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The life line tag. + */ + public static final String LIFELINE_TAG = "Lifeline"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attribute + // ------------------------------------------------------------------------ + /** + * The lifeline position in the containing frame + */ + private int fIndexInFrame = 0; + /** + * The frame where the lifeline is drawn + */ + private Frame fFrame = null; + /** + * The current event occurrence created in the lifeline + */ + private int fEventOccurrence = 0; + /** + * The lifeline category. + */ + private int fCategory = -1; + /** + * Flag whether lifeline has time information available or not + */ + private boolean fHasTimeInfo = false; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public Lifeline() { + setColorPrefId(ISDPreferences.PREF_LIFELINE); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int getX() { + return Metrics.FRAME_H_MARGIN + Metrics.LIFELINE_H_MAGIN + (fIndexInFrame - 1) * Metrics.swimmingLaneWidth(); + } + + @Override + public int getY() { + return 2 * Metrics.FRAME_NAME_H_MARGIN + Metrics.LIFELINE_VT_MAGIN / 2 + Metrics.getFrameFontHeigth() + Metrics.getLifelineHeaderFontHeigth() + Metrics.FRAME_V_MARGIN + 2 * Metrics.LIFELINE_HEARDER_TEXT_V_MARGIN; + } + + @Override + public int getWidth() { + return Metrics.getLifelineWidth(); + } + + @Override + public int getHeight() { + // Set room for two text lines + return Metrics.getLifelineFontHeigth()/** 2 */ + + 2 * Metrics.LIFELINE_NAME_H_MARGIN; + } + + /** + * Set the lifeline category for this lifeline. + * + * @param arrayIndex the index of the category to use + * @see Frame#setLifelineCategories(LifelineCategories[]) + */ + public void setCategory(int arrayIndex) { + fCategory = arrayIndex; + } + + /** + * Gets the lifeline category for this lifeline. + * + * @return arrayIndex the index of the category to use + * @since 2.0 + */ + public int getCategory() { + return fCategory; + } + + /** + * Returns the tooltip text for the lifeline. It is the combination between the category name(if any) and the + * lifeline name + * + * @return the tooltip text + */ + public String getToolTipText() { + if (fCategory >= 0) { + LifelineCategories[] categories = fFrame.getLifelineCategories(); + if (fCategory < categories.length) { + return categories[fCategory].getName() + " " + getName(); //$NON-NLS-1$ + } + } + return ""; //$NON-NLS-1$ + } + + /** + * Returns the index of the first visible Execution Occurrence in the execution occurrence array.<br> + * Execution Occurrences are Y ordered in this array + * + * @return the first visible Execution Occurrence + */ + public int getExecOccurrenceDrawIndex() { + if (!hasChildren()) { + return 0; + } + if (getIndexes().get(BasicExecutionOccurrence.EXEC_OCC_TAG) != null) { + return getIndexes().get(BasicExecutionOccurrence.EXEC_OCC_TAG).intValue(); + } + return 0; + } + + /** + * Set the frame on which this lifeline must be drawn + * + * @param parentFrame + * Parent frame + */ + protected void setFrame(Frame parentFrame) { + fFrame = parentFrame; + if (fHasTimeInfo) { + fFrame.setHasTimeInfo(true); + } + if (fFrame.getMaxEventOccurrence() < getEventOccurrence() + 1) { + fFrame.setMaxEventOccurrence(getEventOccurrence() + 1); + } + } + + /** + * Returns the frame which this lifeline is drawn + * + * @return the Frame + */ + protected Frame getFrame() { + return fFrame; + } + + /** + * Set the lifeline position index in the containing frame + * + * @param index the lifeline X position + */ + protected void setIndex(int index) { + fIndexInFrame = index; + } + + /** + * Returns the lifeline position in de the containing frame + * + * @return the X position + */ + public int getIndex() { + return fIndexInFrame; + } + + /** + * Set the lifeline event occurrence to the value given in parameter This only change the current event occurrence, + * greater event created on this lifeline are still valid and usable. This also need to inform the frame of the + * operation mostly to store in the frame the greater event found in the diagram (used to determine the frame + * height) + * + * @param eventOcc the new current event occurrence + */ + public void setCurrentEventOccurrence(int eventOcc) { + if ((fFrame != null) && (fFrame.getMaxEventOccurrence() < eventOcc)) { + fFrame.setMaxEventOccurrence(eventOcc); + } + fEventOccurrence = eventOcc; + } + + /** + * Returns the last created event occurrence along the lifeline. + * + * @return the current event occurrence + */ + public int getEventOccurrence() { + return fEventOccurrence; + } + + /** + * Creates a new event occurrence along the lifeline. + * + * @return the new created event occurrence + */ + public int getNewEventOccurrence() { + setCurrentEventOccurrence(fEventOccurrence + 1); + return fEventOccurrence; + } + + /** + * Adds the execution occurrence given in parameter to the lifeline.<br> + * A Execution occurrence is never drawn in the frame instead it is added to a lifeline + * + * @param exec the execution occurrence to add + */ + public void addExecution(BasicExecutionOccurrence exec) { + exec.setLifeline(this); + addNode(exec); + if ((fFrame != null) && (fFrame.getMaxEventOccurrence() < exec.getEndOccurrence())) { + fFrame.setMaxEventOccurrence(exec.getEndOccurrence()); + } + } + + /** + * Set whether lifeline has time information available or not. + * @param value The value to set + */ + protected void setTimeInfo(boolean value) { + fHasTimeInfo = value; + if ((fFrame != null) && value) { + fFrame.setHasTimeInfo(value); + } + } + + /** + * Returns true if at least one execution occurrence has time info. + * + * @return true if at least one execution occurrence has time info + */ + public boolean hasTimeInfo() { + return fHasTimeInfo; + } + + /** + * Returns the list of execution occurrence on this lifeline. + * + * @return the execution occurrence list + */ + public List<GraphNode> getExecutions() { + if (hasChildren()) { + return getNodeMap().get(BasicExecutionOccurrence.EXEC_OCC_TAG); + } + return new ArrayList<>(); + } + + @Override + public boolean contains(int xValue, int yValue) { + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); + + if (fFrame == null) { + return false; + } + if (GraphNode.contains(x, y, width, height, xValue, yValue)) { + return true; + } + if (GraphNode.contains(x + Metrics.getLifelineWidth() / 2 - Metrics.EXECUTION_OCCURRENCE_WIDTH / 2, y + height, Metrics.EXECUTION_OCCURRENCE_WIDTH, (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * fFrame.getMaxEventOccurrence() + + Metrics.LIFELINE_VB_MAGIN - 4, xValue, yValue)) { + return true; + } + + height = Metrics.getLifelineFontHeigth() + 2 * Metrics.LIFELINE_HEARDER_TEXT_V_MARGIN; + int hMargin = (Metrics.LIFELINE_VT_MAGIN - height) / 2; + + if (hMargin >= 2) { + if (fFrame.getVisibleAreaY() < y - height - hMargin) { + if (GraphNode.contains(x - Metrics.LIFELINE_SPACING / 2 + 1, y - height - hMargin, Metrics.swimmingLaneWidth() - 2, height + 1, xValue, yValue)) { + return true; + } + } else { + if (GraphNode.contains(x - Metrics.LIFELINE_SPACING / 2 + 1, fFrame.getVisibleAreaY(), Metrics.swimmingLaneWidth() - 2, height, xValue, yValue)) { + return true; + } + } + } + if (getNodeAt(xValue, yValue) != null) { + return true; + } + return false; + } + + /** + * Returns the lifeline visibility for the given visible area + * + * @param vx The x coordinate of the visible area + * @param vy The y coordinate of the visible area + * @param vwidth The width of the visible area + * @param vheight The height of the visible area + * @return true if visible false otherwise + */ + @Override + public boolean isVisible(int vx, int vy, int vwidth, int vheight) { + int x = getX(); + int width = getWidth(); + if (((x >= vx) && (x <= vx + vwidth)) || ((x + width >= vx) && (x <= vx))) { + return true; + } + return false; + } + + /** + * Draws the name within the graphical context. + * + * @param context The graphical context. + */ + protected void drawName(IGC context) { + ISDPreferences pref = SDViewPref.getInstance(); + + int x = getX(); + int y = getY(); + int height = Metrics.getLifelineHeaderFontHeigth() + 2 * Metrics.LIFELINE_HEARDER_TEXT_V_MARGIN; + int hMargin = Metrics.LIFELINE_VT_MAGIN / 4;// (Metrics.LIFELINE_NAME_H_MARGIN)/2; + + context.setLineStyle(context.getLineSolidStyle()); + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_LIFELINE_HEADER)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_LIFELINE_HEADER)); + context.setFont(pref.getFont(ISDPreferences.PREF_LIFELINE_HEADER)); + if (hMargin >= 0) { + if (fFrame.getVisibleAreaY() < y - height - hMargin) { + context.fillRectangle(x - Metrics.LIFELINE_SPACING / 2 + 1, y - height - hMargin, Metrics.swimmingLaneWidth() - 2, height); + context.drawRectangle(x - Metrics.LIFELINE_SPACING / 2 + 1, y - height - hMargin, Metrics.swimmingLaneWidth() - 2, height); + context.setForeground(pref.getFontColor(ISDPreferences.PREF_LIFELINE_HEADER)); + context.drawTextTruncatedCentred(getName(), x + Metrics.LIFELINE_NAME_V_MARGIN - Metrics.LIFELINE_SPACING / 2 + 1, y - height - hMargin, Metrics.swimmingLaneWidth() - 2 * Metrics.LIFELINE_NAME_V_MARGIN - 2, height, true); + } else { + context.fillRectangle(x - Metrics.LIFELINE_SPACING / 2 + 1, fFrame.getVisibleAreaY(), Metrics.swimmingLaneWidth() - 2, height); + context.drawRectangle(x - Metrics.LIFELINE_SPACING / 2 + 1, fFrame.getVisibleAreaY(), Metrics.swimmingLaneWidth() - 2, height); + context.setForeground(pref.getFontColor(ISDPreferences.PREF_LIFELINE_HEADER)); + context.drawTextTruncatedCentred(getName(), x - Metrics.LIFELINE_SPACING / 2 + Metrics.LIFELINE_NAME_V_MARGIN + 1, fFrame.getVisibleAreaY(), Metrics.swimmingLaneWidth() - 2 * Metrics.LIFELINE_NAME_V_MARGIN - 2, height, true); + } + } + } + + /** + * Force the lifeline to be drawn at the given coordinate + * + * @param context - the context to draw into + * @param x - the x coordinate + * @param y - the y coordinate + */ + public void draw(IGC context, int x, int y) { + + ISDPreferences pref = SDViewPref.getInstance(); + + // Set the draw color depending if the lifeline must be selected or not + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + if (isSelected()) { + if (pref.useGradienColor()) { + context.setGradientColor(pref.getBackGroundColor(ISDPreferences.PREF_LIFELINE)); + } + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + } else { + if (pref.useGradienColor()) { + context.setGradientColor(pref.getBackGroundColor(ISDPreferences.PREF_LIFELINE)); + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_FRAME)); + } else { + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_LIFELINE)); + } + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_LIFELINE)); + } + // Store the lifeline coordinates to save some calls + int width = getWidth(); + int height = getHeight(); + + // Draw the rectangle which contain the lifeline name + if (pref.useGradienColor()) { + context.fillGradientRectangle(x, y, width, height / 2 - 7, true); + context.fillRectangle(x, y + height / 2 - 8, width, +height / 2 - 5); + context.fillGradientRectangle(x, y + height, width, -height / 2 + 6, true); + } else { + context.fillRectangle(x, y, width, height); + } + context.drawRectangle(x, y, width, height); + + if (fCategory >= 0) { + LifelineCategories[] categories = fFrame.getLifelineCategories(); + if (fCategory < categories.length) { + IImage image = categories[fCategory].getImage(); + if (image != null) { + context.drawImage(image, x, y, width, height); + } + } + } + + // Draw the lifeline label into the rectangle + // The label is truncated if it cannot fit + IColor temp = context.getForeground(); + context.setFont(pref.getFont(ISDPreferences.PREF_LIFELINE)); + context.setForeground(pref.getFontColor(ISDPreferences.PREF_LIFELINE)); + context.drawTextTruncatedCentred(getName(), x + Metrics.LIFELINE_NAME_V_MARGIN, y, Metrics.getLifelineWidth() - 2 * Metrics.LIFELINE_NAME_V_MARGIN, height, true); + + context.setLineStyle(context.getLineDashStyle()); + context.setForeground(temp); + int oldStyle = context.getLineStyle(); + + // Now draw the lifeline vertical line + // this line height depends on a stop assignment + // if there is no stop the line is drawn to the bottom of the frame + + // by default set the height to reach the frame bottom + int dashedLineEnd = y + height + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * fFrame.getMaxEventOccurrence() + Metrics.LIFELINE_VB_MAGIN; + /* + * if (stop != null) { dashedLineEnd = stop.getY(); } + */ + + if (isSelected()) { + context.setForeground(pref.getBackGroundColorSelection()); + context.setLineWidth(5); + context.drawLine(x + Metrics.getLifelineWidth() / 2, y + height, x + Metrics.getLifelineWidth() / 2, dashedLineEnd - 4); + context.setForeground(pref.getForeGroundColorSelection()); + } + + context.setLineWidth(Metrics.NORMAL_LINE_WIDTH); + context.drawLine(x + Metrics.getLifelineWidth() / 2, y + height, x + Metrics.getLifelineWidth() / 2, dashedLineEnd - 4); + context.drawLine(x + Metrics.getLifelineWidth() / 2, y + height, x + Metrics.getLifelineWidth() / 2, dashedLineEnd - 4); + context.setLineStyle(oldStyle); + + context.setLineStyle(context.getLineSolidStyle()); + + if (hasFocus()) { + drawFocus(context); + } + + super.drawChildenNodes(context); + } + + /** + * Draws the select execution occurrence region using the given color + * + * @param context the graphical context + * @param startEvent the region start + * @param nbEvent the region height + * @param color the color to use + */ + public void highlightExecOccurrenceRegion(IGC context, int startEvent, int nbEvent, IColor color) { + IColor backupColor = context.getBackground(); + context.setBackground(color); + int x = getX() + Metrics.getLifelineWidth() / 2 - Metrics.EXECUTION_OCCURRENCE_WIDTH / 2; + int y = getY() + getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * startEvent; + int width = Metrics.EXECUTION_OCCURRENCE_WIDTH; + int height = ((Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing())) * nbEvent; + context.fillRectangle(x, y, width, height); + context.setBackground(backupColor); + } + + @Override + public void draw(IGC context) { + draw(context, getX(), getY()); + } + + @Override + public String getArrayId() { + return LIFELINE_TAG; + } + + @Override + public boolean positiveDistanceToPoint(int x, int y) { + if (getX() > x - Metrics.swimmingLaneWidth()) { + return true; + } + return false; + } + + @Override + public GraphNode getNodeAt(int x, int y) { + int vy = 0; + int vh = 0; + if (getFrame() != null) { + vy = getFrame().getVisibleAreaY(); + vh = getFrame().getVisibleAreaHeight(); + } else { + return null; + } + if (getExecutions() == null) { + return null; + } + for (int i = getExecOccurrenceDrawIndex(); i < getExecutions().size(); i++) { + GraphNode node = getExecutions().get(i); + if (node.getHeight() < 0) { + if (node.getY() + node.getHeight() > vy + vh) { + break; + } + } else { + if (node.getY() > vy + vh) { + break; + } + } + if (node.contains(x, y)) { + GraphNode internal = node.getNodeAt(x, y); + if (internal != null) { + return internal; + } + return node; + } + } + return null; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/LifelineCategories.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/LifelineCategories.java new file mode 100755 index 0000000000..db1449caad --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/LifelineCategories.java @@ -0,0 +1,81 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IImage; + +/** + * <p> + * LifelineCategories is used to assign additional description for + * lifelines of the same type. This consists in providing a type name and an icon. + * The icon will be displayed in the rectangle which contains the lifeline name. + * The category name is only display in the lifeline tooltip. + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class LifelineCategories { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The category name + */ + private String fName = null; + /** + * The category image + */ + private IImage fCategoryImage = null; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Returns the category name. + * + * @return the category name + */ + public String getName() { + return fName; + } + + /** + * Sets the category name. + * + * @param string the name + */ + public void setName(String string) { + fName = string; + } + + /** + * Returns the category icon. + * + * @return the category icon + */ + public IImage getImage() { + return fCategoryImage; + } + + /** + * Sets the category icon. + * + * @param image the icon + */ + public void setImage(IImage image) { + fCategoryImage = image; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Metrics.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Metrics.java new file mode 100755 index 0000000000..57f6a13193 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Metrics.java @@ -0,0 +1,332 @@ +/********************************************************************** + * Copyright (c) 2005, 2012 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +/** + * This class contains the metrics used to layout a sequence diagram on a view The class method are mostly used in + * combination with the preferences + * + * @version 1.0 + * @author sveyrier + * + */ +public class Metrics { + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * Space between the Frame and the top of the View This also represent the space between the frame and the bottom of + * the View + */ + public static final int FRAME_H_MARGIN = 10; + /** + * Space between the Frame and the left of the View This also represent the space between the Frame and the right of + * the View + */ + public static final int FRAME_V_MARGIN = 10; + /** + * Space between the Lifelines and the right of the Frame + */ + public static final int LIFELINE_H_MAGIN = 23; + /** + * Space between the Lifelines and the bottom of the Frame + */ + public static final int LIFELINE_VB_MAGIN = 20; + /** + * Space between the Lifelines and the top of the Frame + */ + public static final int LIFELINE_VT_MAGIN = 30;// 18 + /** + * Vertical space between the lifeline name and the rectangle which contains that name This is only for the + * "always visible" lifeline name rectangle + */ + public static final int LIFELINE_HEARDER_TEXT_V_MARGIN = 4; + /** + * Vertical spacing between messages + */ + public static final int MESSAGES_SPACING = 30; + /** + * Vertical spacing between the message and its name + */ + public static final int MESSAGES_NAME_SPACING = 10; + /** + * Horizontal spacing between the Frame name and its containing rectangle + */ + public static final int FRAME_NAME_H_MARGIN = 4; + /** + * Vertical spacing between the Frame name and its containing rectangle + */ + public static final int FRAME_NAME_V_MARGIN = 8; + /** + * Horizontal spacing between the lifeline name and its containing rectangle + */ + public static final int LIFELINE_NAME_H_MARGIN = 14; + /** + * Vertical spacing between the lifeline name and its containing rectangle + */ + public static final int LIFELINE_NAME_V_MARGIN = 20; + /** + * Space between the rectangles which contain the Lifelines name + */ + public static final int LIFELINE_SPACING = 45; + /** + * The circle ray used to draw the circle which compose Found and Lost messages + */ + public static final int MESSAGE_CIRCLE_RAY = 5; + /** + * Execution occurrence vertical width + */ + public static final int EXECUTION_OCCURRENCE_WIDTH = 8; + /** + * The square width which contains the Stop representation (a cross) + */ + public static final int STOP_WIDTH = 20; + /** + * The internal message width. + */ + public static final int INTERNAL_MESSAGE_WIDTH = 20; + /** + * The internal sychrounous message height. + */ + public static final int SYNC_INTERNAL_MESSAGE_HEIGHT = 10; + /** + * Line width used when drawing selected GraphNode + */ + public static final int SELECTION_LINE_WIDTH = 5; + /** + * Line width used when drawing non selected GraphNode + */ + public static final int NORMAL_LINE_WIDTH = 1; + /** + * The internal vertical message margin + */ + public static final int INTERNAL_MESSAGE_V_MARGIN = 10; + + /** + * Used to sample the diagram. When the lifeline spacing is smaller than this constant when zooming out then less + * lifelines are displayed to avoid lifelines overlapping and mainly saving some execution time + */ + public static final int LIFELINE_SIGNIFICANT_HSPACING = 10; + /** + * Used to sample the diagram. When the message spacing is smaller than this constant when zooming out then less + * message are displayed to avoid message overlapping and mainly saving some execution time + */ + public static final int MESSAGE_SIGNIFICANT_VSPACING = 1; + /** + * Message selection tolerance. Used for internal syncMessages only + */ + public static final int MESSAGE_SELECTION_TOLERANCE = 30; + /** + * The focus drawing margin. + */ + public static final int FOCUS_DRAWING_MARGIN = 10; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The lifeline font height + */ + private static int fLifelineFontHeight = 0; + /** + * The message font height + */ + private static int fMessageFontHeight = 0; + /** + * The frame font height + */ + private static int fFrameFontHeight = 0; + /** + * The lifeline header font height + */ + private static int fLifelineHeaderFontHeight = 0; + /** + * The lifeline font widht + */ + private static int fLifelineFontWidth = 0; + /** + * The lifeline width + */ + private static int fLifeLineWidth = 119; + /** + * The (forced) event spacing + */ + private static int fForcedEventSpacing = -1; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + /** + * Constructor + * + * Hide private constructor + */ + private Metrics() { + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Set the character height used to draw the lifeline name + * + * @param height the character height + */ + static public void setLifelineFontHeight(int height) { + fLifelineFontHeight = height; + } + + /** + * Set the character width used to draw the lifeline name + * + * @param width the character width + */ + static public void setLifelineFontWidth(int width) { + fLifelineFontWidth = width; + } + + /** + * Set the character height used to draw the message name + * + * @param fontHeight the character height + */ + static public void setMessageFontHeight(int fontHeight) { + fMessageFontHeight = fontHeight; + } + + /** + * Returns the character height used to draw the lifeline name + * + * @return the character height + */ + static public int getFrameFontHeigth() { + return fFrameFontHeight; + } + + /** + * Set the character height used to draw the message name + * + * @param fontHeight the character height + */ + static public void setFrameFontHeight(int fontHeight) { + fFrameFontHeight = fontHeight; + } + + /** + * Returns the character height used to draw the lifeline name + * + * @return the character height + */ + static public int getLifelineHeaderFontHeigth() { + return fLifelineHeaderFontHeight; + } + + /** + * Set the character height used to draw the message name + * + * @param fontHeight the character height + */ + static public void setLifelineHeaderFontHeight(int fontHeight) { + fLifelineHeaderFontHeight = fontHeight; + } + + /** + * Returns the character height used to draw the lifeline name + * + * @return the character height + */ + static public int getLifelineFontHeigth() { + return fLifelineFontHeight; + } + + /** + * Returns the character height used to draw the message name + * + * @return the character height + */ + static public int getMessageFontHeigth() { + if (fForcedEventSpacing >= 0) { + return 0; + } + return fMessageFontHeight; + } + + /** + * This is the vertical space used by a Lifeline (mostly the rectangle which contain its name) + * + * @return the vertical space used by a Lifeline + */ + static public int getLifelineWidth() { + return fLifeLineWidth; + } + + /** + * Set the vertical space used by a Lifeline (mostly the rectangle which contain its name) + * + * @param value the vertical space + */ + static public void setLifelineWidth(int value) { + fLifeLineWidth = value; + } + + /** + * Returns the swimming lane width + * + * @return the swimming lane width + */ + static public int swimmingLaneWidth() { + return getLifelineWidth() + LIFELINE_SPACING; + } + + /** + * Returns the character width used to draw the Lifelines name + * + * @return the average character width + */ + static public int getAverageCharWidth() { + return fLifelineFontWidth; + } + + /** + * Returns the message spacing. + * + * @return the message spacing + */ + static public int getMessagesSpacing() { + if (fForcedEventSpacing >= 0) { + return fForcedEventSpacing; + } + return MESSAGES_SPACING; + } + + /** + * Sets the forced event spacing value . + * + * @param eventSpacing + * The spacing value + */ + static public void setForcedEventSpacing(int eventSpacing) { + fForcedEventSpacing = eventSpacing; + } + + /** + * Gets the forced event spacing value. + * + * @return forcedEventSpacing + */ + static public int getForcedEventSpacing() { + return fForcedEventSpacing; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SDTimeEvent.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SDTimeEvent.java new file mode 100755 index 0000000000..3b7dbd807f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SDTimeEvent.java @@ -0,0 +1,91 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; + +/** + * Class implementation of a sequence diagram time event. + * + * @version 1.0 + * @author sveyrier + * + */ +public class SDTimeEvent { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The time stamp of the event + */ + private final ITmfTimestamp fTimestamp; + /** + * The event index. + */ + private final int fEvent; + /** + * The time range implementing node. + */ + private final ITimeRange fNode; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * The default constructor. + * + * @param time The time stamp of the event. + * @param event The event index. + * @param node The time range implementing node. + * @since 2.0 + */ + public SDTimeEvent(ITmfTimestamp time, int event, ITimeRange node) { + fTimestamp = time; + fEvent = event; + fNode = node; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + /** + * Returns the timestamp of the event. + * + * @return the timestamp of the event. + * @since 2.0 + */ + public ITmfTimestamp getTime() { + return fTimestamp; + } + + /** + * Returns the event index. + * + * @return the event index. + */ + public int getEvent() { + return fEvent; + } + + /** + * Returns the time range implementing node. + * + * @return the time range implementing node. + */ + public ITimeRange getGraphNode() { + return fNode; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Stop.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Stop.java new file mode 100755 index 0000000000..c795fb87c1 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/Stop.java @@ -0,0 +1,167 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * <p> + * It is the UML2 stop graphical representation in the sequence diagram viewer. + * This draw a cross on the lifeline. The stop y coordinate depend on the event occurrence when it appears. + * A stop is never drawn it is assigned to a lifeline. + * </p> + * + * @version 1.0 + * @author sveyrier + */ +public class Stop extends GraphNode { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The graphNode ID + */ + public static final String STOP = "STOP"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The owning lifeline on which the stop appears + */ + private Lifeline fLifeline = null; + /** + * This basically represents the time when the stop occurs on the owning Lifeline + * + * @see Lifeline Lifeline for more event occurence details + */ + private int fEventOccurrence = 0; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int getX() { + if (fLifeline == null) { + return 0; + } + return fLifeline.getX() + Metrics.getLifelineWidth() / 2 - Metrics.STOP_WIDTH / 2; + } + + @Override + public int getY() { + if (fLifeline == null) { + return 0; + } + return fLifeline.getY() + fLifeline.getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * fEventOccurrence - Metrics.STOP_WIDTH / 2; + } + + @Override + public int getWidth() { + if (fLifeline == null) { + return 0; + } + return Metrics.STOP_WIDTH; + } + + @Override + public int getHeight() { + if (fLifeline == null) { + return 0; + } + return Metrics.STOP_WIDTH; + } + + /** + * Set the lifeline on which the stop must be draw + * + * @param theLifeline The the stop owing lifeline + */ + public void setLifeline(Lifeline theLifeline) { + fLifeline = theLifeline; + } + + /** + * Get the lifeline on which the stop must be draw + * + * @return the the stop owing lifeline + * @since 2.0 + */ + public Lifeline getLifeline() { + return fLifeline; + } + + /** + * Get the event occurrence when this stop appears + * + * @return the eventOccurence to assign to the stop + * @since 2.0 + */ + public int getEventOccurrence() { + return fEventOccurrence; + } + + /** + * Set the event occurrence when this stop appears + * + * @param occurrence the eventOccurence to assign to the stop + */ + public void setEventOccurrence(int occurrence) { + fEventOccurrence = occurrence; + } + + @Override + public void draw(IGC context) { + + ISDPreferences pref = SDViewPref.getInstance(); + + // Set the appropriate color depending if the graph node if selected or not + if (fLifeline.isSelected()) { + context.setForeground(pref.getBackGroundColorSelection()); + context.setLineWidth(Metrics.SELECTION_LINE_WIDTH); + int lastWidth = context.getLineWidth(); + context.setLineWidth(9); + // Draw a cross on the lifeline + context.drawLine(getX(), getY(), getX() + getWidth(), getY() + getHeight()); + context.drawLine(getX() + getWidth(), getY(), getX(), getY() + getHeight()); + // restore the context + context.setLineWidth(lastWidth); + context.setBackground(pref.getBackGroundColorSelection()); + context.setForeground(pref.getForeGroundColorSelection()); + } else { + context.setBackground(pref.getBackGroundColor(ISDPreferences.PREF_LIFELINE)); + context.setForeground(pref.getForeGroundColor(ISDPreferences.PREF_LIFELINE)); + } + int lastWidth = context.getLineWidth(); + context.setLineWidth(3); + // Draw a cross on the lifeline + context.drawLine(getX(), getY(), getX() + getWidth(), getY() + getHeight()); + context.drawLine(getX() + getWidth(), getY(), getX(), getY() + getHeight()); + // restore the context + context.setLineWidth(lastWidth); + } + + @Override + public String getArrayId() { + return STOP; + } + + @Override + public boolean contains(int x, int y) { + return false; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SyncMessage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SyncMessage.java new file mode 100755 index 0000000000..04b8394030 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SyncMessage.java @@ -0,0 +1,296 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import java.util.Comparator; + +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.SortSyncMessageComparator; + +/** + * A SyncMessage is a synchronous message which appear at the same event occurrence on both lifeline ends (sender and + * receiver).<br> + * A Sync message is usually drawn horizontally.<br> + * <br> + * <br> + * Usage example: + * + * <pre> + * Frame frame; + * Lifeline lifeLine1; + * Lifeline lifeLine2; + * + * SyncMessage message = new SyncMessage(); + * // Create a new event occurrence on each lifeline + * lifeline1.getNewOccurrenceIndex(); + * lifeline2.getNewOccurrenceIndex(); + * // Set the message sender and receiver + * message.setStartLifeline(lifeLine1); + * message.setEndLifline(lifeline2); + * message.setName("Message label"); + * // add the message to the frame + * frame.addMessage(message); + * </pre> + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline Lifeline for more event occurence details + * @version 1.0 + * @author sveyrier + * + */ +public class SyncMessage extends BaseMessage implements ITimeRange { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The graphNode ID + */ + public static final String SYNC_MESS_TAG = "SyncMessage"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The associated message return + */ + private SyncMessageReturn fMessageReturn; + /** + * The time when the message occurs + */ + private ITmfTimestamp fEventTime = new TmfTimestamp(); + /** + * Flag whether the message has time information available or not + */ + private boolean fHasTimeInfo = false; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor + */ + public SyncMessage() { + setColorPrefId(ISDPreferences.PREF_SYNC_MESS); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Ensure both lifelines have the same event occurrence (the greater found on each lifeline) + */ + protected void syncLifelinesEventOccurrence() { + if ((getStartLifeline() != null) && (getEndLifeline() != null)) { + int newIndex = 0; + if (getStartLifeline().getEventOccurrence() > getEndLifeline().getEventOccurrence()) { + newIndex = getStartLifeline().getEventOccurrence(); + } else { + newIndex = getEndLifeline().getEventOccurrence(); + } + getStartLifeline().setCurrentEventOccurrence(newIndex); + getEndLifeline().setCurrentEventOccurrence(newIndex); + setEventOccurrence(getStartLifeline().getEventOccurrence()); + } + } + + /** + * Set the lifeLine from which the message has been sent.<br> + * A new event occurrence will be created on this lifeLine.<br> + * SyncMessage must occur at the same event occurrence on both lifeline, this method is responsible to synchronize the + * event occurrence on each lifeline (the greater value will be used).<br> + * This synchronization is only done if the end lifeline has already been set. + * + * @param lifeline the message sender + */ + public void autoSetStartLifeline(Lifeline lifeline) { + lifeline.getNewEventOccurrence(); + setStartLifeline(lifeline); + } + + /** + * Set the lifeLine which has receiver the message.<br> + * A new EventOccurence will be create on this lifeLine.<br> + * SyncMessage must occur at the same event occurrence on both lifeline, this method is responsible to synchronize the + * event occurrence on each lifeline (the greater value will be used).<br> + * This synchronization is only done if the start lifeline has already been set. + * + * @param lifeline the message receiver + */ + public void autoSetEndLifeline(Lifeline lifeline) { + lifeline.getNewEventOccurrence(); + setEndLifeline(lifeline); + } + + /** + * Set the lifeLine which has receiver the message.<br> + * SyncMessage must occur at the same event occurrence on both lifeline, this method is responsible to synchronize the + * event occurrence on each lifeline (the greater value will be used).<br> + * This synchronization is only done if the start lifeline has already been set. + * + * @param lifeline the message receiver + */ + @Override + public void setStartLifeline(Lifeline lifeline) { + super.setStartLifeline(lifeline); + if ((getEndLifeline() == null)) { + setEventOccurrence(getStartLifeline().getEventOccurrence()); + } else { + syncLifelinesEventOccurrence(); + } + } + + /** + * Set the lifeLine which has receiver the message.<br> + * SyncMessage must occur at the same event occurrence on both lifelines, this method is responsible to synchronize the + * event occurrence on each lifeline (the greater value will be used).<br> + * This synchronization is only done if the start lifeline has already been set. + * + * @param lifeline the message receiver + */ + @Override + public void setEndLifeline(Lifeline lifeline) { + super.setEndLifeline(lifeline); + if ((getStartLifeline() == null)) { + setEventOccurrence(getEndLifeline().getEventOccurrence()); + } else { + syncLifelinesEventOccurrence(); + } + } + + /** + * Set the event occurrence when this message occurs.<br> + * + * @param occurrence the event occurrence to assign to this message.<br> + * @see Lifeline Lifeline for more event occurence details + */ + @Override + protected void setEventOccurrence(int occurrence) { + setStartOccurrence(occurrence); + setEndOccurrence(occurrence); + } + + /** + * Set the message return associated with this message. + * + * @param message the message return to associate + */ + protected void setMessageReturn(SyncMessageReturn message) { + fMessageReturn = message; + } + + /** + * Returns the syncMessageReturn associated to this syncMessage + * + * @return the message return + */ + public SyncMessageReturn getMessageReturn() { + return fMessageReturn; + } + + /** + * Set the time when the message occurs + * + * @param time the time when the message occurs + * @since 2.0 + */ + public void setTime(ITmfTimestamp time) { + fEventTime = time; + fHasTimeInfo = true; + if (getStartLifeline() != null && getStartLifeline().getFrame() != null) { + getStartLifeline().getFrame().setHasTimeInfo(true); + } else if (getEndLifeline() != null && getEndLifeline().getFrame() != null) { + getEndLifeline().getFrame().setHasTimeInfo(true); + } + } + + /** + * @since 2.0 + */ + @Override + public ITmfTimestamp getEndTime() { + return fEventTime; + } + + /** + * @since 2.0 + */ + @Override + public ITmfTimestamp getStartTime() { + return fEventTime; + } + + @Override + public boolean hasTimeInfo() { + return fHasTimeInfo; + } + + @Override + public void draw(IGC context) { + if (!isVisible()) { + return; + } + + ISDPreferences pref = SDViewPref.getInstance(); + + // Draw it selected? + if (!isSelected()) { + context.setBackground(pref.getBackGroundColor(getColorPrefId())); + context.setForeground(pref.getForeGroundColor(getColorPrefId())); + } + super.draw(context); + } + + @Override + public boolean isVisible(int x, int y, int width, int height) { + if (getY() > y + height + + // take into account the message name drawn above the arrow + Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth()) { + return false; + } + + // UML2 lost/found message visibility special case + // Others visibility cases are perform in the ***common*** case + if ((getEndLifeline() == null && getStartLifeline() != null) || (getEndLifeline() != null && getStartLifeline() == null)) { + if (x + width > getX() + getWidth() && x < getX() + getWidth()) { + return true; + } + } + // ***Common*** syncMessages visibility + return super.isVisible(x, y, width, height); + } + + @Override + public Comparator<GraphNode> getComparator() { + return new SortSyncMessageComparator(); + } + + @Override + public String getArrayId() { + return SYNC_MESS_TAG; + } + + @Override + public boolean positiveDistanceToPoint(int x, int y) { + if (getY() > y) { + return true; + } + return false; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SyncMessageReturn.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SyncMessageReturn.java new file mode 100755 index 0000000000..c3b0f0804c --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/core/SyncMessageReturn.java @@ -0,0 +1,112 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref; + +/** + * The message return graph node implementation.<br> + * This class differs on the SynMessage class only on the drawing line style (dashed instead of plain line).<br> + * Message return are generally associated to a message. This means, they are connected to the same lifelines than the + * associated message but in the opposite direction and for a different event occurrence.<br> + * <br> + * WARNING: The association validity is not checked, it is not necessary to provide a valid association, not even needed + * to set an association to drawn a message with a message return style.<br> + * + * + * @see org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessage SyncMessage for usage example + * @version 1.0 + * @author sveyrier + * + */ +public class SyncMessageReturn extends SyncMessage { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The graphNode ID + */ + public static final String SYNC_MESS_RET_TAG = "SyncMessageRet"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The associated message(the message it is the return). + */ + private SyncMessage fMessage = null; + + // ------------------------------------------------------------------------ + // Constractors + // ------------------------------------------------------------------------ + + /** + * Default constructor + */ + public SyncMessageReturn() { + setColorPrefId(ISDPreferences.PREF_SYNC_MESS_RET); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + /** + * Set the associated message (the message it is the return).<br> + * Setting the association will activate the navigation in the default sequence diagram implementation to the + * message when the user right click on this message return.<br> + * + * @param parentMessage the message to associate + */ + public void setMessage(SyncMessage parentMessage) { + fMessage = parentMessage; + fMessage.setMessageReturn(this); + } + + /** + * Returns the syncMessage associated to this SyncMessageReturn + * + * @return the associated message + */ + public SyncMessage getMessage() { + return fMessage; + } + + @Override + public void draw(IGC context) { + if (!isVisible()) { + return; + } + + ISDPreferences pref = SDViewPref.getInstance(); + + int oldStyle = context.getLineStyle(); + // Message return are dashed + context.setLineStyle(context.getLineDotStyle()); + // Draw it selected? + if (!isSelected()) { + context.setBackground(pref.getBackGroundColor(getColorPrefId())); + context.setForeground(pref.getForeGroundColor(getColorPrefId())); + } + super.draw(context); + // restore the context + context.setLineStyle(oldStyle); + } + + @Override + public String getArrayId() { + return SYNC_MESS_RET_TAG; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/Criteria.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/Criteria.java new file mode 100755 index 0000000000..7c2a304f3b --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/Criteria.java @@ -0,0 +1,415 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFilterProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDGraphNodeSupporter; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * This class describes the find or filter criteria selected by the user in the find or filter dialog box + * + * @version 1.0 + * @author sveyrier + * @author Bernd Hufmann + */ +public class Criteria { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * Flag whether lifeline is selected or not. + */ + private boolean fLifeLineSelected = false; + /** + * Flag whether synchronous message is selected or not. + */ + private boolean fSyncMessageSelected = false; + /** + * Flag whether synchronous message return is selected or not. + */ + private boolean fSyncMessageReturnSelected = false; + /** + * Flag whether asynchronous message is selected or not. + */ + private boolean fAsyncMessageSelected = false; + /** + * Flag whether asynchronous message return is selected or not. + */ + private boolean fAsyncMessageReturnSelected = false; + /** + * Flag whether case sensitive find is required or not. + */ + private boolean fCaseSenstiveSelected = false; + /** + * Flag whether stop graph node is selected or not. + */ + private boolean fStopSelected = false; + /** + * The find expression. + */ + private String fExpression = null; + /** + * The find pattern as regular expression. + */ + private Pattern pattern = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor + */ + public Criteria () { + } + + /** + * Copy constructor + * + * @param other Criteria to create new criteria + */ + public Criteria (Criteria other) { + this.fLifeLineSelected = other.fLifeLineSelected; + this.fSyncMessageSelected = other.fSyncMessageSelected; + this.fSyncMessageReturnSelected = other.fSyncMessageReturnSelected; + this.fAsyncMessageSelected = other.fAsyncMessageSelected; + this.fAsyncMessageReturnSelected = other.fAsyncMessageReturnSelected; + this.fCaseSenstiveSelected = other.fCaseSenstiveSelected; + this.fStopSelected = other.fStopSelected; + fExpression = other.fExpression; + updatePattern(); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Returns true if the AsyncMessageReturn is selected, false otherwise. + * + * @return true if the AsyncMessageReturn is selected, false otherwise + */ + public boolean isAsyncMessageReturnSelected() { + return fAsyncMessageReturnSelected; + } + + /** + * Returns true if the AsyncMessage is selected, false otherwise. + * + * @return true if the AsyncMessage is selected, false otherwise + */ + public boolean isAsyncMessageSelected() { + return fAsyncMessageSelected; + } + + /** + * Returns the text enter by the user. + * + * @return the expression text + */ + public String getExpression() { + return fExpression; + } + + /** + * Returns the regular expression pattern. + * + * @return the regular expression pattern + */ + public Pattern getPattern() { + return pattern; + } + + /** + * Sets the regular expression pattern. + * + * @param pattern + * The pattern to set + */ + public void setPattern(Pattern pattern) { + this.pattern = pattern; + } + + /** + * Returns true if the LifeLine is selected, false otherwise. + * + * @return true if the LifeLine is selected, false otherwise + */ + public boolean isLifeLineSelected() { + return fLifeLineSelected; + } + + /** + * Returns true if the Stop is selected, false otherwise. + * + * @return true if the Stop is selected, false otherwise + */ + public boolean isStopSelected() { + return fStopSelected; + } + + /** + * Returns true if the SyncMessageReturn is selected, false otherwise. + * + * @return true if the SyncMessageReturn is selected, false otherwise + */ + public boolean isSyncMessageReturnSelected() { + return fSyncMessageReturnSelected; + } + + /** + * Returns true if the SyncMessage is selected, false otherwise. + * + * @return true if the SyncMessage is selected, false otherwise + */ + public boolean isSyncMessageSelected() { + return fSyncMessageSelected; + } + + /** + * Sets the AsyncMessageReturn selection state. + * + * @param b true if selected, false otherwise + */ + public void setAsyncMessageReturnSelected(boolean b) { + fAsyncMessageReturnSelected = b; + } + + /** + * Sets the AsyncMessage selection state. + * + * @param b true if selected, false otherwise + */ + public void setAsyncMessageSelected(boolean b) { + fAsyncMessageSelected = b; + } + + /** + * Sets the text entered by the user and compiles the regular expression. + * + * @param string the text + */ + public void setExpression(String string) { + fExpression = string; + updatePattern(); + } + + /** + * Sets the Stop selection state. + * + * @param b true if selected, false otherwise + */ + public void setLifeLineSelected(boolean b) { + fLifeLineSelected = b; + } + + /** + * Set Stop selection state. + * + * @param b true if selected, false otherwise + */ + public void setStopSelected(boolean b) { + fStopSelected = b; + } + + /** + * Sets the SyncMessageReturn selection state. + * + * @param b true if selected, false otherwise + */ + public void setSyncMessageReturnSelected(boolean b) { + fSyncMessageReturnSelected = b; + } + + /** + * Sets the SyncMessage selection state. + * + * @param b true if selected, false otherwise + */ + public void setSyncMessageSelected(boolean b) { + fSyncMessageSelected = b; + } + + /** + * Returns true if the case sensitive is selected, false otherwise. + * + * @return true if the case sensitive is selected, false otherwise + */ + public boolean isCaseSenstiveSelected() { + return fCaseSenstiveSelected; + } + + /** + * Set case sensitive selection state. + * + * @param b true if selected, false otherwise + */ + public void setCaseSenstiveSelected(boolean b) { + fCaseSenstiveSelected = b; + // Make sure that pattern is set + setExpression(fExpression); + } + + /** + * Compares this criteria with a given criteria. + * + * @param to The criteria to compare + * @return usual comparison result (< 0, 0, > 0) + */ + public boolean compareTo(Criteria to) { + boolean retVal = true; + if (getExpression() != null) { + retVal = getExpression().equals(to.getExpression()); + } else if (to.getExpression() != null) { + retVal = to.getExpression().equals(getExpression()); + } + return retVal && isCaseSenstiveSelected() == to.isCaseSenstiveSelected() && isAsyncMessageReturnSelected() == to.isAsyncMessageReturnSelected() && isAsyncMessageSelected() == to.isAsyncMessageSelected() + && isLifeLineSelected() == to.isLifeLineSelected() && isStopSelected() == to.isStopSelected() && isSyncMessageReturnSelected() == to.isSyncMessageReturnSelected() && isSyncMessageSelected() == to.isSyncMessageSelected(); + } + + /** + * Saves current criteria attributes in the dialog settings. + * + * @param settings The dialog settings + */ + public void save(DialogSettings settings) { + settings.put("expression", getExpression()); //$NON-NLS-1$ + settings.put("isCaseSenstiveSelected", isCaseSenstiveSelected()); //$NON-NLS-1$ + settings.put("isAsyncMessageReturnSelected", isAsyncMessageReturnSelected()); //$NON-NLS-1$ + settings.put("isAsyncMessageSelected", isAsyncMessageSelected()); //$NON-NLS-1$ + settings.put("isLifeLineSelected", isLifeLineSelected()); //$NON-NLS-1$ + settings.put("isStopSelected", isStopSelected()); //$NON-NLS-1$ + settings.put("isSyncMessageReturnSelected", isSyncMessageReturnSelected()); //$NON-NLS-1$ + settings.put("isSyncMessageSelected", isSyncMessageSelected()); //$NON-NLS-1$ + } + + /** + * Loads the criteria with values of the dialog settings. + * + * @param settings The dialog settings + */ + public void load(DialogSettings settings) { + setExpression(settings.get("expression")); //$NON-NLS-1$ + setCaseSenstiveSelected(settings.getBoolean("isCaseSenstiveSelected")); //$NON-NLS-1$ + setAsyncMessageReturnSelected(settings.getBoolean("isAsyncMessageReturnSelected")); //$NON-NLS-1$ + setAsyncMessageSelected(settings.getBoolean("isAsyncMessageSelected")); //$NON-NLS-1$ + setLifeLineSelected(settings.getBoolean("isLifeLineSelected")); //$NON-NLS-1$ + setStopSelected(settings.getBoolean("isStopSelected")); //$NON-NLS-1$ + setSyncMessageReturnSelected(settings.getBoolean("isSyncMessageReturnSelected")); //$NON-NLS-1$ + setSyncMessageSelected(settings.getBoolean("isSyncMessageSelected")); //$NON-NLS-1$ + } + + /** + * Gets the summary of supported graph nodes. + * + * @param provider A filter provider + * @param loaderClassName A class loader + * @return graph node summary + */ + public String getGraphNodeSummary(ISDFilterProvider provider, String loaderClassName) { + ArrayList<String> list = new ArrayList<>(); + + if (provider != null) { + if (isLifeLineSelected()) { + list.add(provider.getNodeName(ISDGraphNodeSupporter.LIFELINE, loaderClassName)); + } + if (isSyncMessageSelected()) { + list.add(provider.getNodeName(ISDGraphNodeSupporter.SYNCMESSAGE, loaderClassName)); + } + if (isSyncMessageReturnSelected()) { + list.add(provider.getNodeName(ISDGraphNodeSupporter.SYNCMESSAGERETURN, loaderClassName)); + } + if (isAsyncMessageSelected()) { + list.add(provider.getNodeName(ISDGraphNodeSupporter.ASYNCMESSAGE, loaderClassName)); + } + if (isAsyncMessageReturnSelected()) { + list.add(provider.getNodeName(ISDGraphNodeSupporter.ASYNCMESSAGERETURN, loaderClassName)); + } + if (isStopSelected()) { + list.add(provider.getNodeName(ISDGraphNodeSupporter.STOP, loaderClassName)); + } + } else { + if (isLifeLineSelected()) { + list.add(Messages.SequenceDiagram_Lifeline); + } + if (isSyncMessageSelected()) { + list.add(Messages.SequenceDiagram_SynchronousMessage); + } + if (isSyncMessageReturnSelected()) { + list.add(Messages.SequenceDiagram_SynchronousMessageReturn); + } + if (isAsyncMessageSelected()) { + list.add(Messages.SequenceDiagram_AsynchronousMessage); + } + if (isAsyncMessageReturnSelected()) { + list.add(Messages.SequenceDiagram_AsynchronousMessageReturn); + } + if (isStopSelected()) { + list.add(Messages.SequenceDiagram_Stop); + } + } + StringBuffer ret = new StringBuffer(); + String prefix = "["; //$NON-NLS-1$ + for (Iterator<String> i = list.iterator(); i.hasNext();) { + String s = i.next(); + ret.append(prefix); + ret.append(s); + prefix = " " + Messages.SequenceDiagram_or + " "; //$NON-NLS-1$ //$NON-NLS-2$ + } + ret.append("]"); //$NON-NLS-1$ + return ret.toString(); + } + + /** + * Matches given string using compiled pattern based on user expression. + * + * @param stringToMatch A string to match + * @return true if string matches expression + */ + public boolean matches(String stringToMatch) { + if (pattern == null) { + return false; + } + return pattern.matcher(stringToMatch).matches(); + } + + /** + * Updates the regular expression pattern based on the expression. + */ + private void updatePattern() { + if (fExpression != null) { + try { + if (fCaseSenstiveSelected) { + pattern = Pattern.compile(fExpression); + } + else { + pattern = Pattern.compile(fExpression, Pattern.CASE_INSENSITIVE); + } + } catch (PatternSyntaxException e) { + pattern = null; + } + } + else { + pattern = null; + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/FilterCriteria.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/FilterCriteria.java new file mode 100755 index 0000000000..91ec6e876c --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/FilterCriteria.java @@ -0,0 +1,273 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.dialogs.DialogSettings; + +/** + * A filter criteria is a criteria that can be activated or not, positive or not. + * + * @version 1.0 + * @author sveyrier + * + */ +public class FilterCriteria { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The filter state value for 'active'. + */ + protected static final String ACTIVE = "active"; //$NON-NLS-1$ + /** + * The property value for positive filter. + */ + protected static final String POSITIVE = "positive"; //$NON-NLS-1$ + /** + * The filter loader class name property. + */ + protected static final String LOADERCLASSNAME = "loaderClassName"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The criteria reference. + */ + private Criteria fCriteria; + /** + * Flag whether this criteria is active or not + */ + private boolean fIsActive; + /** + * Flag whether this criteria is for positive filter or not + */ + private boolean fIsPositive; + /** + * The loader class name. + */ + private String fLoaderClassName; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + /** + * Standard constructor + * + * @param criteria A criteria reference + * @param isActive <code>true</code> if filter criteria is active else <code>false</code> + * @param isPositive <code>true</code> for positive filter else <code>false</code> + */ + public FilterCriteria(Criteria criteria, boolean isActive, boolean isPositive) { + this(criteria, isActive, isPositive, null); + } + + /** + * Constructor + * + * @param criteria A criteria reference + * @param isActive <code>true</code> if filter criteria is active else <code>false</code> + * @param isPositive <code>true</code> for positive filter else <code>false</code> + * @param loaderClassName A loader class name + */ + public FilterCriteria(Criteria criteria, boolean isActive, boolean isPositive, String loaderClassName) { + fCriteria = criteria; + fIsActive = isActive; + fIsPositive = isPositive; + fLoaderClassName = loaderClassName; + } + + /** + * Copy Constructor + * @param other FilterCriteria + */ + public FilterCriteria (FilterCriteria other) { + fCriteria = new Criteria(other.fCriteria); + fIsActive = other.fIsActive; + fIsPositive = other.fIsPositive; + fLoaderClassName = other.fLoaderClassName; + } + + /** + * Default constructor + */ + protected FilterCriteria() { + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(super.toString()); + sb.append(':'); + if (fCriteria != null) { + sb.append(" expression=");sb.append(fCriteria.getExpression()); //$NON-NLS-1$ + sb.append(" active=");sb.append(fIsActive); //$NON-NLS-1$ + sb.append(" positive=");sb.append(fIsPositive); //$NON-NLS-1$ + } else { + sb.append("empty criteria"); //$NON-NLS-1$ + } + return sb.toString(); + } + + /** + * Sets a criteria reference. + * @param criteria A criteria reference + */ + public void setCriteria(Criteria criteria) { + fCriteria = criteria; + } + + /** + * Returns the criteria reference. + * + * @return the criteria reference + */ + public Criteria getCriteria() { + return fCriteria; + } + + /** + * Sets the active flag. + * + * @param isActive A active value. + */ + public void setActive(boolean isActive) { + fIsActive = isActive; + } + + /** + * Returns whether filter criteria is active or not. + * + * @return whether filter criteria is active or not. + */ + public boolean isActive() { + return fIsActive; + } + + /** + * Sets filter is for positive filtering or not. + * + * @param isPositive The value to set. + */ + public void setPositive(boolean isPositive) { + fIsPositive = isPositive; + } + + /** + * Returns whether the filter si for positive filtering or not. + * + * @return Returns the positive. + */ + public boolean isPositive() { + return fIsPositive; + } + + /** + * Sets the loader class name for this filter. + * + * @param loaderClassName The loader class name to set + */ + public void setLoaderClassName(String loaderClassName) { + fLoaderClassName = loaderClassName; + } + + /** + * Returns the class loader name. + * + * @return the class loader name. + */ + public String getLoaderClassName() { + return fLoaderClassName; + } + + /** + * Finds a filter criteria within a list of criteria. + * + * @param what The filter to find + * @param list A list of filter criteria + * @return The found filter criteria or null + */ + public static FilterCriteria find(FilterCriteria what, List<FilterCriteria> list) { + if (what != null && list != null) { + try { + for (Iterator<FilterCriteria> i = list.iterator(); i.hasNext();) { + FilterCriteria fc = i.next(); + if (what.compareTo(fc)) { + return fc; + } + } + } catch (Exception e) { + // Silence + } + } + return null; + } + + /** + * Compares this filter criteria with a given criteria. + * + * @param to The filter criteria to compare. + * @return usual comparison result (< 0, 0, > 0) + */ + public boolean compareTo(FilterCriteria to) { + if (isPositive() == to.isPositive() && getCriteria().compareTo(to.getCriteria())) { + if (getLoaderClassName() == null && to.getLoaderClassName() == null) { + return true; + } + if ((getLoaderClassName() != null && to.getLoaderClassName() != null) && getLoaderClassName().equals(to.getLoaderClassName())) { + return true; + } + } + return false; + } + + /** + * Saves current criteria attributes in the dialog settings. + * + * @param settings The dialog settings + */ + public void save(DialogSettings settings) { + settings.put(ACTIVE, isActive()); + settings.put(POSITIVE, isPositive()); + if (getLoaderClassName() != null) { + settings.put(LOADERCLASSNAME, getLoaderClassName()); + } else { + settings.put(LOADERCLASSNAME, ""); //$NON-NLS-1$ + } + if (fCriteria != null) { + fCriteria.save(settings); + } + } + + /** + * Loads the criteria with values of the dialog settings. + * + * @param settings The dialog settings + */ + public void load(DialogSettings settings) { + setActive(settings.getBoolean(ACTIVE)); + setPositive(settings.getBoolean(POSITIVE)); + String loaderClassName = settings.get(LOADERCLASSNAME); + setLoaderClassName(loaderClassName != null && loaderClassName.length() > 0 ? loaderClassName : null); + if (fCriteria != null) { + fCriteria.load(settings); + } + } +}
\ No newline at end of file diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/FilterListDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/FilterListDialog.java new file mode 100755 index 0000000000..a905c391b6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/FilterListDialog.java @@ -0,0 +1,518 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFilterProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; +import org.eclipse.ui.IViewPart; + +/** + * This is the filters list dialog.<br> + * It is associated to an SDView and to a ISDFilterProvider.<br> + * + * @version 1.0 + * @author sveyrier + */ +public class FilterListDialog extends Dialog { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * Filter list criteria property name + */ + protected static final String FILTERS_LIST_CRITERIA = "filtersListsCriteria"; //$NON-NLS-1$ + /** + * Filter list size property name + */ + protected static final String FILTERS_LIST_SIZE = "filtersListSize"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The viewer and provided are kept here as attributes + */ + private final IViewPart fViewer; + /** + * The filter provider implementation + */ + private final ISDFilterProvider fProvider; + /** + * The filters are the result of editing this list + */ + private List<FilterCriteria> fFilters; + /** + * The add button. + */ + private Button fAdd; + /** + * The remove button. + */ + private Button fRemove; + /** + * The edit button. + */ + private Button fEdit; + /** + * The table with list of filters. + */ + private Table fTable; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Standard constructor + * + * @param view The view reference + * @param loader The filter provider implementation + */ + public FilterListDialog(IViewPart view, ISDFilterProvider loader) { + super(view.getSite().getShell()); + fViewer = view; + fProvider = loader; + fFilters = null; + setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + /** + * Adds a criteria to the table + * + * @param criteria A criteria to add + * @param checked A flag whether criteria is checked (selected) or not + * @param positive A flag whether criteria is for positive filter or not + * @param loaderClassName A loader class name for the filters + */ + protected void addCriteria(Criteria criteria, boolean checked, boolean positive, String loaderClassName) { + CriteriaTableItem cti = new CriteriaTableItem(fTable, checked, positive, loaderClassName); + cti.setCriteria(criteria); + } + + /** + * Replaces a selected criteria with a new criteria. + * + * @param newCriteria A new criteria. + */ + protected void replaceSelectedCriteria(Criteria newCriteria) { + CriteriaTableItem cti = (CriteriaTableItem) fTable.getSelection()[0].getData(); + cti.setCriteria(newCriteria); + } + + /** + * Handles table selection count. + */ + protected void handleTableSelectionCount() { + int count = fTable.getSelectionCount(); + fEdit.setEnabled(count == 1); + fRemove.setEnabled(count > 0); + } + + @Override + public Control createDialogArea(Composite parent) { + + Group ret = new Group(parent, SWT.NONE); + ret.setText(Messages.SequenceDiagram_ListOfHideDisplayPatterns); + RowLayout rowLayout = new RowLayout(); + rowLayout.wrap = false; + rowLayout.pack = true; + rowLayout.justify = false; + rowLayout.type = SWT.HORIZONTAL; + rowLayout.marginLeft = 4; + rowLayout.marginTop = 4; + rowLayout.marginRight = 4; + rowLayout.marginBottom = 4; + rowLayout.spacing = 8; + ret.setLayout(rowLayout); + + fTable = new Table(ret, SWT.MULTI | SWT.CHECK); + fTable.setLayoutData(new RowData(220, 84)); + fTable.setHeaderVisible(false); + fTable.addSelectionListener(new SelectionListener() { + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + int count = fTable.getSelectionCount(); + if (count == 1) { + Criteria criteria = openFilterDialog(((CriteriaTableItem) fTable.getSelection()[0].getData()).getCriteria(), Messages.SequenceDiagram_Update); + if (criteria != null) { + replaceSelectedCriteria(criteria); + } + } + } + + @Override + public void widgetSelected(SelectionEvent e) { + handleTableSelectionCount(); + } + }); + if (fFilters != null) { + for (Iterator<FilterCriteria> i = fFilters.iterator(); i.hasNext();) { + FilterCriteria filterCriteria = i.next(); + addCriteria(filterCriteria.getCriteria(), filterCriteria.isActive(), filterCriteria.isPositive(), filterCriteria.getLoaderClassName()); + } + } + + Composite commands = new Composite(ret, SWT.NONE); + RowLayout rowLayoutCommands = new RowLayout(); + rowLayoutCommands.wrap = false; + rowLayoutCommands.pack = false; + rowLayoutCommands.justify = true; + rowLayoutCommands.type = SWT.VERTICAL; + rowLayoutCommands.marginLeft = 0; + rowLayoutCommands.marginTop = 4; + rowLayoutCommands.marginRight = 0; + rowLayoutCommands.marginBottom = 4; + rowLayoutCommands.spacing = 8; + commands.setLayout(rowLayoutCommands); + fAdd = new Button(commands, SWT.NONE); + fAdd.setText(Messages.SequenceDiagram_Add); + fAdd.addSelectionListener(new SelectionListener() { + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // Nothing to do + } + + @Override + public void widgetSelected(SelectionEvent e) { + Criteria init = new Criteria(); + Criteria c = openFilterDialog(init, Messages.SequenceDiagram_Create); + if (c != null) { + addCriteria(c, true, false, null); + } + } + }); + + fEdit = new Button(commands, SWT.NONE); + fEdit.setText(Messages.SequenceDiagram_EditIt); + fEdit.addSelectionListener(new SelectionListener() { + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // Nothing to do + } + + @Override + public void widgetSelected(SelectionEvent e) { + Criteria c = openFilterDialog(((CriteriaTableItem) fTable.getSelection()[0].getData()).getCriteria(), Messages.SequenceDiagram_Update); + if (c != null) { + replaceSelectedCriteria(c); + } + } + }); + fEdit.setEnabled(false); + + fRemove = new Button(commands, SWT.NONE); + fRemove.setText(Messages.SequenceDiagram_Remove); + fRemove.addSelectionListener(new SelectionListener() { + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // Nothing to do + } + + @Override + public void widgetSelected(SelectionEvent e) { + fTable.remove(fTable.getSelectionIndices()); + handleTableSelectionCount(); + } + }); + fRemove.setEnabled(false); + + getShell().setText(Messages.SequenceDiagram_SequenceDiagramHidePatterns); + /* + * for (int i=0;i<filters.size();i++) { if (filters.get(i) instanceof FilterCriteria) + * addCriteria(((FilterCriteria)filters.get(i)).getCriteria(),true); } + */ + return ret; + } + + /** + * Opens the filter dialog box with given parameter. + * + * @param criteria The criteria reference to pass + * @param action to distinguish between "Update" and "Create" + * @return the criteria that has been updated or created + */ + protected Criteria openFilterDialog(Criteria criteria, String action) { + SearchFilterDialog filter = new SearchFilterDialog((SDView) fViewer, fProvider, true, SWT.APPLICATION_MODAL); + filter.setCriteria(criteria); + filter.setOkText(action); + filter.setTitle(Messages.SequenceDiagram_DefinitionOfHidePattern); + filter.open(); + return filter.getCriteria(); + } + + @Override + public int open() { + create(); + getShell().pack(); + getShell().setLocation(getShell().getDisplay().getCursorLocation()); + loadFiltersCriteria(); + return super.open(); + } + + @Override + public void okPressed() { + if (fTable.getItemCount() > 0) { + fFilters = new ArrayList<>(); + } else { + fFilters = null; + } + for (int i = 0; i < fTable.getItemCount(); i++) { + TableItem item = fTable.getItem(i); + CriteriaTableItem cti = (CriteriaTableItem) item.getData(); + FilterCriteria fc = new FilterCriteria(cti.getCriteria(), item.getChecked(), cti.isPositive(), cti.getLoaderClassName()); + FilterCriteria efc = FilterCriteria.find(fc, fFilters); + if (efc == null) { + fFilters.add(fc); + } else { + efc.setActive(efc.isActive() || fc.isActive()); + } + } + super.close(); + fProvider.filter(fFilters); + saveFiltersCriteria(fFilters); + } + + /** + * Sets the list of filters. + * + * @param filters The list of filters to set. + */ + public void setFilters(List<FilterCriteria> filters) { + fFilters = filters; + } + + /** + * Returns the filters list after editing. + * + * @return the filters list after editing + */ + public List<FilterCriteria> getFilters() { + return fFilters; + } + + /** + * Loads the filter criteria from global filters which are saved in the dialog settings. + */ + protected void loadFiltersCriteria() { + List<FilterCriteria> globalFilters = getGlobalFilters(); + for (Iterator<FilterCriteria> i = globalFilters.iterator(); i.hasNext();) { + FilterCriteria filterCriteria = i.next(); + addCriteria(filterCriteria.getCriteria(), filterCriteria.isActive(), filterCriteria.isPositive(), filterCriteria.getLoaderClassName()); + } + } + + /** + * Returns the global filters which are saved in the dialog settings.. + * + * @return the saved global filters + */ + + public static List<FilterCriteria> getGlobalFilters() { + DialogSettings settings = (DialogSettings) Activator.getDefault().getDialogSettings().getSection(FILTERS_LIST_CRITERIA); + int i = 0; + DialogSettings section = null; + int size = 0; + List<FilterCriteria> globalFilters = new ArrayList<>(); + if (settings != null) { + try { + size = settings.getInt(FILTERS_LIST_SIZE); + } catch (NumberFormatException e) { + // This is not a problem + size = 0; + } + section = (DialogSettings) settings.getSection(FILTERS_LIST_CRITERIA + i); + + while ((section != null) && (i < size)) { + FilterCriteria criteria = new FilterCriteria(); + criteria.setCriteria(new Criteria()); + criteria.load(section); + globalFilters.add(criteria); + section = (DialogSettings) settings.getSection(FILTERS_LIST_CRITERIA + (++i)); + } + } + + return globalFilters; + } + + /** + * Saves the filter criteria in the dialog settings. + * + * @param globalFilters A list of filters to save. + */ + public static void saveFiltersCriteria(List<FilterCriteria> globalFilters) { + DialogSettings settings = (DialogSettings) Activator.getDefault().getDialogSettings(); + DialogSettings section = (DialogSettings) settings.getSection(FILTERS_LIST_CRITERIA); + if (section == null) { + section = (DialogSettings) settings.addNewSection(FILTERS_LIST_CRITERIA); + } + + if (globalFilters == null) { + section.put(FILTERS_LIST_SIZE, 0); + return; + } + + section.put(FILTERS_LIST_SIZE, globalFilters.size()); + + FilterCriteria criteria; + + for (int j = 0; j < globalFilters.size(); j++) { + if (globalFilters.get(j) == null) { + return; + } + + criteria = globalFilters.get(j); + DialogSettings subSection = (DialogSettings) section.getSection(FILTERS_LIST_CRITERIA + j); + + if (subSection == null) { + subSection = (DialogSettings) section.addNewSection(FILTERS_LIST_CRITERIA + j); + } + criteria.save(subSection); + } + } + + /** + * Deactivates the saved global filters. + */ + public static void deactivateSavedGlobalFilters() { + // Deactivate all filters + List<FilterCriteria> filters = getGlobalFilters(); + for(FilterCriteria criteria : filters) { + criteria.setActive(false); + } + // Save settings + FilterListDialog.saveFiltersCriteria(filters); + } + + // ------------------------------------------------------------------------ + // Helper classes + // ------------------------------------------------------------------------ + /** + * A class to map TableItems that can be toggled active or inactive and Criteria + */ + protected class CriteriaTableItem { + + /** + * The criteria reference + */ + protected Criteria fCriteria; + /** + * The "positive" value. + */ + protected boolean fIsPositive; + /** + * The loader class name + */ + protected String fLoaderClassName; + /** + * The actual table item. + */ + protected TableItem fTableItem; + + /** + * Constructor + * + * @param parent The parent table + * @param isActive <code>true</code> if filter criteria is active else <code>false</code> + * @param isPositive <code>true</code> for positive filter else <code>false</code> + * @param loaderClassName The loader class name + */ + public CriteriaTableItem(Table parent, boolean isActive, boolean isPositive, String loaderClassName) { + fTableItem = new TableItem(parent, SWT.NONE); + fTableItem.setData(this); + fTableItem.setChecked(isActive); + fIsPositive = isPositive; + fLoaderClassName = loaderClassName; + } + + /** + * Constructor + * + * @param parent The parent table + * @param isActive <code>true</code> if filter criteria is active else <code>false</code> + * @param isPositive <code>true</code> for positive filter else <code>false</code> + * @param loaderClassName The loader class name + * @param index The table item index + */ + public CriteriaTableItem(Table parent, boolean isActive, boolean isPositive, String loaderClassName, int index) { + fTableItem = new TableItem(parent, SWT.NONE, index); + fTableItem.setChecked(isActive); + fIsPositive = isPositive; + fLoaderClassName = loaderClassName; + } + + /** + * Sets the criteria. + * + * @param criteria The criteria to set + */ + public void setCriteria(Criteria criteria) { + fCriteria = criteria; + fTableItem.setText((fIsPositive ? Messages.SequenceDiagram_display : Messages.SequenceDiagram_hide) + " " + fCriteria.getExpression() + " " + fCriteria.getGraphNodeSummary(fProvider, fLoaderClassName)); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Returns the criteria. + * @return the criteria + */ + public Criteria getCriteria() { + return fCriteria; + } + + /** + * Returns whether positive filtering is active or not. + * + * @return <code>true</code> for positive filter else <code>false</code> + */ + public boolean isPositive() { + return fIsPositive; + } + + /** + * Returns the loader class name. + * + * @return the loader class name + */ + public String getLoaderClassName() { + return fLoaderClassName; + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/MinMaxDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/MinMaxDialog.java new file mode 100755 index 0000000000..4847b1d41e --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/MinMaxDialog.java @@ -0,0 +1,190 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +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.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Dialog box for entering minimum and maximum time range for time compression bar. + * + * @version 1.0 + * @author sveyrier + * @author Bernd Hufmann + * + */ +public class MinMaxDialog extends Dialog { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * Text field for minimum. + */ + private Text fMinText; + /** + * Text field for maximum. + */ + private Text fMaxText; + /** + * Text field for scale. + */ + private Text fScaleText; + /** + * Text field for precision. + */ + private Text fPrecisionText; + /** + * The sequence diagram widget reference. + */ + private SDWidget fSdWidget; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + /** + * Standard constructor. + * @param shell The shell + * @param viewer The sequence diagram widget reference. + */ + public MinMaxDialog(Shell shell, SDWidget viewer) { + super(shell); + fSdWidget = viewer; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + /** + * Method to create a grid data base on horizontal span. + * @param span The horizontal span + * @return a grid data object + */ + protected GridData newGridData(int span) { + GridData data = new GridData(GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL); + data.horizontalSpan = span; + return data; + } + + @Override + protected Control createDialogArea(Composite p) { + p.getShell().setText(Messages.SequenceDiagram_TimeCompressionBarConfig); + Composite parent = (Composite) super.createDialogArea(p); + + GridLayout parentLayout = new GridLayout(); + parentLayout.numColumns = 6; + parent.setLayout(parentLayout); + + Group g1 = new Group(parent, SWT.SHADOW_NONE); + g1.setLayoutData(newGridData(3)); + GridLayout g1layout = new GridLayout(); + g1layout.numColumns = 3; + g1.setLayout(g1layout); + + Label minLabel = new Label(g1, SWT.RADIO); + minLabel.setText(Messages.SequenceDiagram_MinTime); + minLabel.setLayoutData(newGridData(1)); + + fMinText = new Text(g1, SWT.SINGLE | SWT.BORDER); + fMinText.setLayoutData(newGridData(2)); + fMinText.setText(String.valueOf(fSdWidget.getFrame().getMinTime().getValue())); + + Label maxLabel = new Label(g1, SWT.RADIO); + maxLabel.setText(Messages.SequenceDiagram_MaxTime); + maxLabel.setLayoutData(newGridData(1)); + + fMaxText = new Text(g1, SWT.SINGLE | SWT.BORDER); + fMaxText.setLayoutData(newGridData(2)); + fMaxText.setText(String.valueOf(fSdWidget.getFrame().getMaxTime().getValue())); + + Label scaleLabel = new Label(g1, SWT.RADIO); + scaleLabel.setText(Messages.SequenceDiagram_Scale); + scaleLabel.setLayoutData(newGridData(1)); + + fScaleText = new Text(g1, SWT.SINGLE | SWT.BORDER); + fScaleText.setLayoutData(newGridData(2)); + fScaleText.setText(String.valueOf(fSdWidget.getFrame().getMinTime().getScale())); + + + Label precisionLabel = new Label(g1, SWT.RADIO); + precisionLabel.setText(Messages.SequenceDiagram_Precision); + precisionLabel.setLayoutData(newGridData(1)); + + fPrecisionText = new Text(g1, SWT.SINGLE | SWT.BORDER); + fPrecisionText.setLayoutData(newGridData(2)); + fPrecisionText.setText(String.valueOf(fSdWidget.getFrame().getMinTime().getPrecision())); + + return parent; + } + + @Override + protected void okPressed() { + long min = 0; + long max = 0; + int scale = 0; + int precision = 0; + try { + min = Long.parseLong(fMinText.getText()); + max = Long.parseLong(fMaxText.getText()); + scale = Integer.parseInt(fScaleText.getText()); + precision = Integer.parseInt(fPrecisionText.getText()); + + fSdWidget.getFrame().setMax(new TmfTimestamp(max, scale, precision)); + fSdWidget.getFrame().setMin(new TmfTimestamp(min, scale, precision)); + + fSdWidget.redraw(); + + super.okPressed(); + } catch (Exception e) { + MessageDialog.openError(getShell(), Messages.SequenceDiagram_Error, Messages.SequenceDiagram_InvalidRange); + } + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + super.createButtonsForButtonBar(parent); + createButton(parent, IDialogConstants.CLIENT_ID, Messages.SequenceDiagram_Default, false); + getButton(IDialogConstants.CLIENT_ID).addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + fSdWidget.getFrame().resetCustomMinMax(); + fMinText.setText(String.valueOf(fSdWidget.getFrame().getMinTime().getValue())); + fMaxText.setText(String.valueOf(fSdWidget.getFrame().getMaxTime().getValue())); + fScaleText.setText(String.valueOf(fSdWidget.getFrame().getMinTime().getScale())); + fPrecisionText.setText(String.valueOf(fSdWidget.getFrame().getMinTime().getPrecision())); + fMaxText.getParent().layout(true); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // nothing to do + } + }); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/PagesDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/PagesDialog.java new file mode 100755 index 0000000000..29c8b737db --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/PagesDialog.java @@ -0,0 +1,204 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import java.text.MessageFormat; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDAdvancedPagingProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; +import org.eclipse.ui.IViewPart; + +/** + * Class implementation of the pages dialog.<br> + * + * It is associated to an SDView and to a ISDAdvancedPagingProvider.<br> + * + * @version 1.0 + * @author sveyrier + */ +public class PagesDialog extends Dialog { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * viewer and provided are kept here as attributes + */ + private ISDAdvancedPagingProvider fProvider = null; + + /** Current page */ + private TextArea fCurrentPage; + + /** Comment label */ + private Label fTotalPageComment; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Standard constructor + * + * @param view The sequence diagram view reference + * @param provider The paging provider reference + */ + public PagesDialog(IViewPart view, ISDAdvancedPagingProvider provider) { + super(view.getSite().getShell()); + fProvider = provider; + setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public Control createDialogArea(Composite parent) { + + Group ret = new Group(parent, SWT.NONE); + GridData data = new GridData(); + data.grabExcessHorizontalSpace = true; + data.horizontalAlignment = GridData.FILL; + ret.setLayoutData(data); + ret.setText(Messages.SequenceDiagram_PageNavigation); + + FillLayout fillLayout = new FillLayout(SWT.VERTICAL); + ret.setLayout(fillLayout); + + Label label = new Label(ret, SWT.NONE); + label.setText(Messages.SequenceDiagram_CurrentPage); + + fCurrentPage = new TextArea(ret); + fCurrentPage.setBounds(1, fProvider.pagesCount()); + fCurrentPage.setValue(fProvider.currentPage() + 1); + + fTotalPageComment = new Label(ret, SWT.NONE); + fTotalPageComment.setAlignment(SWT.RIGHT); + + updateComments(); + + getShell().setText(Messages.SequenceDiagram_SequenceDiagramPages); + return ret; + } + + @Override + public void okPressed() { + int currentPageValue = fCurrentPage.getValue() - 1; + super.close(); + fProvider.pageNumberChanged(currentPageValue); + } + + /** + * Updates the comments texts. + */ + private void updateComments() { + int pages = Math.max(0, fProvider.pagesCount()); + StringBuffer totalPageCommentText = new StringBuffer(); + totalPageCommentText.append(Messages.SequenceDiagram_Total); + totalPageCommentText.append(pages); + totalPageCommentText.append(" "); //$NON-NLS-1$ + if (pages == 0) { + totalPageCommentText.append(Messages.SequenceDiagram_pages); + } else if (pages == 1) { + totalPageCommentText.append(Messages.SequenceDiagram_page); + } else { + totalPageCommentText.append(Messages.SequenceDiagram_pages); + } + fTotalPageComment.setText(totalPageCommentText.toString()); + } + + + // ------------------------------------------------------------------------ + // Helper classes + // ------------------------------------------------------------------------ + + /** + * This is a Text Control that accepts only digits and ensures that bounds are respected + */ + protected static class TextArea { + /** + * The text field. + */ + private Text fText; + /** + * The minimum page value + */ + private int fMin; + /** + * The maximum page value + */ + private int fMax; + + /** + * Constructor + * + * @param parent The paren composite + */ + public TextArea(Composite parent) { + fText = new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.RIGHT); + fText.setTextLimit(10); + } + + /** + * Sets the page value. + * + * @param page The page value + */ + public void setValue(int page) { + int value = Math.max(fMin, Math.min(fMax, page)); + fText.setText(Integer.toString(value)); + } + + /** + * Returns the page value. + * + * @return the page value + */ + public int getValue() { + int res; + try { + res = Integer.parseInt(fText.getText()); + } catch (Exception e) { + // ignored + res = 0; + } + return Math.max(fMin, Math.min(fMax, res)); + } + + /** + * Sets the minimum and maximum page values. + * + * @param min A minimum page value + * @param max A maximum page value + */ + public void setBounds(int min, int max) { + fMin = Math.max(0, min); + fMax = Math.max(fMin, max); + Integer tab[] = new Integer[2]; + tab[0] = Integer.valueOf(fMin); + tab[1] = Integer.valueOf(fMax); + fText.setToolTipText(MessageFormat.format(Messages.SequenceDiagram_IsInBetween, (Object[]) tab)); + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SDPrintDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SDPrintDialog.java new file mode 100755 index 0000000000..b5378652ee --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SDPrintDialog.java @@ -0,0 +1,174 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * This class implements a dialog box for collecting printing information. + * + * @version 1.0 + * @author sveyrier + */ +public class SDPrintDialog extends Dialog { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The sequence dialog widget reference. + */ + private SDWidget fSdView; + /** + * Sequence dialog print dialog UI + */ + private SDPrintDialogUI fDialogUI; + /** + * Error message to display. + */ + private String fErrorMessage = null; + /** + * A message label. + */ + private Label fMessageLabel = null; + /** + * Flag whether the page is complete or not + */ + private boolean fIsPageComplete = true; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Standard constructor + * + * @param shell Shell reference + * @param viewer Sequence diagram widget reference + */ + public SDPrintDialog(Shell shell, SDWidget viewer) { + super(shell); + fSdView = viewer; + + fDialogUI = new SDPrintDialogUI(shell, fSdView); + fDialogUI.setParentDialog(this); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + protected Control createDialogArea(Composite p) { + p.getShell().setText(Messages.SequenceDiagram_Print); + Composite parent = (Composite) super.createDialogArea(p); + + fDialogUI.createDialogArea(parent); + + fMessageLabel = new Label(parent, SWT.NONE); + GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); + gridData.horizontalSpan = 6; + fMessageLabel.setLayoutData(gridData); + setErrorMessage(fErrorMessage); + + return parent; + } + + @Override + protected void okPressed() { + + if (fDialogUI.okPressed()) { + super.okPressed(); + } + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + + super.createButtonsForButtonBar(parent); + createButton(parent, IDialogConstants.CLIENT_ID, Messages.SequenceDiagram_Printer, false); + + getButton(IDialogConstants.CLIENT_ID).addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + fDialogUI.printButtonSelected(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + + updateButtons(); + } + + /** + * @return the dialog UI + */ + public SDPrintDialogUI getDialogUI() { + return fDialogUI; + } + + /** + * Sets the error message. + * + * @param message error message to set + */ + public void setErrorMessage(String message) { + fErrorMessage = message; + if (fMessageLabel != null) { + if (fErrorMessage == null) { + fMessageLabel.setText(""); //$NON-NLS-1$ + } else { + fMessageLabel.setText(fErrorMessage); + } + } + } + + /** + * Sets the page complete flag. + * @param complete whether page is complete or not + */ + public void setPageComplete(boolean complete) { + fIsPageComplete = complete; + updateButtons(); + } + + /** + * Udates the button enable state. + */ + public void updateButtons() { + Button okButton = getButton(IDialogConstants.OK_ID); + if (fIsPageComplete) { + if (okButton != null) { + okButton.setEnabled(true); + } + } else { + if (okButton != null) { + okButton.setEnabled(false); + } + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SDPrintDialogUI.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SDPrintDialogUI.java new file mode 100755 index 0000000000..8780c8d243 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SDPrintDialogUI.java @@ -0,0 +1,1424 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import java.text.MessageFormat; +import java.util.Arrays; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.printing.PrintDialog; +import org.eclipse.swt.printing.Printer; +import org.eclipse.swt.printing.PrinterData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.DiagramToolTip; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.NGC; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * The class implements the actual print dialog UI for collecting printing data. + * + * @version 1.0 + * @author sveyrier + */ +public class SDPrintDialogUI { + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The set horizontal pages number. + */ + private Button fSetHPagesNumber; + /** + * The set vertical pages number. + */ + private Button fSetVPagesNumber; + /** + * Flag whether to use current zoom or not. + */ + private Button fUseCurrentZoom; + /** + * Flag whether to print all pages or not + */ + private Button fAllPages; + /** + * Flag whether to print current page only + */ + private Button fCurrentPage; + /** + * Button to select a page list. + */ + private Button fPageList; + /** + * Button to select a page range. + */ + private Button fPageRange; + /** + * Text field to enter from page. + */ + private Text fFromPage; + /** + * Text field to enter to page. + */ + private Text fToPage; + /** + * The sequence diagram widget reference. + */ + private SDWidget fSdView; + /** + * Text field for number of horizontal pages + */ + private Text fHorPagesNum; + /** + * Text field for number of vertical pages + */ + private Text fVertPagesNum; + /** + * Text field for toal number of pages + */ + private Text fTotalPages; + /** + * A modify listener implementation to handle modifications. + */ + private ModifyListener fModifyListener; + /** + * A selection listener implementation to handle selections. + */ + private SelectionListener fSelectionListener; + /** + * Local canvas displaying sequence diagram overview. + */ + private LocalSD fOverviewCanvas; + /** + * Number of pages + */ + private int fNbPages = 0; + /** + * Number of selected pages. + */ + private int fPageNum = -1; + /** + * Number of first page. + */ + private int fFirstPage = -1; + /** + * List of pages to print. + */ + private int fPagesList[]; + + /** + * Value for dividing the sequence diagram into pages + */ + private float fStepX; + + /** + * Value for dividing the sequence diagram into pages + */ + private float fStepY; + + /** + * Value for dividing the sequence diagram into pages + */ + private float sTX; + + /** + * Value for dividing the sequence diagram into pages + */ + private float sTY; + + /** + * Page which to print from. + */ + private int fFrom; + /** + * Page which to print to. + */ + private int fTo; + /** + * Flag for enabling multi-selection. + */ + private boolean fMultiSelection = false; + /** + * Flag for enabling area selection. + */ + private boolean fAreaSelection = false; + /** + * Flag for printing all. + */ + private boolean fPrintAll; + /** + * Flag for printing current page only. + */ + private boolean fPrintCurrent; + /** + * Flag for printing a selection of pages. + */ + private boolean fPrintSelection; + /** + * Flag for printing a range of pages. + */ + private boolean fPrintRange; + /** + * Number of selected rows + */ + private int fNbRows; + /** + * Number of selected lines + */ + private int fNbLines; + /** + * The zoom factor. + */ + private float fZoomFactor; + /** + * The printer data reference. + */ + private PrinterData fPrinterData; + /** + * The diagram tooltip to show if necessary. + */ + private DiagramToolTip fToolTip = null; + /** + * Label for current selection. + */ + private Label fCurrentSelection; + /** + * The shell reference. + */ + private Shell fShell; + /** + * Button to open printer dialog from OS. + */ + private Button fPrinterDialog; + /** + * Flag for showing print button. + */ + private boolean fShowPrintButton; + /** + * Test value + */ + private int fTest = 3; + /** + * Parent wizard page if used as wizard + */ + private WizardPage fParentWizardPage = null; + /** + * Reference to parent print dialog. + */ + private SDPrintDialog fParentDialog = null; + + // ------------------------------------------------------------------------ + // Helper Class + // ------------------------------------------------------------------------ + /** + * Local sequence diagram widget used to display overview of sequence diagram to print. + * @version 1.0 + */ + private class LocalSD extends SDWidget { + + /** + * Constructor + * @param c Parent composite + * @param s Style bits + */ + public LocalSD(Composite c, int s) { + super(c, s); + } + + @Override + public int getContentsHeight() { + if (fSdView.getContentsHeight() > fSdView.getContentsHeight()) { + return (int) (fSdView.getVisibleHeight() / (float) fTest / fSdView.getZoomValue()); + } + return super.getContentsHeight(); + } + + @Override + public int getContentsWidth() { + if (fSdView.getVisibleWidth() > fSdView.getContentsWidth()) { + return (int) (fSdView.getVisibleWidth() / (float) fTest / fSdView.getZoomValue()); + } + return super.getContentsWidth(); + } + + @Override + protected void contentsMouseHover(MouseEvent event) { + } + + /** + * Creates page selection images. + * + * @param img - Overview image + * @param width -The width value + * @param stepX - Step X + * @param height - Height value + * @param stepY - Step Y + * @return new image + */ + protected Image createPagesSelectionImages(Image img, int width, float stepX, int height, float stepY) { + + Image over = new Image(super.getShell().getDisplay(), img.getImageData()); + + for (int pageIndex = 0; pageIndex < fPagesList.length; pageIndex++) { + + int pageNum = fPagesList[pageIndex]; + + if (getPagesForSelection() > 0 && pageNum > 0) { + int line = pageNum / getNbRow(); + int row = pageNum % getNbRow(); + if (row != 0) { + line++; + } else { + row = getNbRow(); + } + + line--; + row--; + + Image toDel = over; + if (fOverviewCanvas.isFocusControl()) { + over = new Image(super.getShell().getDisplay(), drawRegionSelected(toDel, new Rectangle(contentsToViewX((int) (row * stepX * fOverviewCanvas.getZoomValue())), contentsToViewY((int) (line * stepY * fOverviewCanvas.getZoomValue())), + ((int) (stepX * fOverviewCanvas.getZoomValue())), ((int) (stepY * fOverviewCanvas.getZoomValue()))), new RGB(0, 0, 128))); + } else { + over = new Image(super.getShell().getDisplay(), drawRegionSelected(toDel, new Rectangle(contentsToViewX((int) (row * stepX * fOverviewCanvas.getZoomValue())), contentsToViewY((int) (line * stepY * fOverviewCanvas.getZoomValue())), + ((int) (stepX * fOverviewCanvas.getZoomValue())), ((int) (stepY * fOverviewCanvas.getZoomValue()))), new RGB(221, 208, 200))); + } + toDel.dispose(); + } + } + + Arrays.sort(fPagesList); + int pos = Arrays.binarySearch(fPagesList, fPageNum); + if ((pos < 0) && (getPagesForSelection() > 0 && fPageNum > 0)) { + int line = fPageNum / getNbRow(); + int row = fPageNum % getNbRow(); + if (row != 0) { + line++; + } else { + row = getNbRow(); + } + + line--; + row--; + + Image toDel = over; + over = new Image(super.getShell().getDisplay(), drawRegionSelected(toDel, new Rectangle(contentsToViewX((int) (row * stepX * fOverviewCanvas.getZoomValue())), contentsToViewY((int) (line * stepY * fOverviewCanvas.getZoomValue())), + ((int) (stepX * fOverviewCanvas.getZoomValue())), ((int) (stepY * fOverviewCanvas.getZoomValue()))), new RGB(221, 208, 200))); + toDel.dispose(); + } + + GC imGC2 = new GC(over); + imGC2.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); + NGC imGC = new NGC(fOverviewCanvas, imGC2); + for (int i = 0, x = 0; x <= width && stepX > 0; i++, x = (int) (i * stepX)) { + imGC.drawLine(x, 0, x, height); + } + + for (int j = 0, y = 0; y <= height && stepY > 0; j++, y = (int) (j * stepY)) { + imGC.drawLine(0, y, width, y); + } + + imGC2.dispose(); + imGC.dispose(); + return over; + } + + @Override + protected void drawContents(GC gc, int clipx, int clipy, int clipw, int cliph) { + + Image dbuffer = getDrawBuffer(); + computeStepXY(); + Image d; + + int lw = (int) (getContentsWidth() / getZoomValue()); + if (getContentsWidth() < getVisibleWidth()) { + lw = (int) (getVisibleWidth() / getZoomValue()); + } + + int lh = (int) (getContentsHeight() / getZoomValue()); + if (getContentsHeight() < getVisibleHeight()) { + lh = (int) (getVisibleHeight() / getZoomValue()); + } + d = createPagesSelectionImages(dbuffer, lw, fStepX, lh, fStepY); + + if (!isEnabled()) { + Image toDel = d; + d = new Image(super.getShell().getDisplay(), drawRegionSelected(d, new Rectangle(0, 0, lw, lh), new RGB(221, 208, 200))); + toDel.dispose(); + } + + Rectangle area = getClientArea(); + int w = d.getBounds().width; + int h = d.getBounds().height; + gc.drawImage(d, 0, 0, w, h, 0, 0, area.width, area.height); + + fTotalPages.setText(Integer.valueOf(maxNumOfPages()).toString()); + displayPageNum(); + dbuffer.dispose(); + d.dispose(); + gc.dispose(); + } + + @Override + protected void keyPressedEvent(KeyEvent e) { + if (e.keyCode == SWT.CTRL) { + fMultiSelection = true; + } + if (e.keyCode == SWT.SHIFT) { + fAreaSelection = true; + } + if (e.keyCode == SWT.ARROW_DOWN) { + if (fPageNum + getNbRow() <= maxNumOfPages()) { + fPageNum += getNbRow(); + } + int line = fPageNum / getNbRow(); + int row = fPageNum % getNbRow(); + if (row == 0) { + line--; + } + if ((line + 1) * fStepY > (fOverviewCanvas.getContentsY() + fOverviewCanvas.getVisibleHeight()) / fOverviewCanvas.getZoomValue()) { + fOverviewCanvas.scrollBy(0, (int) (fStepY * fOverviewCanvas.getZoomValue())); + } + } + if (e.keyCode == SWT.ARROW_UP) { + if (fPageNum - getNbRow() > 0) { + fPageNum -= getNbRow(); + } + int line = fPageNum / getNbRow(); + int row = fPageNum % getNbRow(); + if (row == 0) { + line--; + } + if ((line) * fStepY <= fOverviewCanvas.getContentsY() / fOverviewCanvas.getZoomValue()) { + fOverviewCanvas.scrollBy(0, -(int) (fStepY * fOverviewCanvas.getZoomValue())); + } + } + if (e.keyCode == SWT.ARROW_LEFT) { + if ((fPageNum - 2) / getNbRow() == (fPageNum - 1) / getNbRow() && fPageNum > 1) { + fPageNum--; + } + int row = fPageNum % getNbRow(); + if ((row - 1) * fStepX < (fOverviewCanvas.getContentsX()) / fOverviewCanvas.getZoomValue()) { + fOverviewCanvas.scrollBy(-(int) (fStepX * fOverviewCanvas.getZoomValue()), 0); + } + } + if (e.keyCode == SWT.ARROW_RIGHT) { + if ((fPageNum - 1) / getNbRow() == fPageNum / getNbRow()) { + fPageNum++; + } + int row = fPageNum % getNbRow(); + if (row == 0) { + row = getNbRow(); + } + if ((row) * fStepX > (fOverviewCanvas.getContentsX() + fOverviewCanvas.getVisibleWidth()) / fOverviewCanvas.getZoomValue()) { + fOverviewCanvas.scrollBy((int) (fStepX * fOverviewCanvas.getZoomValue()), 0); + } + } + + if (e.keyCode == 32 && fPageNum > -1) { + Arrays.sort(fPagesList); + int pos = Arrays.binarySearch(fPagesList, fPageNum); + if (pos < 0) { + addToPagesList(fPageNum); + } else { + removeFromPagesList(fPageNum); + } + } + + if (!fAreaSelection && !fMultiSelection) { + fFirstPage = fPageNum; + fPagesList = new int[1]; + fPagesList[0] = fPageNum; + } else if ((fPageNum != -1) && (fAreaSelection) && (fFirstPage != -1)) { + fPagesList = new int[0]; + int line1 = fFirstPage / getNbRow(); + int row1 = fFirstPage % getNbRow(); + if (row1 != 0) { + line1++; + } else { + row1 = getNbRow(); + } + + int line2 = fPageNum / getNbRow(); + int row2 = fPageNum % getNbRow(); + if (row2 != 0) { + line2++; + } else { + row2 = getNbRow(); + } + + int temp; + if (line1 > line2) { + temp = line2; + line2 = line1; + line1 = temp; + } + + if (row1 > row2) { + temp = row2; + row2 = row1; + row1 = temp; + } + + for (int i = row1 - 1; i < row2; i++) { + for (int j = line1 - 1; j < line2; j++) { + addToPagesList(i + j * getNbRow() + 1); + } + } + } + displayPageNum(); + fOverviewCanvas.redraw(); + } + + @Override + protected void keyReleasedEvent(KeyEvent e) { + if (e.keyCode == SWT.CTRL) { + fMultiSelection = false; + } + if (e.keyCode == SWT.SHIFT) { + fAreaSelection = false; + } + } + + @Override + protected void contentsMouseDownEvent(MouseEvent event) { + + computeStepXY(); + int x1 = (int) ((event.x / fOverviewCanvas.getZoomValue()) / fStepX); + int x2 = (int) ((event.y / fOverviewCanvas.getZoomValue()) / fStepY); + + int oldPage = fPageNum; + + fPageNum = x1 + x2 * getNbRow() + 1; + + if (fPageNum > maxNumOfPages()) { + fPageNum = oldPage; + return; + } + + if (!fAreaSelection) { + fFirstPage = fPageNum; + } + + if ((fPageNum != -1) && (fMultiSelection)) { + Arrays.sort(fPagesList); + int pos = Arrays.binarySearch(fPagesList, fPageNum); + if (pos < 0) { + addToPagesList(fPageNum); + } else { + removeFromPagesList(fPageNum); + } + } else if ((fPageNum != -1) && (fAreaSelection) && (fFirstPage != -1)) { + + fPagesList = new int[0]; + + int line1 = fFirstPage / getNbRow(); + int row1 = fFirstPage % getNbRow(); + if (row1 != 0) { + line1++; + } else { + row1 = getNbRow(); + } + + int line2 = fPageNum / getNbRow(); + int row2 = fPageNum % getNbRow(); + if (row2 != 0) { + line2++; + } else { + row2 = getNbRow(); + } + + int temp; + if (line1 > line2) { + temp = line2; + line2 = line1; + line1 = temp; + } + + if (row1 > row2) { + temp = row2; + row2 = row1; + row1 = temp; + } + + for (int i = row1 - 1; i < row2; i++) { + for (int j = line1 - 1; j < line2; j++) { + addToPagesList(i + j * getNbRow() + 1); + } + } + } else { + fPagesList = new int[1]; + fPagesList[0] = fPageNum; + } + if ((event.stateMask & SWT.CTRL) != 0) { + fMultiSelection = true; + } + displayPageNum(); + redraw(); + } + + @Override + protected void contentsMouseMoveEvent(MouseEvent e) { + fToolTip.hideToolTip(); + } + + @Override + public void resizeContents(int w, int h) { + super.resizeContents(w, h); + } + + } + + /** + * A traverse listener implementation. + */ + protected static class LocalTraverseListener implements TraverseListener { + @Override + public void keyTraversed(TraverseEvent e) { + if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) { + e.doit = true; + } + } + } + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Constructor + * + * @param shell + * The shell reference + * @param sdWidget + * The sequence diagram widget reference + */ + public SDPrintDialogUI(Shell shell, SDWidget sdWidget) { + this(shell, sdWidget, false); + } + + /** + * Constructor + * + * @param shell + * The shell reference + * @param sdWidget + * The sequence diagram widget reference + * @param showPrintBtn + * Flag for showing print buttons + */ + public SDPrintDialogUI(Shell shell, SDWidget sdWidget, boolean showPrintBtn) { + fShell = shell; + fSdView = sdWidget; + fShowPrintButton = showPrintBtn; + + fPrinterData = Printer.getDefaultPrinterData(); + if (fPrinterData != null) { + fPrinterData.scope = PrinterData.SELECTION; + } + + fPagesList = new int[0]; + + fSelectionListener = new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + if (fUseCurrentZoom.getSelection()) { + fHorPagesNum.setEnabled(false); + fVertPagesNum.setEnabled(false); + } + if (fSetHPagesNumber.getSelection()) { + fHorPagesNum.setEnabled(true); + fVertPagesNum.setEnabled(false); + if (fCurrentPage.getSelection()) { + fCurrentPage.setSelection(false); + fAllPages.setSelection(true); + } + if ("".equals(fHorPagesNum.getText())) { //$NON-NLS-1$ + fHorPagesNum.setText("1"); //$NON-NLS-1$ + } + } + if (fSetVPagesNumber.getSelection()) { + fHorPagesNum.setEnabled(false); + fVertPagesNum.setEnabled(true); + if (fCurrentPage.getSelection()) { + fCurrentPage.setSelection(false); + fAllPages.setSelection(true); + } + if ("".equals(fVertPagesNum.getText())) { //$NON-NLS-1$ + fVertPagesNum.setText("1"); //$NON-NLS-1$ + } + } + if (fCurrentPage.getSelection() || fAllPages.getSelection() || fPageList.getSelection()) { + fFromPage.setEnabled(false); + fToPage.setEnabled(false); + } else { + fFromPage.setEnabled(true); + fToPage.setEnabled(true); + } + + fCurrentPage.setEnabled(fUseCurrentZoom.getSelection()); + fOverviewCanvas.setEnabled(fPageList.getSelection()); + if (fOverviewCanvas.isEnabled() && (e.widget == fUseCurrentZoom || e.widget == fSetHPagesNumber || e.widget == fSetVPagesNumber)) { + fPagesList = new int[1]; + fPagesList[0] = 1; + fPageNum = 1; + fFirstPage = 1; + } else if ((fOverviewCanvas.isEnabled() && (e.widget == fPageList)) && + (fPagesList == null || fPagesList.length <= 0)) { + + fPagesList = new int[1]; + fPagesList[0] = 1; + fPageNum = 1; + fFirstPage = 1; + } + computeStepXY(); + fTotalPages.setText(Integer.valueOf(maxNumOfPages()).toString()); + fOverviewCanvas.redraw(); + fOverviewCanvas.update(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + fPagesList = new int[0]; + computeStepXY(); + fOverviewCanvas.redraw(); + } + + }; + + fModifyListener = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + fPagesList = new int[0]; + computeStepXY(); + fTotalPages.setText(Integer.valueOf(maxNumOfPages()).toString()); + fOverviewCanvas.redraw(); + } + + }; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Creates new grid data object. + * + * @param span horizontal span. + * @return grid data + */ + protected GridData newGridData(int span) { + GridData data = new GridData(GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL); + data.horizontalSpan = span; + return data; + } + + /** + * Creates the dialog area. + * + * @param parent The parent composite + * @return dialog control + */ + public Control createDialogArea(Composite parent) { + + GridLayout parentLayout = new GridLayout(); + parentLayout.numColumns = 6; + parent.setLayout(parentLayout); + + Group g1 = new Group(parent, SWT.SHADOW_NONE); + g1.setText(Messages.SequenceDiagram_ZoomOption); + g1.setLayoutData(newGridData(3)); + GridLayout g1layout = new GridLayout(); + g1layout.numColumns = 2; + g1.setLayout(g1layout); + + fUseCurrentZoom = new Button(g1, SWT.RADIO); + fUseCurrentZoom.setText(Messages.SequenceDiagram_UseCurrentZoom); + fUseCurrentZoom.setLayoutData(newGridData(2)); + fUseCurrentZoom.addSelectionListener(fSelectionListener); + + fSetHPagesNumber = new Button(g1, SWT.RADIO); + fSetHPagesNumber.setText(Messages.SequenceDiagram_NumberOfHorizontalPages); + fSetHPagesNumber.setLayoutData(newGridData(1)); + fSetHPagesNumber.addSelectionListener(fSelectionListener); + + fHorPagesNum = new Text(g1, SWT.SINGLE | SWT.BORDER); + fHorPagesNum.addModifyListener(fModifyListener); + + fSetVPagesNumber = new Button(g1, SWT.RADIO); + fSetVPagesNumber.setText(Messages.SequenceDiagram_NumberOfVerticalPages); + fSetVPagesNumber.setLayoutData(newGridData(1)); + fSetVPagesNumber.addSelectionListener(fSelectionListener); + + fVertPagesNum = new Text(g1, SWT.SINGLE | SWT.BORDER); + fVertPagesNum.addModifyListener(fModifyListener); + + Label nbTotal = new Label(g1, SWT.SHADOW_NONE | SWT.RIGHT); + nbTotal.setText(Messages.TotalNumberOfPages); + // nbTotal.setLayoutData(newGridData(1)); + + fTotalPages = new Text(g1, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); + // nbHV.addModifyListener(modifListener); + + Group g2 = new Group(parent, SWT.SHADOW_NONE); + g2.setText(Messages.SequenceDiagram_Preview); + GridData data = new GridData(GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL); + data.horizontalSpan = 3; + data.verticalSpan = 2; + g2.setLayoutData(data); + GridLayout g2layout = new GridLayout(); + // g2layout. + g2layout.numColumns = 1; + // SVLayout g2layout = new SVLayout(); + g2.setLayout(g2layout); + + GridData data2 = new GridData(GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL); + data2.horizontalSpan = 1; + data2.verticalSpan = 1; + + fOverviewCanvas = new LocalSD(g2, SWT.NO_BACKGROUND); + GridData seqDiagLayoutData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL); + fOverviewCanvas.setLayoutData(seqDiagLayoutData); + // overviewCanvas.resizeContents(100,100); + if (fSdView.getContentsWidth() < fSdView.getVisibleWidth() && fSdView.getContentsHeight() < fSdView.getVisibleHeight()) { + fTest = 3; + } else { + fTest = 10; + } + fOverviewCanvas.setFrame(fSdView.getFrame(), true); + fOverviewCanvas.setZoomValue((float) 1 / fTest); + fOverviewCanvas.setCornerControl(null); + seqDiagLayoutData.widthHint = fOverviewCanvas.getContentsWidth() / fTest; + seqDiagLayoutData.widthHint = fOverviewCanvas.getFrame().getWidth() / fTest + 15; + + if (fSdView.getVisibleWidth() < fSdView.getContentsWidth()) { + seqDiagLayoutData.widthHint = fOverviewCanvas.getContentsWidth() / fTest; + if (seqDiagLayoutData.widthHint > Display.getDefault().getClientArea().width / 4) { + seqDiagLayoutData.widthHint = Display.getDefault().getClientArea().width / 4; + } + } else { + seqDiagLayoutData.widthHint = fOverviewCanvas.getFrame().getWidth() / fTest + 15; + } + + if (fSdView.getVisibleHeight() < fSdView.getContentsHeight()) { + seqDiagLayoutData.heightHint = fOverviewCanvas.getContentsHeight() / fTest; + if (seqDiagLayoutData.heightHint > Display.getDefault().getClientArea().width / 4) { + seqDiagLayoutData.heightHint = Display.getDefault().getClientArea().width / 4; + } + } else { + seqDiagLayoutData.heightHint = fOverviewCanvas.getFrame().getHeight() / fTest; + } + + fOverviewCanvas.setEnabled(false); + + fCurrentSelection = new Label(g2, SWT.SHADOW_NONE | SWT.LEFT); + fCurrentSelection.setLayoutData(newGridData(1)); + + Group g3 = new Group(parent, SWT.SHADOW_NONE); + g3.setText(Messages.SequenceDiagram_PrintRange); + g3.setLayoutData(newGridData(3)); + GridLayout g3layout = new GridLayout(); + g3layout.numColumns = 4; + g3.setLayout(g3layout); + + fAllPages = new Button(g3, SWT.RADIO); + fAllPages.setText(Messages.SequenceDiagram_AllPages); + fAllPages.setLayoutData(newGridData(4)); + fAllPages.addSelectionListener(fSelectionListener); + + fCurrentPage = new Button(g3, SWT.RADIO); + fCurrentPage.setText(Messages.SequenceDiagram_CurrentView); + fCurrentPage.setLayoutData(newGridData(4)); + fCurrentPage.setEnabled(true); + fCurrentPage.setSelection(true); + fCurrentPage.addSelectionListener(fSelectionListener); + + fPageList = new Button(g3, SWT.RADIO); + fPageList.setText(Messages.SequenceDiagram_SelectedPages); + fPageList.setLayoutData(newGridData(4)); + fPageList.addSelectionListener(fSelectionListener); + + fPageRange = new Button(g3, SWT.RADIO); + fPageRange.setText(Messages.SequenceDiagram_FromPage); + fPageRange.setLayoutData(newGridData(1)); + fPageRange.addSelectionListener(fSelectionListener); + + fFromPage = new Text(g3, SWT.SINGLE | SWT.BORDER); + + Label labelTo = new Label(g3, SWT.CENTER); + labelTo.setText(Messages.SequenceDiagram_to); + + fToPage = new Text(g3, SWT.SINGLE | SWT.BORDER); + + fToolTip = new DiagramToolTip(fOverviewCanvas); + + fOverviewCanvas.getViewControl().addMouseTrackListener(new MouseTrackListener() { + @Override + public void mouseEnter(MouseEvent e) { + fToolTip.hideToolTip(); + } + + @Override + public void mouseExit(MouseEvent e) { + fToolTip.hideToolTip(); + } + + @Override + public void mouseHover(MouseEvent e) { + int x1 = (int) (fOverviewCanvas.viewToContentsX(e.x) / fOverviewCanvas.getZoomValue() / fStepX); + int x2 = (int) (fOverviewCanvas.viewToContentsY(e.y) / fOverviewCanvas.getZoomValue() / fStepY); + int num = x1 + x2 * getNbRow() + 1; + if (num > maxNumOfPages()) { + return; + } + if (num > 0) { + fToolTip.showToolTip(String.valueOf(num)); + displayPageNum(); + } else { + fCurrentSelection.setText("");//$NON-NLS-1$ + fToolTip.hideToolTip(); + } + } + + }); + + fOverviewCanvas.addTraverseListener(new LocalTraverseListener()); + + fOverviewCanvas.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + fOverviewCanvas.redraw(); + } + + @Override + public void focusLost(FocusEvent e) { + fOverviewCanvas.redraw(); + } + }); + + if (fShowPrintButton) { + Composite printerDlg = new Composite(parent, SWT.NONE); + data = new GridData(SWT.FILL, SWT.DEFAULT, true, false); + data.horizontalSpan = 6; + parentLayout = new GridLayout(); + parentLayout.numColumns = 2; + printerDlg.setLayout(parentLayout); + printerDlg.setLayoutData(data); + + Label label = new Label(printerDlg, SWT.NONE); + label.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false)); + fPrinterDialog = new Button(printerDlg, SWT.PUSH); + fPrinterDialog.setText(Messages.SequenceDiagram_Printer); + + fPrinterDialog.addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + printButtonSelected(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + }); + } + + updatePrinterStatus(); + + return parent; + } + + /** + * Get number of pages for selection. + * @return number of pages for selection. + */ + public int getPagesForSelection() { + return fNbPages; + } + + /** + * Handler for when the OK button is pressed + * + * @return True if the operation was successful, false if there was an error + */ + public boolean okPressed() { + fPrintAll = fAllPages.getSelection(); + fPrintCurrent = fCurrentPage.getSelection(); + fPrintSelection = fPageList.getSelection(); + fPrintRange = fPageRange.getSelection(); + try { + if (fPrintRange) { + fFrom = Integer.valueOf(fFromPage.getText()).intValue(); + fTo = Integer.valueOf(fToPage.getText()).intValue(); + if (fFrom > maxNumOfPages() || fTo > maxNumOfPages() || fFrom <= 0 || fTo <= 0) { + MessageDialog.openError(getShell(), Messages.SequenceDiagram_Error, Messages.SequenceDiagram_InvalidRange); + return false; + } + } else if (fSetHPagesNumber.getSelection() && fNbPages <= 0) { + MessageDialog.openError(getShell(), Messages.SequenceDiagram_Error, Messages.SequenceDiagram_InvalidNbHorizontal); + return false; + } else if (fSetVPagesNumber.getSelection() && fNbPages <= 0) { + MessageDialog.openError(getShell(), Messages.SequenceDiagram_Error, Messages.SequenceDiagram_InvalidNbVertical); + return false; + } else if (fPrintSelection && getPageList().length <= 0) { + MessageDialog.openError(getShell(), Messages.SequenceDiagram_Error, Messages.SequenceDiagram_NoPageSelected); + return false; + } + + } catch (Exception e) { + MessageDialog.openError(getShell(), Messages.SequenceDiagram_Error, Messages.SequenceDiagram_InvalidRange); + fFrom = 0; + fTo = 0; + return false; + } + + return true; + } + + /** + * Draws region that was selected + * @param img The corresponding image + * @param r The selected rectangle. + * @param color The color to use for selection + * @return image data reference + */ + public ImageData drawRegionSelected(Image img, Rectangle r, RGB color) { + ImageData id = img.getImageData(); + for (int a = 0; a < r.width && r.x + a < id.width; a++) { + for (int b = 0; b < r.height && r.y + b < id.height; b++) { + int index = id.getPixel(r.x + a, r.y + b); + RGB rgb = id.palette.getRGB(index); + rgb = combine(color, rgb); + id.setPixel(r.x + a, r.y + b, id.palette.getPixel(rgb)); + } + } + return id; + } + + /** + * Combines two RGB colors. + * @param front The front color + * @param back The back color + * @return new RGB color + */ + public static RGB combine(RGB front, RGB back) { + int _af = 128; + int _ab = 200; + + double af = (_af) / 255.0; + double rf = front.red; + double gf = front.green; + double bf = front.blue; + + double ab = (_ab) / 255.0; + double rb = back.red; + double gb = back.green; + double bb = back.blue; + + double k = (1.0 - af) * ab; + int r = (int) ((af * rf + k * rb)); + int g = (int) ((af * gf + k * gb)); + int b = (int) ((af * bf + k * bb)); + + return new RGB(r, g, b); + } + + /** + * Computes value for X coordinates step and Y coordinates step. + */ + protected void computeStepXY() { + float cw = fOverviewCanvas.getContentsWidth() / fOverviewCanvas.getZoomValue(); + float ch = fOverviewCanvas.getContentsHeight() / fOverviewCanvas.getZoomValue(); + try { + if (fPrinterData == null) { + fStepX = 0; + fStepY = 0; + fNbPages = 0; + fZoomFactor = 0; + } else { + Printer printer = new Printer(fPrinterData); + if (fSetHPagesNumber.getSelection()) { + fNbPages = Integer.valueOf(fHorPagesNum.getText()).intValue(); + float z1 = fSdView.getContentsWidth() / cw; + float z2 = printer.getClientArea().width / ((float) fSdView.getContentsWidth() / fNbPages); + + fStepY = printer.getClientArea().height / z1 / z2; + fStepX = cw / fNbPages; + } else if (fSetVPagesNumber.getSelection()) { + fNbPages = Integer.valueOf(fVertPagesNum.getText()).intValue(); + float z1 = fSdView.getContentsHeight() / ch; + float z2 = printer.getClientArea().height / ((float) fSdView.getContentsHeight() / fNbPages); + fStepX = printer.getClientArea().width / z1 / z2; + fStepY = ch / fNbPages; + } else { + float z1 = fSdView.getContentsWidth() / (cw); + fStepX = fSdView.getVisibleWidth() / z1; + fNbPages = Math.round(cw / fStepX); + if (fNbPages == 0) { + fNbPages = 1; + } + int pw = printer.getClientArea().width; + int ph = printer.getClientArea().height; + float z2 = pw / ((float) fSdView.getContentsWidth() / fNbPages); + fStepY = ph / z1 / z2; + } + } + } catch (NumberFormatException e) { + fStepX = fStepY = fNbPages = 0; + fZoomFactor = 0; + } + sTX = fStepX * (fSdView.getContentsWidth() / cw); + sTY = fStepY * (fSdView.getContentsHeight() / ch); + float rat = 1; + if ((fSdView.getVisibleWidth() > fSdView.getContentsWidth()) && (fSetVPagesNumber.getSelection() || fSetHPagesNumber.getSelection())) { + rat = (float) fSdView.getVisibleWidth() / (float) fSdView.getContentsWidth(); + } + fZoomFactor = (fOverviewCanvas.getContentsWidth() / cw) / fOverviewCanvas.getZoomFactor() * rat; + } + + /** + * Returns the pages list. + * + * @return the pages list. + */ + public int[] getPageList() { + return Arrays.copyOf(fPagesList, fPagesList.length); + } + + /** + * Adds a page to pages list. + * + * @param num + * The number of the the new page + */ + public void addToPagesList(int num) { + int temp[] = new int[fPagesList.length + 1]; + System.arraycopy(fPagesList, 0, temp, 0, fPagesList.length); + temp[temp.length - 1] = num; + fPagesList = new int[temp.length]; + System.arraycopy(temp, 0, fPagesList, 0, temp.length); + } + + /** + * Removes a page from the pages list. + * + * @param num + * The number of the page to remove + */ + public void removeFromPagesList(int num) { + int pos = Arrays.binarySearch(fPagesList, num); + int temp[] = new int[fPagesList.length - 1]; + System.arraycopy(fPagesList, 0, temp, 0, pos); + System.arraycopy(fPagesList, pos + 1, temp, pos, fPagesList.length - pos - 1); + fPagesList = new int[temp.length]; + System.arraycopy(temp, 0, fPagesList, 0, temp.length); + } + + /** + * Returns the maximum number of pages. + * + * @return maximum number of pages. + */ + public int maxNumOfPages() { + return (getNbRow() * getNbLines()); + } + + /** + * Returns the number of rows. + * + * @return number of rows. + */ + public int getNbRow() { + if (!fSetHPagesNumber.isDisposed()) { + int cw = (int) (fOverviewCanvas.getContentsWidth() / fOverviewCanvas.getZoomValue()); + int row = 1; + if (fStepX != 0) { + row = (int) (cw / fStepX); + if (fSetHPagesNumber.getSelection()) { + row = Math.round(cw / fStepX); + } else if ((cw % fStepX != 0)) { + row++; + } + } + fNbRows = row; + } + return fNbRows; + } + + /** + * Returns the number of lines. + * + * @return number of lines + */ + public int getNbLines() { + if (!fSetVPagesNumber.isDisposed()) { + int ch = (int) (fOverviewCanvas.getContentsHeight() / fOverviewCanvas.getZoomValue()); + int line = 1; + if (fStepY != 0) { + line = (int) (ch / fStepY); + if (fSetVPagesNumber.getSelection()) { + line = Math.round(ch / fStepY); + } else if (ch % fStepY != 0) { + line++; + } + } + fNbLines = line; + } + return fNbLines; + } + + /** + * Returns whether to print all pages or not. + * + * @return <code>true</code> for all pages else <code>false</code>. + */ + public boolean printAll() { + return fPrintAll; + } + + /** + * Returns whether to print only current page + * + * @return <code>true</code> for current page only else <code>false</code>.. + */ + public boolean printCurrent() { + return fPrintCurrent; + } + + /** + * Returns whether to print selected pages. + * + * @return <code>true</code> for selected pages only else <code>false</code>. + */ + public boolean printSelection() { + return fPrintSelection; + } + + /** + * Returns whether to print range of pages. + * + * @return <code>true</code> for range of pages only else <code>false</code>. + */ + public boolean printRange() { + return fPrintRange; + } + + /** + * Returns the step in X direction. + * + * @return step in X direction + */ + public float getStepX() { + return sTX; + } + + /** + * Returns the step in Y direction. + * + * @return step in Y direction + */ + public float getStepY() { + return sTY; + } + + /** + * Returns the zoom factor + * + * @return zoom factor + */ + public float getZoomFactor() { + return fZoomFactor; + } + + /** + * Returns the printer data reference. + * + * @return printer data reference + */ + public PrinterData getPrinterData() { + return fPrinterData; + } + + /** + * Returns the page number to start printing from. + * + * @return page number to start printing from + */ + public int getFrom() { + return fFrom; + } + + /** + * Returns the page number to print to. + * + * @return page number to print to + */ + public int getTo() { + return fTo; + } + + /** + * Displays current number of pages + */ + protected void displayPageNum() { + if (fPageNum > 0) { + String message = MessageFormat.format(Messages.SequenceDiagram_Page, new Object[] { Integer.valueOf(fPageNum) }); + fCurrentSelection.setText(message); + fCurrentSelection.getParent().layout(); + } + } + + /** + * Returns the shell reference. + * + * @return the shell reference. + */ + public Shell getShell() { + return fShell; + } + + /** + * Sets the shell. + * + * @param shell The shell reference. + */ + public void setShell(Shell shell) { + fShell = shell; + } + + /** + * Handle selection of print button. + */ + public void printButtonSelected() { + PrintDialog printer = new PrintDialog(getShell()); + if (fAllPages.getSelection()) { + printer.setScope(PrinterData.ALL_PAGES); + } + if (fCurrentPage.getSelection()) { + printer.setScope(PrinterData.SELECTION); + } + if (fPageList.getSelection()) { + printer.setScope(PrinterData.SELECTION); + } + if (fPageRange.getSelection()) { + printer.setScope(PrinterData.PAGE_RANGE); + fFrom = Integer.valueOf(fFromPage.getText()).intValue(); + fTo = Integer.valueOf(fToPage.getText()).intValue(); + printer.setStartPage(fFrom); + printer.setEndPage(fTo); + } + + PrinterData newPrinterData = printer.open(); + if (newPrinterData != null) { + fPrinterData = newPrinterData; + } + updatePrinterStatus(); + + if (printer.getScope() == PrinterData.ALL_PAGES) { + fAllPages.setSelection(true); + fCurrentPage.setSelection(false); + fPageList.setSelection(false); + fPageRange.setSelection(false); + fHorPagesNum.setEnabled(false); + fVertPagesNum.setEnabled(false); + } + if (printer.getScope() == PrinterData.PAGE_RANGE) { + fAllPages.setSelection(false); + fCurrentPage.setSelection(false); + fPageList.setSelection(false); + fPageRange.setSelection(true); + fFromPage.setEnabled(true); + fToPage.setEnabled(true); + fFromPage.setText((Integer.valueOf(printer.getStartPage())).toString()); + fToPage.setText((Integer.valueOf(printer.getEndPage())).toString()); + } + computeStepXY(); + fOverviewCanvas.redraw(); + } + + /** + * Sets parent wizard page + * + * @param parent The parent wizard page + */ + public void setParentWizardPage(WizardPage parent) { + fParentWizardPage = parent; + } + + /** + * Sets the parent dialog box. + * + * @param parent The parent dialog box. + */ + public void setParentDialog(SDPrintDialog parent) { + fParentDialog = parent; + } + + /** + * Updates the printer status + */ + protected void updatePrinterStatus() { + if (fParentWizardPage != null) { + // used in the wizard dialog + if (fPrinterData == null) { + // show error message and disable Finish button + fParentWizardPage.setErrorMessage(Messages.SequenceDiagram_NoPrinterSelected); + fParentWizardPage.setPageComplete(false); + } else { + // clear error message and enable Finish button + fParentWizardPage.setErrorMessage(null); + fParentWizardPage.setPageComplete(true); + } + } else if (fParentDialog != null) { + // used in the print dialog + if (fPrinterData == null) { + // show error message and disable OK button + fParentDialog.setErrorMessage(Messages.SequenceDiagram_NoPrinterSelected); + fParentDialog.setPageComplete(false); + } else { + // clear error message and enable OK button + fParentDialog.setErrorMessage(null); + fParentDialog.setPageComplete(true); + } + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SearchFilterDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SearchFilterDialog.java new file mode 100755 index 0000000000..3a283402e6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/SearchFilterDialog.java @@ -0,0 +1,451 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessageReturn; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Stop; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessageReturn; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFindProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDGraphNodeSupporter; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * This is the common dialog to define Find and/or Filter Criteria(s) + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class SearchFilterDialog extends Dialog { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The find criteria property name + */ + protected static final String FIND_CRITERIA = "findCriteria"; //$NON-NLS-1$ + /** + * The find expression list property name + */ + protected static final String FIND_EXPRESSION_LIST = "findExpressionList"; //$NON-NLS-1$ + /** + * The filter criteria poperty name + */ + protected static final String FILTER_CRITERIA = "filterCriteria"; //$NON-NLS-1$ + /** + * The filter expression list property name + */ + protected static final String FILTER_EXPRESSION_LIST = "filterExpressionList"; //$NON-NLS-1$ + /** + * The maximum number of expressions stored. + */ + protected static final int MAX_EXPRESSION_LIST = 7; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The sequence diagram view reference. + */ + private final SDView fSdView; + + /** + * The tab with the controls for a Criteria + */ + private final TabFolder fTabFolder = null; + + /** + * The Criteria updated by this dialog + */ + private Criteria fCriteria = null; + + /** + * The find/filter provider telling which graph nodes are supported + */ + private final ISDGraphNodeSupporter fProvider; + + /** + * The okText is the text for the Ok button and title is the title of the + * dialog.<br> + * Both depend (okText and title (below)) on the usage that is done of this + * dialog (find or filter). + */ + private String fOkText; + + /** + * The title is the title of the dialog.<br> + * Both depend (okText and title) on the usage that is done of this dialog + * (find or filter). + */ + private String fTitle; + + /** + * List of string expressions that have been searched already + */ + private String[] fExpressionList; + + /** + * find is true if the dialog is for the find feature and false for filter + * feature + */ + private boolean fIsFind; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Standard constructor + * + * @param view + * A sequence diagram view reference + * @param provider + * A graph node supporter provider + * @param filter + * A flag to indicate filtering (true) or finding (false) + * @param style + * Style bits + */ + public SearchFilterDialog(SDView view, ISDGraphNodeSupporter provider, boolean filter, int style) { + super(view.getSDWidget().getShell()); + setShellStyle(SWT.DIALOG_TRIM | style); + fProvider = provider; + fSdView = view; + fIsFind = !filter; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public Control createDialogArea(Composite arg0) { + if (fIsFind) { + fExpressionList = Activator.getDefault().getDialogSettings().getArray(FIND_EXPRESSION_LIST); + } else { + fExpressionList = Activator.getDefault().getDialogSettings().getArray(FILTER_EXPRESSION_LIST); + } + if (fExpressionList == null) { + fExpressionList = new String[0]; + } + return new TabContents(arg0, fProvider, getButton(IDialogConstants.OK_ID), fExpressionList); + } + + /** + * Open the dialog box + */ + @Override + public int open() { + create(); + + if (fCriteria == null) { + loadCriteria(); + } + + if (fCriteria == null) { + fCriteria = new Criteria(); + fCriteria.setLifeLineSelected(fProvider.isNodeSupported(ISDGraphNodeSupporter.LIFELINE)); + fCriteria.setSyncMessageSelected(fProvider.isNodeSupported(ISDGraphNodeSupporter.SYNCMESSAGE)); + fCriteria.setSyncMessageReturnSelected(fProvider.isNodeSupported(ISDGraphNodeSupporter.SYNCMESSAGERETURN)); + fCriteria.setAsyncMessageSelected(fProvider.isNodeSupported(ISDGraphNodeSupporter.ASYNCMESSAGE)); + fCriteria.setAsyncMessageReturnSelected(fProvider.isNodeSupported(ISDGraphNodeSupporter.ASYNCMESSAGERETURN)); + fCriteria.setStopSelected(fProvider.isNodeSupported(ISDGraphNodeSupporter.STOP)); + } + copyFromCriteria(fCriteria); + + if (fOkText != null) { + getButton(IDialogConstants.OK_ID).setText(fOkText); + } else { + getButton(IDialogConstants.OK_ID).setText(Messages.SequenceDiagram_Find); + } + + if (fIsFind) { + getButton(IDialogConstants.CANCEL_ID).setText(Messages.SequenceDiagram_Close); + } + + Button okButton = getButton(IDialogConstants.OK_ID); + ((TabContents) getDialogArea()).setOkButton(okButton); + if (fCriteria == null || !((fCriteria.getExpression() != null && !fCriteria.getExpression().equals("")) && //$NON-NLS-1$ + (fCriteria.isAsyncMessageReturnSelected() || fCriteria.isAsyncMessageSelected() || fCriteria.isLifeLineSelected() || fCriteria.isStopSelected() || fCriteria.isSyncMessageReturnSelected() || fCriteria.isSyncMessageSelected()))) { + okButton.setEnabled(false); + } + + if (fTitle != null) { + getShell().setText(fTitle); + } else { + getShell().setText(Messages.SequenceDiagram_SequenceDiagramFind); + } + + getShell().pack(); + getShell().setLocation(getShell().getDisplay().getCursorLocation()); + + fCriteria = null; + return super.open(); + } + + /** + * Loads criteria from the dialog settings which are saved in the workspace. + */ + protected void loadCriteria() { + + String CRITERIA = FIND_CRITERIA; + if (!fIsFind) { + CRITERIA = FILTER_CRITERIA; + } + + DialogSettings section = (DialogSettings) Activator.getDefault().getDialogSettings().getSection(CRITERIA); + List<GraphNode> selection = fSdView.getSDWidget().getSelection(); + if ((selection == null || selection.size() != 1) || (!fIsFind)) { + if (section != null) { + fCriteria = new Criteria(); + fCriteria.load(section); + } + } else { + GraphNode gn = selection.get(0); + fCriteria = new Criteria(); + fCriteria.setExpression(gn.getName()); + fCriteria.setCaseSenstiveSelected(true); + if (gn instanceof Lifeline && fProvider.isNodeSupported(ISDGraphNodeSupporter.LIFELINE)) { + fCriteria.setLifeLineSelected(true); + } else if (gn instanceof SyncMessageReturn && fProvider.isNodeSupported(ISDGraphNodeSupporter.SYNCMESSAGERETURN)) { + fCriteria.setSyncMessageReturnSelected(true); + } else if ((gn instanceof SyncMessageReturn || gn instanceof SyncMessage) && fProvider.isNodeSupported(ISDGraphNodeSupporter.SYNCMESSAGE)) { + fCriteria.setSyncMessageSelected(true); + } else if (gn instanceof AsyncMessageReturn && fProvider.isNodeSupported(ISDGraphNodeSupporter.ASYNCMESSAGERETURN)) { + fCriteria.setAsyncMessageReturnSelected(true); + } else if ((gn instanceof AsyncMessageReturn || gn instanceof AsyncMessage) && fProvider.isNodeSupported(ISDGraphNodeSupporter.ASYNCMESSAGE)) { + fCriteria.setAsyncMessageSelected(true); + } else if (gn instanceof Stop && fProvider.isNodeSupported(ISDGraphNodeSupporter.STOP)) { + fCriteria.setStopSelected(true); + } + } + } + + /** + * Called when the dialog box ok button is pressed and calls back the + * appropriate action provider (ISDFilterProvider or ISDFindProvider). + */ + @Override + public void okPressed() { + copyToCriteria(); + if (!fIsFind) { + saveCriteria(); + super.close(); // Filter is modal + } + if ((fProvider != null) && (fProvider instanceof ISDFindProvider) && fIsFind) { + boolean result = ((ISDFindProvider) fProvider).find(fCriteria); + TabContents content = getTabContents(); + content.setResult(result); + } + } + + @Override + public void cancelPressed() { + if (fIsFind) { + copyToCriteria(); + if (fProvider instanceof ISDFindProvider) { + ((ISDFindProvider) fProvider).cancel(); + } + saveCriteria(); + } + super.cancelPressed(); + } + + /** + * Saves the criteria to the dialog settings within the workspace. + */ + public void saveCriteria() { + String CRITERIA = FIND_CRITERIA; + String EXPRESSION_LIST = FIND_EXPRESSION_LIST; + if (!fIsFind) { + CRITERIA = FILTER_CRITERIA; + EXPRESSION_LIST = FILTER_EXPRESSION_LIST; + } + DialogSettings settings = (DialogSettings) Activator.getDefault().getDialogSettings(); + DialogSettings section = (DialogSettings) settings.getSection(CRITERIA); + if (section == null) { + section = (DialogSettings) settings.addNewSection(CRITERIA); + } + fCriteria.save(section); + + if (fCriteria.getExpression().length() > 0) { + ArrayList<String> list = new ArrayList<>(); + for (int i = 0; i < fExpressionList.length; i++) { + list.add(fExpressionList[i]); + } + // Remove the used expression if one from the dropdown list + list.remove(fCriteria.getExpression()); + // Put the new expression at the beginning + list.add(0, fCriteria.getExpression()); + // Fill in the expressionList, truncating to MAX_EXPRESSION_LIST + int size = Math.min(list.size(), MAX_EXPRESSION_LIST); + String[] temp = new String[size]; + for (int i = 0; i < size; i++) { + temp[i] = list.get(i); + } + fExpressionList = temp; + settings.put(EXPRESSION_LIST, fExpressionList); + } + } + + /** + * Returns the criteria + * + * @return the criteria + */ + public Criteria getCriteria() { + return fCriteria; + } + + /** + * Sets the criteria. + * + * @param criteria + * the criteria to set. + */ + public void setCriteria(Criteria criteria) { + fCriteria = criteria; + } + + /** + * Get the current end-user settings from the dialog to a Criteria + */ + public void copyToCriteria() { + fCriteria = new Criteria(); + TabContents content = getTabContents(); + fCriteria.setLifeLineSelected(content.isLifelineButtonSelected()); + fCriteria.setSyncMessageSelected(content.isSynMessageButtonSelected()); + fCriteria.setSyncMessageReturnSelected(content.isSynMessageReturnButtonSelected()); + fCriteria.setAsyncMessageSelected(content.isAsynMessageButtonSelected()); + fCriteria.setAsyncMessageReturnSelected(content.isAsynMessageReturnButtonSelected()); + fCriteria.setStopSelected(content.isStopButtonSelected()); + fCriteria.setCaseSenstiveSelected(content.isCaseSensitiveSelected()); + fCriteria.setExpression(content.getSearchText()); + } + + /** + * Returns the tab content reference. + * + * @return the tab content + */ + protected TabContents getTabContents() { + TabContents content = null; + if (fTabFolder == null) { + content = (TabContents) getDialogArea(); + } else { + content = (TabContents) fTabFolder.getSelection()[0].getControl(); + } + return content; + } + + /** + * Initialize the dialog with the settings of an existing Criteria<br> + * Criteria must not be null and the TabContents must have been created + * + * @param from + * the criteria to copy from + */ + public void copyFromCriteria(Criteria from) { + TabContents content = getTabContents(); + content.setLifelineButtonSelection(from.isLifeLineSelected()); + content.setSynMessageButtonSelection(from.isSyncMessageSelected()); + content.setSynMessageReturnButtonSelection(from.isSyncMessageReturnSelected()); + content.setAsynMessageButtonSelection(from.isAsyncMessageSelected()); + content.setAsynMessageReturnButtonSelection(from.isSyncMessageReturnSelected()); + content.setStopButtonSelection(from.isStopSelected()); + content.setCaseSensitiveSelection(from.isCaseSenstiveSelected()); + if (from.getExpression() != null) { + content.setSearchText(from.getExpression()); + } + } + + /** + * Sets the text to be used for the ok button + * + * @param okText + * text to set + */ + public void setOkText(String okText) { + fOkText = okText; + } + + /** + * Sets the title to be used for the dialog box. + * + * @param title + * The title to set + */ + public void setTitle(String title) { + fTitle = title; + } + + /** + * Gets the text to be used for the ok button + * + * @return the text to be used for the ok button + * @since 2.0 + */ + public String getOkText() { + return fOkText; + } + + /** + * Sets the IsFind flag (true for find, else for filter) + * + * @param flag value to set + * @since 2.0 + */ + protected void setIsFind(boolean flag) { + fIsFind = flag; + } + + /** + * Gets the title to be used for the dialog box. + * + * @return the title to be used for the dialog box. + * @since 2.0 + */ + public String getTitle() { + return fTitle; + } + + /** + * Gets the IsFind flag (true for find, else for filter) + * + * @return true for find, else for filter + * @since 2.0 + */ + protected boolean isFind() { + return fIsFind; + } + + + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/TabContents.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/TabContents.java new file mode 100755 index 0000000000..3b5f98b090 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/dialogs/TabContents.java @@ -0,0 +1,476 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDGraphNodeSupporter; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Class implementation contains the controls that allows to create or update a find or filter Criteria. + * + * @version 1.0 + * @author sveyrier + */ +public class TabContents extends Composite { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The button for lifelines. + */ + private Button fLifelineButton; + /** + * The button for stops. + */ + private Button fStopButton = null; + /** + * The button for synchronous messages + */ + private Button fSynMessageButton = null; + /** + * The button for synchronous return messages + */ + private Button fSynMessageReturnButton = null; + /** + * The button for asynchronous messages + */ + private Button fAsynMessageButton = null; + /** + * The button for asynchronous return messages + */ + private Button fAsynMessageReturnButton = null; + /** + * The search text combo box. + */ + private Combo fSearchText = null; + /** + * The button for case sensitive expressions. + */ + private Button fCaseSensitive = null; + /** + * The label for the result string. + */ + private Label fResult = null; + /** + * The button for notifying parent about valid data. + */ + private Button fParentOkButton = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Creates the dialog contents + * + * @param parent the parent widget + * @param provider the provider which handle the action + * @param okButton of the dialog (to be enabled/disabled) + * @param expressionList list of strings already searched for + */ + public TabContents(Composite parent, ISDGraphNodeSupporter provider, Button okButton, String[] expressionList) { + super(parent, SWT.NONE); + fParentOkButton = okButton; + setLayout(new GridLayout()); + + GraphNodeTypeListener graphNodeTypeListener = new GraphNodeTypeListener(); + ExpressionListener expressionListener = new ExpressionListener(); + + // Inform the user how to fill the string to search + Label searchTitle = new Label(this, SWT.LEFT); + searchTitle.setText(Messages.SequenceDiagram_MatchingString); + Composite searchPart = new Composite(this, SWT.NONE); + GridData searchPartData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL); + GridLayout searchPartLayout = new GridLayout(); + searchPartLayout.numColumns = 2; + searchPart.setLayout(searchPartLayout); + searchPart.setLayoutData(searchPartData); + + // Create the user string input area + fSearchText = new Combo(searchPart, SWT.DROP_DOWN); + GridData comboData = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL); + /* + * GridData tabLayoutData2 = new GridData(GridData.HORIZONTAL_ALIGN_FILL| GridData.VERTICAL_ALIGN_FILL); + */ + fSearchText.setLayoutData(comboData); + if (expressionList != null) { + for (int i = 0; i < expressionList.length; i++) { + fSearchText.add(expressionList[i]); + } + } + fSearchText.addModifyListener(expressionListener); + + // Create the case sensitive check button + fCaseSensitive = new Button(searchPart, SWT.CHECK); + fCaseSensitive.setText(Messages.SequenceDiagram_CaseSensitive); + + // Create the group for searched graph node kind selection + Group kindSelection = new Group(this, SWT.SHADOW_NONE); + kindSelection.setText(Messages.SequenceDiagram_SearchFor); + // kindSelection.setLayoutData(tabLayoutData2); + GridLayout kindSelectionLayout = new GridLayout(); + kindSelectionLayout.numColumns = 1; + kindSelection.setLayout(kindSelectionLayout); + GridData kindSelectionData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL); + kindSelection.setLayoutData(kindSelectionData); + + // Create the lifeline check button + if (provider != null && provider.isNodeSupported(ISDGraphNodeSupporter.LIFELINE)) { + fLifelineButton = new Button(kindSelection, SWT.CHECK); + String nodeName = provider.getNodeName(ISDGraphNodeSupporter.LIFELINE, null); + if (nodeName != null) { + fLifelineButton.setText(nodeName); + } else { + fLifelineButton.setText(Messages.SequenceDiagram_Lifeline); + } + fLifelineButton.setEnabled(true); + fLifelineButton.addSelectionListener(graphNodeTypeListener); + } + + if (provider != null && provider.isNodeSupported(ISDGraphNodeSupporter.STOP)) { + // Create the stop check button + fStopButton = new Button(kindSelection, SWT.CHECK); + String nodeName = provider.getNodeName(ISDGraphNodeSupporter.STOP, null); + if (nodeName != null) { + fStopButton.setText(nodeName); + } else { + fStopButton.setText(Messages.SequenceDiagram_Stop); + } + + fStopButton.setEnabled(true); + fStopButton.addSelectionListener(graphNodeTypeListener); + } + + if (provider != null && provider.isNodeSupported(ISDGraphNodeSupporter.SYNCMESSAGE)) { + // Create the synchronous message check button + fSynMessageButton = new Button(kindSelection, SWT.CHECK); + String nodeName = provider.getNodeName(ISDGraphNodeSupporter.SYNCMESSAGE, null); + if (nodeName != null) { + fSynMessageButton.setText(nodeName); + } else { + fSynMessageButton.setText(Messages.SequenceDiagram_SynchronousMessage); + } + fSynMessageButton.setEnabled(true); + fSynMessageButton.addSelectionListener(graphNodeTypeListener); + } + + if (provider != null && provider.isNodeSupported(ISDGraphNodeSupporter.SYNCMESSAGERETURN)) { + // Create the synchronous message return check button + fSynMessageReturnButton = new Button(kindSelection, SWT.CHECK); + String nodeName = provider.getNodeName(ISDGraphNodeSupporter.SYNCMESSAGERETURN, null); + if (nodeName != null) { + fSynMessageReturnButton.setText(nodeName); + } else { + fSynMessageReturnButton.setText(Messages.SequenceDiagram_SynchronousMessageReturn); + } + fSynMessageReturnButton.setEnabled(true); + fSynMessageReturnButton.addSelectionListener(graphNodeTypeListener); + } + + if (provider != null && provider.isNodeSupported(ISDGraphNodeSupporter.ASYNCMESSAGE)) { + // Create the asynchronous message check button + fAsynMessageButton = new Button(kindSelection, SWT.CHECK); + String nodeName = provider.getNodeName(ISDGraphNodeSupporter.ASYNCMESSAGE, null); + if (nodeName != null) { + fAsynMessageButton.setText(nodeName); + } else { + fAsynMessageButton.setText(Messages.SequenceDiagram_AsynchronousMessage); + } + fAsynMessageButton.setEnabled(true); + fAsynMessageButton.addSelectionListener(graphNodeTypeListener); + } + + if (provider != null && provider.isNodeSupported(ISDGraphNodeSupporter.ASYNCMESSAGERETURN)) { + // Create the asynchronous message return check button + fAsynMessageReturnButton = new Button(kindSelection, SWT.CHECK); + String nodeName = provider.getNodeName(ISDGraphNodeSupporter.ASYNCMESSAGERETURN, null); + if (nodeName != null) { + fAsynMessageReturnButton.setText(nodeName); + } else { + fAsynMessageReturnButton.setText(Messages.SequenceDiagram_AsynchronousMessageReturn); + } + fAsynMessageReturnButton.setEnabled(true); + fAsynMessageReturnButton.addSelectionListener(graphNodeTypeListener); + } + + fResult = new Label(this, SWT.LEFT); + fResult.setText(Messages.SequenceDiagram_StringNotFound); + fResult.setVisible(false); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Set result text visibility + * @param found <code>true</code> for found (enable visibility) else false + */ + public void setResult(boolean found) { + fResult.setVisible(!found); + } + + /** + * Updates parent OK button based on input data. + */ + public void updateOkButton() { + if (fParentOkButton == null) { + return; + } + boolean enabled = (fSearchText.getText() != null && !fSearchText.getText().equals("")) && //$NON-NLS-1$ + (isLifelineButtonSelected() || isStopButtonSelected() || isSynMessageButtonSelected() || isSynMessageReturnButtonSelected() || isAsynMessageButtonSelected() || isAsynMessageReturnButtonSelected()); + fParentOkButton.setEnabled(enabled); + } + + /** + * Sets the parent OK button reference. + * + * @param okButton The parent OK button + */ + public void setOkButton(Button okButton) { + fParentOkButton = okButton; + } + + /** + * Returns the asynchronous message check button state + * + * @return true if check, false otherwise + */ + public boolean isAsynMessageButtonSelected() { + if (fAsynMessageButton != null) { + return fAsynMessageButton.getSelection(); + } + return false; + } + + /** + * Returns the asynchronous message return check button state + * + * @return true if check, false otherwise + */ + public boolean isAsynMessageReturnButtonSelected() { + if (fAsynMessageReturnButton != null) { + return fAsynMessageReturnButton.getSelection(); + } + return false; + } + + /** + * Returns the case sensitive check button state + * + * @return true if check, false otherwise + */ + public boolean isCaseSensitiveSelected() { + if (fCaseSensitive != null) { + return fCaseSensitive.getSelection(); + } + return false; + } + + /** + * Returns the lifeline check button state + * + * @return true if check, false otherwise + */ + public boolean isLifelineButtonSelected() { + if (fLifelineButton != null) { + return fLifelineButton.getSelection(); + } + return false; + } + + /** + * Returns the user input string + * + * @return the string to search for + */ + public String getSearchText() { + return fSearchText.getText(); + } + + /** + * Returns the stop check button state + * + * @return true if check, false otherwise + */ + public boolean isStopButtonSelected() { + if (fStopButton != null) { + return fStopButton.getSelection(); + } + return false; + } + + /** + * Returns the synchronous message check button state + * + * @return true if check, false otherwise + */ + public boolean isSynMessageButtonSelected() { + if (fSynMessageButton != null) { + return fSynMessageButton.getSelection(); + } + return false; + } + + /** + * Returns the synchronous message return check button state + * + * @return true if check, false otherwise + */ + public boolean isSynMessageReturnButtonSelected() { + if (fSynMessageReturnButton != null) { + return fSynMessageReturnButton.getSelection(); + } + return false; + } + + /** + * Set the asynchronous message check button state + * + * @param state + * The new state to set + */ + public void setAsynMessageButtonSelection(boolean state) { + if (fAsynMessageButton != null) { + fAsynMessageButton.setSelection(state); + } + } + + /** + * Set the asynchronous message return check button state + * + * @param state + * The new state to set + */ + public void setAsynMessageReturnButtonSelection(boolean state) { + if (fAsynMessageReturnButton != null) { + fAsynMessageReturnButton.setSelection(state); + } + } + + /** + * Set the case sensitive check button state + * + * @param state + * The new state to set + */ + public void setCaseSensitiveSelection(boolean state) { + if (fCaseSensitive != null) { + fCaseSensitive.setSelection(state); + } + } + + /** + * Set the lifeline check button state + * + * @param state + * The new state to set + */ + public void setLifelineButtonSelection(boolean state) { + if (fLifelineButton != null) { + fLifelineButton.setSelection(state); + } + } + + /** + * Set the user input string + * + * @param text + * The search text + */ + public void setSearchText(String text) { + fSearchText.setText(text); + } + + /** + * Set the stop check button state + * + * @param state + * The new state to set + */ + public void setStopButtonSelection(boolean state) { + if (fStopButton != null) { + fStopButton.setSelection(state); + } + } + + /** + * Set the synchronous message check button state + * + * @param state + * The new state to set + */ + public void setSynMessageButtonSelection(boolean state) { + if (fSynMessageButton != null) { + fSynMessageButton.setSelection(state); + } + } + + /** + * Set the synchronous message return check button state + * + * @param state + * The new state to set + */ + public void setSynMessageReturnButtonSelection(boolean state) { + if (fSynMessageReturnButton != null) { + fSynMessageReturnButton.setSelection(state); + } + } + + // ------------------------------------------------------------------------ + // Helper classes + // ------------------------------------------------------------------------ + + /** + * Selection listener implementation for graph node types. + * @version 1.0 + */ + protected class GraphNodeTypeListener implements SelectionListener { + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // Nothing to do + } + + @Override + public void widgetSelected(SelectionEvent e) { + updateOkButton(); + } + } + + /** + * Modify listener implementation for the expression field. + * + * @version 1.0 + */ + protected class ExpressionListener implements ModifyListener { + @Override + public void modifyText(ModifyEvent e) { + updateOkButton(); + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IColor.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IColor.java new file mode 100755 index 0000000000..2b5e2e1bf7 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IColor.java @@ -0,0 +1,36 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings; + +/** + * Interface for handling a color resource. + * + * @version 1.0 + * @author sveyrier + */ +public interface IColor { + + /** + * Returns the contained color. Returned object must be an instance of org.eclipse.swt.graphics.Color if used with + * the org.eclipse.linuxtools.tmf.ui.views.uml2sd.NGC graphical context + * + * @return the color + */ + Object getColor(); + + /** + * Disposes the color + */ + void dispose(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IFont.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IFont.java new file mode 100755 index 0000000000..c2196e06af --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IFont.java @@ -0,0 +1,36 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings; + +/** + * Interface for handling a font resource. + * + * @version 1.0 + * @author sveyrier + */ +public interface IFont { + + /** + * Returns the contained font. Returned object must be an instance of org.eclipse.swt.graphics.Font if used with the + * org.eclipse.linuxtools.tmf.ui.views.uml2sd.NGC graphical context + * + * @return the font + */ + Object getFont(); + + /** + * Disposes the font + */ + void dispose(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IGC.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IGC.java new file mode 100755 index 0000000000..c26bb5ceed --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IGC.java @@ -0,0 +1,375 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings; + +/** + * Interface for a graphical context. + * + * @version 1.0 + * @author sveyrier + * + */ +public interface IGC { + + /** + * Set the current line style + * + * @param style the new line style + */ + void setLineStyle(int style); + + /** + * Returns current the line style used in the graphical context + * + * @return the current line style + */ + int getLineStyle(); + + /** + * Returns the contents x coordinate that is at the upper left corner of the view + * + * @return the contents x coordinate + */ + int getContentsX(); + + /** + * Returns the contents y coordinate that is at the upper left corner of the view + * + * @return the contents y coordinate + */ + int getContentsY(); + + /** + * Returns the contents visible width + * + * @return the contents width + */ + int getVisibleWidth(); + + /** + * Returns the contents visible height + * + * @return the contents height + */ + int getVisibleHeight(); + + /** + * Translates the given contents x coordinate into view x coordinate + * + * @param x the x coordinate to translate + * @return the corresponding view x coordinate + */ + int contentsToViewX(int x); + + /** + * Translates the given contents y coordinate into view y coordinate + * + * @param y the y coordinate to translate + * @return the corresponding view y coordinate + */ + int contentsToViewY(int y); + + /** + * Draws a line, using the foreground color, between the points (x1, y1) and (x2, y2). + * + * @param x1 the first point's x coordinate + * @param y1 the first point's y coordinate + * @param x2 the second point's x coordinate + * @param y2 the second point's y coordinate + */ + void drawLine(int x1, int y1, int x2, int y2); + + /** + * Draws the outline of the rectangle specified by the arguments, using the receiver's foreground color. The left + * and right edges of the rectangle are at x and x + width. The top and bottom edges are at y and y + height. + * + * @param x the x coordinate of the rectangle to be drawn + * @param y the y coordinate of the rectangle to be drawn + * @param width the width of the rectangle to be drawn + * @param height the height of the rectangle to be drawn + */ + void drawRectangle(int x, int y, int width, int height); + + /** + * Draws a rectangle, based on the specified arguments, which has the appearance of the platform's focus rectangle + * if the platform supports such a notion, and otherwise draws a simple rectangle in the receiver's foreground + * color. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + void drawFocus(int x, int y, int width, int height); + + /** + * Fills the interior of the closed polygon which is defined by the specified array of integer coordinates, using + * the receiver's background color. The array contains alternating x and y values which are considered to represent + * points which are the vertices of the polygon. Lines are drawn between each consecutive pair, and between the + * first pair and last pair in the array. + * + * @param points an array of alternating x and y values which are the vertices of the polygon + */ + void fillPolygon(int[] points); + + /** + * Draws the closed polygon which is defined by the specified array of integer coordinates, using the receiver's + * foreground color. The array contains alternating x and y values which are considered to represent points which + * are the vertices of the polygon. Lines are drawn between each consecutive pair, and between the first pair and + * last pair in the array. + * + * @param points an array of alternating x and y values which are the vertices of the polygon + */ + void drawPolygon(int[] points); + + /** + * Fills the interior of the rectangle specified by the arguments, using the receiver's background color. + * + * @param x the x coordinate of the rectangle to be filled + * @param y the y coordinate of the rectangle to be filled + * @param width the width of the rectangle to be filled + * @param height the height of the rectangle to be filled + */ + void fillRectangle(int x, int y, int width, int height); + + /** + * Fills the interior of the specified rectangle with a gradient sweeping from left to right or top to bottom + * progressing from the graphical context gradient color to its background color. + * + * @param x the x coordinate of the rectangle to be filled + * @param y the y coordinate of the rectangle to be filled + * @param width the width of the rectangle to be filled, may be negative (inverts direction of gradient if + * horizontal) + * @param height the height of the rectangle to be filled, may be negative (inverts direction of gradient if + * horizontal) + * @param vertical if true sweeps from top to bottom, else sweeps from left to right + */ + void fillGradientRectangle(int x, int y, int width, int height, boolean vertical); + + /** + * Returns the given string width in pixels + * + * @param name the string + * @return the string width + */ + int textExtent(String name); + + /** + * Draws the given string, using the receiver's current font and foreground color. Tab expansion and carriage return + * processing are performed. If trans is true, then the background of the rectangular area where the text is being + * drawn will not be modified, otherwise it will be filled with the receiver's background color. + * + * @param string the string to be drawn + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param trans if true the background will be transparent, otherwise it will be opaque + */ + void drawText(String string, int x, int y, boolean trans); + + /** + * Draws the given string, using the receiver's current font and foreground color. Tab expansion and carriage return + * processing are performed. The background of the rectangular area where the text is being drawn will be filled + * with the receiver's background color. + * + * @param string the string to be drawn + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + */ + void drawText(String string, int x, int y); + + /** + * Fills the interior of an oval, within the specified rectangular area, with the receiver's background color. + * + * @param x the x coordinate of the upper left corner of the oval to be filled + * @param y the y coordinate of the upper left corner of the oval to be filled + * @param width the width of the oval to be filled + * @param height the width of the oval to be filled + */ + void fillOval(int x, int y, int width, int height); + + /** + * Returns current the background color used in the graphical context + * + * @return the background color + */ + IColor getBackground(); + + /** + * Returns current the background color used in the graphical context + * + * @return the background color + */ + IColor getForeground(); + + /** + * Set the graphical context foreground color + * + * @param color the foreground color + */ + void setBackground(IColor color); + + /** + * Set the graphical context background color + * + * @param color the background color + */ + void setForeground(IColor color); + + /** + * Set the color to use when filling regions using gradient. The color will progess from the given color to the + * current background color + * + * @param color the gardiient color to use + */ + void setGradientColor(IColor color); + + /** + * Set the line width to use for drawing + * + * @param width the line width + */ + void setLineWidth(int width); + + /** + * Returns the current graphical context line width used for drawing + * + * @return the line width + */ + int getLineWidth(); + + /** + * Returns the LineDotD style constant + * + * @return the constant value + */ + int getLineDotStyle(); + + /** + * Returns the LineDash style constant + * + * @return the constant + */ + int getLineDashStyle(); + + /** + * Returns the LineSolid style constant + * + * @return the constant + */ + int getLineSolidStyle(); + + /** + * Draws the given string centered into the given rectangle. If the string cannot fit in the rectangle area, the + * string is truncated. If trans is true, then the background of the rectangular area where the text is being drawn + * will not be modified, otherwise it will be filled with the receiver's background color. + * + * @param name the string to draw + * @param x the x coordinate of the rectangle to draw the string + * @param y the y coordinate of the rectangle to draw the string + * @param width the width of the rectangle to draw the string + * @param height the height of the rectangle to draw the string + * @param trans if true the background will be transparent, otherwise it will be opaque + */ + void drawTextTruncatedCentred(String name, int x, int y, int width, int height, boolean trans); + + /** + * Draws the given string into the given rectangle (left justify) If the string cannot fit in the rectangle area, + * the string is truncated. If trans is true, then the background of the rectangular area where the text is being + * drawn will not be modified, otherwise it will be filled with the receiver's background color. + * + * @param name The text to put in the rectangle + * @param x the x coordinate of the rectangle to draw the string + * @param y the y coordinate of the rectangle to draw the string + * @param width the width of the rectangle to draw the string + * @param height the height of the rectangle to draw the string + * @param trans if true the background will be transparent, otherwise it will be opaque + */ + void drawTextTruncated(String name, int x, int y, int width, int height, boolean trans); + + /** + * Copies a the source image into a (potentially different sized) rectangular area in the graphical context. If the + * source image has smaller sizes, then the source area will be stretched to fit the destination area as it is + * copied. + * + * @param image the image to draw + * @param x the x coordinate in the destination to copy to + * @param y the y coordinate in the destination to copy to + * @param maxWith the width in pixels of the destination rectangle + * @param maxHeight the height in pixels of the destination rectangle + */ + void drawImage(IImage image, int x, int y, int maxWith, int maxHeight); + + /** + * Draws the outline of a circular or elliptical arc within the specified rectangular area. The resulting arc begins + * at startAngle and extends for arcAngle degrees, using the current color. Angles are interpreted such that 0 + * degrees is at the 3 o'clock position. A positive value indicates a counter-clockwise rotation while a negative + * value indicates a clockwise rotation. The center of the arc is the center of the rectangle whose origin is (x, y) + * and whose size is specified by the width and height arguments. The resulting arc covers an area width + 1 pixels + * wide by height + 1 pixels tall. + * + * @param x the x coordinate of the upper-left corner of the arc to be drawn + * @param y the y coordinate of the upper-left corner of the arc to be drawn + * @param width the width of the arc to be drawn + * @param height the height of the arc to be drawn + * @param startAngle the beginning angle + * @param endAngle the ending angle + */ + void drawArc(int x, int y, int width, int height, int startAngle, int endAngle); + + /** + * Set the current font used in the graphical context + * + * @param font the font to use + */ + void setFont(IFont font); + + /** + * Returns the font height given font + * + * @param font The font to check for + * @return the the font height + */ + int getFontHeight(IFont font); + + /** + * Returns the average character width for the given font + * + * @param font The font to check for + * @return the average width + */ + int getFontWidth(IFont font); + + /** + * Creates a color with the given RGB values + * + * @param r the red component + * @param g the green component + * @param b the blue component + * @return the color + */ + IColor createColor(int r, int g, int b); + + /** + * Returns the zoom factor applied in both x and y directions when drawing + * + * @return the zoom factor + */ + float getZoom(); + + /** + * Draws text with focus style. + * + * @param focus <code>true</code> if item has focus else <code>false</code> + */ + void setDrawTextWithFocusStyle(boolean focus); +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IImage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IImage.java new file mode 100755 index 0000000000..eac7ee28ff --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/IImage.java @@ -0,0 +1,37 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings; + +/** + * Interface for handling a image resource. + * + * @version 1.0 + * @author sveyrier + * + */ +public interface IImage { + + /** + * Returns the contained image. Returned object must be an instance of org.eclipse.swt.graphics.Image if used with + * the org.eclipse.linuxtools.tmf.ui.views.uml2sd.NGC graphical context + * + * @return the color + */ + Object getImage(); + + /** + * Disposes the image + */ + void dispose(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/ColorImpl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/ColorImpl.java new file mode 100755 index 0000000000..25b5a689ac --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/ColorImpl.java @@ -0,0 +1,92 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.impl; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; + +/** + * Default implementation of the IColor interface. + * + * @version 1.0 + * @author sveyrier + * + */ +public class ColorImpl implements IColor { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The color object. + */ + private final Color fColor; + /** + * Flag to indicate that this object is managing the resource. + */ + private boolean fManageColor = true; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Constructor + * + * @param display The display to use + * @param r A value for red + * @param g A value for green + * @param b A value for blue + */ + public ColorImpl(Display display, int r, int g, int b) { + fColor = new Color(display, r, g, b); + } + + /** + * Copy constructor + * + * @param color + * A color to copy + */ + protected ColorImpl(Color color) { + fColor = color; + fManageColor = false; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Returns a system color. + * + * @param color The color ID + * @return a system color + */ + public static ColorImpl getSystemColor(int color) { + return new ColorImpl(Display.getDefault().getSystemColor(color)); + } + + @Override + public Object getColor() { + return fColor; + } + + @Override + public void dispose() { + if ((fColor != null) && (fManageColor)) { + fColor.dispose(); + } + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/FontImpl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/FontImpl.java new file mode 100755 index 0000000000..1b1cbcf890 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/FontImpl.java @@ -0,0 +1,89 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.impl; + +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IFont; + +/** + * Default implementation of the IFont interface. + * + * @version 1.0 + * @author sveyrier + * + */ +public class FontImpl implements IFont { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The font object + */ + private final Font fFont; + /** + * Flag to indicate that this object is managing the resource. + */ + protected boolean fManageFont = true; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor. + * + * @param display The display to use + * @param data A font data + */ + public FontImpl(Display display, FontData data) { + fFont = new Font(display, data); + } + + /** + * Copy constructor + * + * @param value A font to copy. + */ + protected FontImpl(Font value) { + fFont = value; + fManageFont = false; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Returns a font implementation based system font. + * + * @return a font implementation based system font. + */ + public static FontImpl getSystemFont() { + return new FontImpl(Display.getDefault().getSystemFont()); + } + + @Override + public Object getFont() { + return fFont; + } + + @Override + public void dispose() { + if (fFont != null) { + fFont.dispose(); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/ImageImpl.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/ImageImpl.java new file mode 100755 index 0000000000..5637b88981 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/drawings/impl/ImageImpl.java @@ -0,0 +1,105 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.impl; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IImage; + +/** + * Default implementation of the IImage interface. + * + * @version 1.0 + * @author sveyrier + * + */ +public class ImageImpl implements IImage { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The image reference + */ + private final Image fImage; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor. + * + * @param file A file name of image file. + */ + public ImageImpl(String file) { + fImage = createResourceImage(file); + } + + /** + * Copy constructor + * + * @param image THe image to copy + */ + public ImageImpl(Image image) { + fImage = image; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + /** + * Returns Image object from file name. + * + * @param name File name of image file + * @return image object or <code>null</code> + */ + public Image getResourceImage(String name) { + return createResourceImage(name); + } + + @Override + public Object getImage() { + return fImage; + } + + @Override + public void dispose() { + if (fImage != null) { + fImage.dispose(); + } + } + + /** + * Returns Image object from file name. + * + * @param name File name of image file + * @return image object or <code>null</code> + */ + private static Image createResourceImage(String name) { + try { + URL BASIC_URL = new URL("platform", "localhost", "plugin");//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + URL url = new URL(BASIC_URL, "plugin/org.eclipse.linuxtools.tmf.ui/icons/" + name);//$NON-NLS-1$ + ImageDescriptor img = ImageDescriptor.createFromURL(url); + return img.createImage(); + } catch (MalformedURLException e) { + Activator.getDefault().logError("Error opening image file", e); //$NON-NLS-1$ + } + return null; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/BaseSDAction.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/BaseSDAction.java new file mode 100644 index 0000000000..e45fd20d28 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/BaseSDAction.java @@ -0,0 +1,89 @@ +/********************************************************************** + * Copyright (c) 2013 Ericsson + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.jface.action.Action; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; + +/** + * Base class for sequence diagram actions. + * + * @author Bernd Hufmann + * @since 2.0 + */ +public class BaseSDAction extends Action { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The sequence diagram view reference. + */ + private SDView fView = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public BaseSDAction() { + this(null); + } + + /** + * Constructor + * + * @param view + * a sequence diagram view reference + */ + public BaseSDAction(SDView view) { + super(); + fView = view; + } + + /** + * Constructor + * @param view + * a sequence diagram view reference + * @param text + * The action text + * @param style + * The style + */ + protected BaseSDAction(SDView view, String text, int style) { + super(text, style); + fView = view; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * Sets the active SD view. + * + * @param view The SD view. + */ + public void setView(SDView view) { + fView = view; + } + + /** + * Gets the active SD view. + * + * @return view The SD view. + * @since 2.0 + */ + public SDView getView() { + return fView; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ConfigureMinMax.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ConfigureMinMax.java new file mode 100755 index 0000000000..21ee20f259 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ConfigureMinMax.java @@ -0,0 +1,46 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.MinMaxDialog; + +/** + * Action class implementation to configure minimum and maximum time range values. + * + * @version 1.0 + * @author sveyrier + * + */ +public class ConfigureMinMax extends BaseSDAction { + + /** + * Constructor + * @param view + * the sequence diagram view reference + * @since 2.0 + */ + public ConfigureMinMax(SDView view) { + super(view); + } + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + @Override + public void run() { + if ((getView() != null) && (getView().getSDWidget() != null)) { + MinMaxDialog minMax = new MinMaxDialog(getView().getSite().getShell(), getView().getSDWidget()); + minMax.open(); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/FirstPage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/FirstPage.java new file mode 100644 index 0000000000..33476c7dc0 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/FirstPage.java @@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Action class implementation to move the focus to the first page of the whole sequence diagram. + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class FirstPage extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.firstpage"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor + * + * @param view the view reference + */ + public FirstPage(SDView view) { + super(view); + setText(Messages.SequenceDiagram_FirstPage); + setToolTipText(Messages.SequenceDiagram_GoToFirstPage); + setId(ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FIRST_PAGE)); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if ((getView() == null) || (getView().getSDWidget()) == null) { + return; + } + if (getView().getSDPagingProvider() != null) { + getView().getSDPagingProvider().firstPage(); + } + getView().updateCoolBar(); + getView().getSDWidget().redraw(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/KeyBindingsManager.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/KeyBindingsManager.java new file mode 100644 index 0000000000..c4e9c8d3ae --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/KeyBindingsManager.java @@ -0,0 +1,302 @@ +/********************************************************************** + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; + +/** + * <p> + * Singleton class that manages key-bindings for certain commands across multiple sequence + * diagram view instances. + * </p> + * + * @version 1.0 + * @author Bernd Hufmann + * + */ +public class KeyBindingsManager { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The singleton instance. + */ + private static KeyBindingsManager fInstance = null; + /** + * The list of view names. + */ + private Set<String> fViews = new HashSet<>(); + /** + * The list of activations Activations to store + */ + private List<IHandlerActivation> fHandlerActivations = new ArrayList<>(); + /** + * The action reference for moving to a message in view. + */ + private MoveToMessage fGoToMessageForKeyBinding; + /** + * The action reference for opening the find dialog. + */ + private OpenSDFindDialog fFindForKeyBinding; + /** + * The action reference for moving up in view. + */ + private MoveSDUp fMoveUpForKeyBinding; + /** + * The action reference for moving down in view. + */ + private MoveSDDown fMoveDownForKeyBinding; + /** + * The action reference for moving left in view. + */ + private MoveSDLeft fMoveLeftForKeyBinding; + /** + * The action reference for moving right in view. + */ + private MoveSDRight fMoveRightForKeyBinding; + /** + * The action reference for showing node start. + */ + private ShowNodeStart fShowNodeStartForKeyBinding; + /** + * The action reference for showing node end. + */ + private ShowNodeEnd fShowNodeEndForKeyBinding; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructor + */ + protected KeyBindingsManager() { + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + /** + * Returns the KeyBindingsManager singleton instance. + * + * @return the KeyBindingsManager singleton instance + */ + public static synchronized KeyBindingsManager getInstance() { + if (fInstance == null) { + fInstance = new KeyBindingsManager(); + } + return fInstance; + } + + /** + * Adds a view list of managed view list. + * + * @param viewId Id of SD view to add and to manage + */ + public void add(String viewId) { + + if (fViews.isEmpty()) { + initialize(); + } + + if(!fViews.contains(viewId)) { + fViews.add(viewId); + } + } + + /** + * Removes a view from managed view list + * + * @param viewId Id of SD view to remove + */ + public void remove(String viewId) { + if (fViews.contains(viewId)) { + fViews.remove(viewId); + } + if (fViews.isEmpty()) { + dispose(); + } + } + + /* + * Initialized the KeyBindingsManager. + */ + private void initialize() { + fGoToMessageForKeyBinding = new MoveToMessage(); + IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(IHandlerService.class); + AbstractHandler handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fGoToMessageForKeyBinding.run(); + return null; + } + }; + IHandlerActivation activation = service.activateHandler(fGoToMessageForKeyBinding.getActionDefinitionId(), handler); + fHandlerActivations.add(activation); + + fMoveUpForKeyBinding = new MoveSDUp(); + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fMoveUpForKeyBinding.run(); + return null; + } + }; + activation = service.activateHandler(fMoveUpForKeyBinding.getActionDefinitionId(), handler); + fHandlerActivations.add(activation); + + fMoveDownForKeyBinding = new MoveSDDown(); + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fMoveDownForKeyBinding.run(); + return null; + } + }; + activation = service.activateHandler(fMoveDownForKeyBinding.getActionDefinitionId(), handler); + fHandlerActivations.add(activation); + + fMoveLeftForKeyBinding = new MoveSDLeft(); + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fMoveLeftForKeyBinding.run(); + return null; + } + }; + activation = service.activateHandler(fMoveLeftForKeyBinding.getActionDefinitionId(), handler); + fHandlerActivations.add(activation); + + fMoveRightForKeyBinding = new MoveSDRight(); + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fMoveRightForKeyBinding.run(); + return null; + } + }; + activation = service.activateHandler(fMoveRightForKeyBinding.getActionDefinitionId(), handler); + fHandlerActivations.add(activation); + + fFindForKeyBinding = new OpenSDFindDialog(); + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fFindForKeyBinding.run(); + return null; + } + }; + activation = service.activateHandler(fFindForKeyBinding.getActionDefinitionId(), handler); + fFindForKeyBinding.setEnabled(false); + fHandlerActivations.add(activation); + + fShowNodeStartForKeyBinding = new ShowNodeStart(); + fShowNodeStartForKeyBinding.setText(Messages.SequenceDiagram_ShowNodeStart); + + fShowNodeStartForKeyBinding.setId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeStart");//$NON-NLS-1$ + fShowNodeStartForKeyBinding.setActionDefinitionId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeStart");//$NON-NLS-1$ + + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fShowNodeStartForKeyBinding.run(); + return null; + } + }; + activation = service.activateHandler(fShowNodeStartForKeyBinding.getActionDefinitionId(), handler); + fHandlerActivations.add(activation); + + fShowNodeEndForKeyBinding = new ShowNodeEnd(); + fShowNodeEndForKeyBinding.setText(Messages.SequenceDiagram_ShowNodeEnd); + fShowNodeEndForKeyBinding.setId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeEnd");//$NON-NLS-1$ + fShowNodeEndForKeyBinding.setActionDefinitionId("org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ShowNodeEnd");//$NON-NLS-1$ + + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fShowNodeEndForKeyBinding.run(); + return null; + } + }; + activation = service.activateHandler(fShowNodeEndForKeyBinding.getActionDefinitionId(), handler); + fHandlerActivations.add(activation); + + } + + /* + * Disposes the KeyBindingsManager + */ + private void dispose() { + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + //During Eclipse shutdown the active workbench window is null + return; + } + IHandlerService service = (IHandlerService) window.getService(IHandlerService.class); + for(IHandlerActivation activation : fHandlerActivations) { + service.deactivateHandler(activation); + } + + fGoToMessageForKeyBinding = null; + fFindForKeyBinding = null; + fMoveUpForKeyBinding = null; + fMoveDownForKeyBinding = null; + fMoveLeftForKeyBinding = null; + fMoveRightForKeyBinding = null; + fShowNodeStartForKeyBinding = null; + fShowNodeEndForKeyBinding = null; + } + + /** + * Set the view in all supported actions + * + * @param view to set in global actions + */ + public void setSdView(SDView view) { + if (!fViews.isEmpty()) { + fGoToMessageForKeyBinding.setView(view); + fFindForKeyBinding.setView(view); + fMoveUpForKeyBinding.setView(view); + fMoveDownForKeyBinding.setView(view); + fMoveLeftForKeyBinding.setView(view); + fMoveRightForKeyBinding.setView(view); + fShowNodeStartForKeyBinding.setView(view); + fShowNodeEndForKeyBinding.setView(view); + } + } + + /** + * Enable / disable find action + * + * @param enabled <code>true</code> for enabling else <code>false</code> + */ + public void setFindEnabled(boolean enabled) { + if (fFindForKeyBinding != null) { + fFindForKeyBinding.setEnabled(enabled); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/LastPage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/LastPage.java new file mode 100644 index 0000000000..4dacc9f7ff --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/LastPage.java @@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Action class implementation to move the focus to the last page of the whole sequence diagram. + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class LastPage extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.lastpage"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Standard constructor + * + * @param view the view reference + */ + public LastPage(SDView view) { + super(view); + setText(Messages.SequenceDiagram_LastPage); + setToolTipText(Messages.SequenceDiagram_GoToLastPage); + setId(ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_LAST_PAGE)); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if ((getView() == null) || (getView().getSDWidget()) == null) { + return; + } + if (getView().getSDPagingProvider() != null) { + getView().getSDPagingProvider().lastPage(); + } + getView().updateCoolBar(); + getView().getSDWidget().redraw(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDDown.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDDown.java new file mode 100755 index 0000000000..44f965d3ce --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDDown.java @@ -0,0 +1,70 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; + +/** + * Action class implementation to move down in the sequence diagram view within a page. + * + * @version 1.0 + * @author sveyrier + * + */ +public class MoveSDDown extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.MoveSDDown"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public MoveSDDown() { + this(null); + } + + /** + * Constructor + * + * @param view a sequence diagram view reference + */ + public MoveSDDown(SDView view) { + super(view); + setId(ID); + setActionDefinitionId(ID); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + @Override + public void run() { + if (getView() == null) { + return; + } + + SDWidget viewer = getView().getSDWidget(); + if (viewer != null) { + viewer.scrollBy(0, +viewer.getVisibleHeight()); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDLeft.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDLeft.java new file mode 100755 index 0000000000..1ce3220436 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDLeft.java @@ -0,0 +1,72 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; + +/** + * Action class implementation to move left in the sequence diagram view within a page. + * + * @version 1.0 + * @author sveyrier + * + */ +public class MoveSDLeft extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.MoveSDLeft"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public MoveSDLeft(){ + this(null); + } + + /** + * Constructor + * + * @param view a sequence diagram view reference + */ + public MoveSDLeft(SDView view) { + super(view); + setId(ID); + setActionDefinitionId(ID); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public void run() { + + if (getView() == null) { + return; + } + + SDWidget viewer = getView().getSDWidget(); + if (viewer != null) { + viewer.scrollBy(-viewer.getVisibleWidth(), 0); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDRight.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDRight.java new file mode 100755 index 0000000000..ce2a3cec7f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDRight.java @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; + +/** + * Action class implementation to move right in the sequence diagram view within a page. + * + * @version 1.0 + * @author sveyrier + * + */ + +public class MoveSDRight extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.MoveSDRight"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public MoveSDRight() { + this(null); + } + + /** + * Constructor + * + * @param view a sequence diagram view reference + */ + public MoveSDRight(SDView view) { + super(view); + setId(ID); + setActionDefinitionId(ID); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + + if (getView() == null) { + return; + } + + SDWidget viewer = getView().getSDWidget(); + if (viewer != null) { + viewer.scrollBy(+viewer.getVisibleWidth(), 0); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDUp.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDUp.java new file mode 100755 index 0000000000..f10c1dd16f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveSDUp.java @@ -0,0 +1,71 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; + +/** + * Action class implementation to move up in the sequence diagram view within a + * page. + * + * @version 1.0 + * @author sveyrier + */ +public class MoveSDUp extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.MoveSDUp"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public MoveSDUp() { + this(null); + } + + /** + * Constructor + * + * @param view a sequence diagram view reference + */ + public MoveSDUp(SDView view) { + super(view); + setId(ID); + setActionDefinitionId(ID); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if (getView() == null) { + return; + } + SDWidget viewer = getView().getSDWidget(); + + if (viewer != null) { + viewer.scrollBy(0, -viewer.getVisibleHeight()); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveToMessage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveToMessage.java new file mode 100755 index 0000000000..2b8438eea6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/MoveToMessage.java @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import java.util.Iterator; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.BaseMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessageReturn; + +/** + * Action Class implementation to move to selected message + * + * @version 1.0 + * @author sveyrier + * + */ +public class MoveToMessage extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.GoToMessage"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default Constructor + */ + public MoveToMessage() { + this(null); + } + + /** + * Constructor + * + * @param view a sequence diagram view reference + */ + public MoveToMessage(SDView view) { + super(view); + setId(ID); + setActionDefinitionId(ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SEARCH_MATCH)); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if (getView() == null) { + return; + } + + SDWidget sdWidget = getView().getSDWidget(); + + if (sdWidget == null) { + return; + } + + ISelectionProvider selProvider = sdWidget.getSelectionProvider(); + ISelection sel = selProvider.getSelection(); + Object selectedNode = null; + Iterator<Object> it = ((StructuredSelection) sel).iterator(); + while (it.hasNext()) { + Object node = it.next(); + if (node instanceof BaseMessage) { + selectedNode = node; + } + } + + if (selectedNode == null) { + return; + } + + if (selectedNode instanceof SyncMessageReturn) { + GraphNode node = ((SyncMessageReturn) selectedNode).getMessage(); + sdWidget.clearSelection(); + sdWidget.addSelection(node); + sdWidget.ensureVisible(node); + sdWidget.redraw(); + } else if (selectedNode instanceof SyncMessage) { + GraphNode node = ((SyncMessage) selectedNode).getMessageReturn(); + sdWidget.clearSelection(); + sdWidget.addSelection(node); + sdWidget.ensureVisible(node); + sdWidget.redraw(); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/NextPage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/NextPage.java new file mode 100755 index 0000000000..2518ab9e5e --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/NextPage.java @@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Action class implementation to move the focus to the next page of the whole sequence diagram. + * + * @version 1.0 + * @author sveyrier + * + */ +public class NextPage extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.nextpage"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + * + * @param view the view reference + */ + public NextPage(SDView view) { + super(view); + setText(Messages.SequenceDiagram_NextPage); + setToolTipText(Messages.SequenceDiagram_GoToNextPage); + setId(ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_PAGE)); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public void run() { + if ((getView() == null) || (getView().getSDWidget()) == null) { + return; + } + if (getView().getSDPagingProvider() != null) { + getView().getSDPagingProvider().nextPage(); + } + getView().updateCoolBar(); + getView().getSDWidget().redraw(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDFiltersDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDFiltersDialog.java new file mode 100755 index 0000000000..7afaf6f5cf --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDFiltersDialog.java @@ -0,0 +1,75 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.FilterListDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFilterProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Action class implementation for 'Filtering' of messages/lifelines. + * + * @version 1.0 + * @author sveyrier + */ +public class OpenSDFiltersDialog extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.sdFilters"; //$NON-NLS-1$ + + /** + * The filter provider reference + */ + private final ISDFilterProvider fProvider; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Constructor + * + * @param view + * The view reference + * @param provider + * The provider + */ + public OpenSDFiltersDialog(SDView view, ISDFilterProvider provider) { + super(view); + setText(Messages.SequenceDiagram_HidePatterns); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FILTERS)); + setId(ID); + setToolTipText(Messages.SequenceDiagram_HidePatterns); + fProvider = provider; + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if (getView() == null) { + return; + } + FilterListDialog dialog = new FilterListDialog(getView(), fProvider); + dialog.open(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDFindDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDFindDialog.java new file mode 100755 index 0000000000..0bfce393ca --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDFindDialog.java @@ -0,0 +1,93 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.swt.SWT; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.SearchFilterDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Action class implementation for 'Finding' of messages/lifelines. + * + * @version 1.0 + * @author sveyrier + * + */ +public class OpenSDFindDialog extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.sdFind"; //$NON-NLS-1$ + + /** + * The action definition ID. + */ + public static final String ACTION_DEFINITION_ID = "org.eclipse.ui.edit.findReplace"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public OpenSDFindDialog() { + this(null); + } + + /** + * Constructor + * + * @param view The view reference + */ + public OpenSDFindDialog(SDView view) { + super(view); + setText(Messages.SequenceDiagram_Find + "..."); //$NON-NLS-1$ + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SEARCH_SEQ)); + setId(ID); + setActionDefinitionId(ACTION_DEFINITION_ID); + setToolTipText(Messages.SequenceDiagram_Find + "..."); //$NON-NLS-1$ + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if (getView() == null) { + return; + } + + // Disable action while search is ongoing + this.setEnabled(false); + + try { + if ((getView().getExtendedFindProvider() != null) && (getView().getExtendedFindProvider().getFindAction() != null)) { + getView().getExtendedFindProvider().getFindAction().run(); + } else if (getView().getSDFindProvider() != null) { + SearchFilterDialog dialog = new SearchFilterDialog(getView(), getView().getSDFindProvider(), false, SWT.NORMAL); + dialog.open(); + } + } finally { + // Enable action after finishing the search + this.setEnabled(true); + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDPagesDialog.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDPagesDialog.java new file mode 100755 index 0000000000..e7145f3252 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/OpenSDPagesDialog.java @@ -0,0 +1,80 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.PagesDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDAdvancedPagingProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Action class implementation for paging. + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class OpenSDPagesDialog extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.sdPaging"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The advanced paging provider reference. + */ + private final ISDAdvancedPagingProvider fProvider; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructor + * + * @param view + * The view reference + * @param provider + * The provider + */ + public OpenSDPagesDialog(SDView view, ISDAdvancedPagingProvider provider) { + super(view); + setText(Messages.SequenceDiagram_Pages); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_GOTO_PAGE)); + setId(ID); + fProvider = provider; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public void run() { + if (getView() == null) { + return; + } + PagesDialog dialog = new PagesDialog(getView(), fProvider); + dialog.open(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/PrevPage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/PrevPage.java new file mode 100755 index 0000000000..741d06f329 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/PrevPage.java @@ -0,0 +1,70 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * Action class implementation to move the focus to the previous page of the whole sequence diagram. + * + * @version 1.0 + * @author sveyrier + * + */ +public class PrevPage extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.prevpage"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor + * + * @param view the view reference + */ + public PrevPage(SDView view) { + super(view); + setText(Messages.SequenceDiagram_PreviousPage); + setToolTipText(Messages.SequenceDiagram_GoToPreviousPage); + setId(ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_PAGE)); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if ((getView() == null) || (getView().getSDWidget()) == null) { + return; + } + if (getView().getSDPagingProvider() != null) { + getView().getSDPagingProvider().prevPage(); + } + getView().updateCoolBar(); + getView().getSDWidget().redraw(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/Print.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/Print.java new file mode 100755 index 0000000000..433d4d4e03 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/Print.java @@ -0,0 +1,60 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; + +/** + * Action class implementation for 'Printing'. + * + * @version 1.0 + * @author sveyrier + */ +public class Print extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * The action ID. + */ + public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.print"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructor + * + * @param view The view reference + */ + public Print(SDView view) { + super(view); + setId(ID); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if ((getView() == null) || getView().getSDWidget() == null){ + return; + } + + getView().getSDWidget().print(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ShowNodeEnd.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ShowNodeEnd.java new file mode 100755 index 0000000000..901bba59c7 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ShowNodeEnd.java @@ -0,0 +1,89 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import java.util.Iterator; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; + +/** + * Action class implementation to show end of a graph node. + * + * @version 1.0 + * @author sveyrier + */ +public class ShowNodeEnd extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public ShowNodeEnd() { + this(null); + } + + /** + * Constructor + * + * @param view The sequence diagram view reference + * @since 2.0 + */ + public ShowNodeEnd(SDView view) { + super(view); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NODE_END)); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public void run() { + if (getView() == null) { + return; + } + + SDWidget sdWidget = getView().getSDWidget(); + + if (sdWidget == null) { + return; + } + + ISelectionProvider selProvider = sdWidget.getSelectionProvider(); + ISelection sel = selProvider.getSelection(); + Object selectedNode = null; + + Iterator<Object> it = ((StructuredSelection) sel).iterator(); + while (it.hasNext()) { + selectedNode = it.next(); + } + + if (selectedNode != null) { + GraphNode node = (GraphNode) selectedNode; + if ((node.getX() + node.getWidth()) * sdWidget.getZoomFactor() < sdWidget.getContentsX() + sdWidget.getVisibleWidth() / 2) { + sdWidget.ensureVisible(Math.round((node.getX() + node.getWidth()) * sdWidget.getZoomFactor()) - sdWidget.getVisibleWidth() / 2, Math.round((node.getY() + node.getHeight()) * sdWidget.getZoomFactor())); + } else { + sdWidget.ensureVisible(Math.round((node.getX() + node.getWidth()) * sdWidget.getZoomFactor() + sdWidget.getVisibleWidth() / (float) 2), Math.round((node.getY() + node.getHeight()) * sdWidget.getZoomFactor())); + } + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ShowNodeStart.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ShowNodeStart.java new file mode 100755 index 0000000000..7c3ca150b6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/ShowNodeStart.java @@ -0,0 +1,89 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import java.util.Iterator; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; + +/** + * Action class implementation to show end of a graph node. + * + * @version 1.0 + * @author sveyrier + */ +public class ShowNodeStart extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Default constructor + */ + public ShowNodeStart() { + this(null); + } + + /** + * Constructor + * + * @param view + * The sequence diagram view reference + * @since 2.0 + */ + public ShowNodeStart(SDView view) { + super(view); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NODE_START)); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + if (getView() == null) { + return; + } + + SDWidget sdWidget = getView().getSDWidget(); + + if (sdWidget == null) { + return; + } + + ISelectionProvider selProvider = sdWidget.getSelectionProvider(); + ISelection sel = selProvider.getSelection(); + Object selectedNode = null; + Iterator<Object> it = ((StructuredSelection) sel).iterator(); + while (it.hasNext()) { + selectedNode = it.next(); + } + if (selectedNode != null) { + GraphNode node = (GraphNode) selectedNode; + if (node.getX() * sdWidget.getZoomFactor() < sdWidget.getContentsX() + sdWidget.getVisibleWidth() / 2) { + sdWidget.ensureVisible(Math.round(node.getX() * sdWidget.getZoomFactor() - sdWidget.getVisibleWidth() / (float) 2), Math.round(node.getY() * sdWidget.getZoomFactor())); + } else { + sdWidget.ensureVisible(Math.round(node.getX() * sdWidget.getZoomFactor() + sdWidget.getVisibleWidth() / (float) 2), Math.round(node.getY() * sdWidget.getZoomFactor())); + } + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/Zoom.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/Zoom.java new file mode 100755 index 0000000000..5a7ee0c668 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/Zoom.java @@ -0,0 +1,239 @@ +/********************************************************************** + * Copyright (c) 2005, 2014 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers; + +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDWidget; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; +import org.eclipse.ui.IActionBars; + +/** + * Action class implementation for zooming in, out or reset of zoom. + * + * @version 1.0 + * @author sveyrier + * + */ +public class Zoom extends BaseSDAction { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The Action ID for zooming in. + */ + public static final String ZOOM_IN_ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ZoomInCoolBar"; //$NON-NLS-1$ + /** + * The Action ID for zooming out. + */ + public static final String ZOOM_OUT_ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ZoomOutCoolBar"; //$NON-NLS-1$ + /** + * The Action ID for reset zooming. + */ + public static final String RESET_ZOOM_ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.ResetZoom"; //$NON-NLS-1$ + /** + * The Action ID for no zoominf. + */ + public static final String NO_ZOOM_ID = "org.eclipse.linuxtools.tmf.ui.views.uml2sd.handlers.NoZoom"; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * Flag to indicate last zoom in. + */ + private boolean fLastZoomIn = false; + /** + * Flag to indicate last zoom out. + */ + private boolean fLastZoomOut = false; + /** + * The cursor used when zooming in. + */ + private final Cursor fZoomInCursor; + /** + * The cursor used when zooming out. + */ + private final Cursor fZoomOutCursor; + + /** + * The different zoom actions + */ + public static enum ZoomType { + /** No zoom information */ + ZOOM_NONE, + /** Zoom in */ + ZOOM_IN, + /** Zoom out */ + ZOOM_OUT, + /** Reset to the default zoom level */ + ZOOM_RESET + } + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructor + * @param view The view reference + * @param type The type of zoom. + */ + public Zoom(SDView view, ZoomType type) { + super(view, "", AS_RADIO_BUTTON); //$NON-NLS-1$ + + // Pre-create zooming cursors + fZoomInCursor = new Cursor(Display.getCurrent(), + Activator.getDefault().getImageFromImageRegistry(ITmfImageConstants.IMG_UI_ZOOM_IN).getImageData(), + Activator.getDefault().getImageFromImageRegistry(ITmfImageConstants.IMG_UI_ZOOM).getImageData(), 0, 0); + + fZoomOutCursor = new Cursor(Display.getCurrent(), + Activator.getDefault().getImageFromImageRegistry(ITmfImageConstants.IMG_UI_ZOOM_OUT).getImageData(), + Activator.getDefault().getImageFromImageRegistry(ITmfImageConstants.IMG_UI_ZOOM).getImageData(), 0, 0); + + switch (type) { + case ZOOM_IN: + setText(Messages.SequenceDiagram_ZoomIn); + setToolTipText(Messages.SequenceDiagram_ZoomInTheDiagram); + setId(ZOOM_IN_ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_IN_MENU)); + break; + + case ZOOM_OUT: + setText(Messages.SequenceDiagram_ZoomOut); + setToolTipText(Messages.SequenceDiagram_ZoomOutTheDiagram); + setId(ZOOM_OUT_ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_OUT_MENU)); + break; + + case ZOOM_RESET: + setText(Messages.SequenceDiagram_ResetZoomFactor); + setToolTipText(Messages.SequenceDiagram_ResetZoomFactor); + setId(RESET_ZOOM_ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_HOME_MENU)); + break; + + case ZOOM_NONE: + default: + setText(Messages.SequenceDiagram_Select); + setToolTipText(Messages.SequenceDiagram_Select); + setId(NO_ZOOM_ID); + setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SELECT_MENU)); + break; + } + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public void run() { + + if ((getView() == null) || (getView().getSDWidget() == null)) { + return; + } + + SDWidget viewer = getView().getSDWidget(); + + if (getId().equals(ZOOM_OUT_ID)) { + // Eclipse 3.0 M7 workaround + if (fLastZoomOut == isChecked()) { + setChecked(!isChecked()); + } + + viewer.setZoomOutMode(isChecked()); + fLastZoomOut = isChecked(); + if (isChecked()) { + viewer.setCursor(fZoomOutCursor); + setActionChecked(NO_ZOOM_ID, false); + } else { + viewer.setCursor(new Cursor(Display.getDefault(), SWT.CURSOR_ARROW)); + setActionChecked(NO_ZOOM_ID, true); + } + } else if (getId().equals(ZOOM_IN_ID)) { + // Eclipse 3.0 M7 workaround + if (fLastZoomIn == isChecked()) { + setChecked(!isChecked()); + } + + viewer.setZoomInMode(isChecked()); + fLastZoomIn = isChecked(); + if (isChecked()) { + viewer.setCursor(fZoomInCursor); + setActionChecked(NO_ZOOM_ID, false); + } else { + viewer.setCursor(new Cursor(Display.getDefault(), SWT.CURSOR_ARROW)); + setActionChecked(NO_ZOOM_ID, true); + } + } else if (getId().equals(RESET_ZOOM_ID)) { + viewer.resetZoomFactor(); + + // The reset action is a radio button only to uncheck the zoom in and out button + // when it is clicked. This avoid adding code to do it manually + // We only have to force it to false every time + setChecked(false); + setActionChecked(NO_ZOOM_ID, true); + } else if (getId().equals(NO_ZOOM_ID)) { + setChecked(true); + viewer.setZoomInMode(false); + viewer.setZoomInMode(false); + viewer.setCursor(new Cursor(Display.getDefault(), SWT.CURSOR_ARROW)); + } + } + + /** + * Set action check state of a view action for a given action ID. + * + * @param id The action ID + * @param checked true to check the action, false to uncheck the action + */ + protected void setActionChecked(String id, boolean checked) { + if (getView() != null) { + IActionBars bar = getView().getViewSite().getActionBars(); + if (bar == null) { + return; + } + IToolBarManager barManager = bar.getToolBarManager(); + if (barManager == null) { + return; + } + IContributionItem nextPage = barManager.find(id); + if (nextPage instanceof ActionContributionItem) { + IAction action = ((ActionContributionItem) nextPage).getAction(); + if (action != null) { + action.setChecked(checked); + } + } + } + } + + /** + * Dispose the action and its resources + * + * @since 3.2 + */ + public void dispose() { + fZoomInCursor.dispose(); + fZoomOutCursor.dispose(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/IExtendedFilterProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/IExtendedFilterProvider.java new file mode 100755 index 0000000000..3e8ee59fa7 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/IExtendedFilterProvider.java @@ -0,0 +1,37 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +import org.eclipse.jface.action.Action; + +/** + * Interface for providing an extended filter provider. + * + * Sequence Diagram loaders which implement this interface provide the action for filtering the sequence diagram.<br> + * + * Action provider are associated to a Sequence Diagram view by calling <code>SDView.setExtendedFilterProvider</code><br> + * + * @version 1.0 + * @author sveyrier + * + */ +public interface IExtendedFilterProvider { + + /** + * Returns a filter action implementation. + * + * @return a filter action implementation + */ + Action getFilterAction(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/IExtendedFindProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/IExtendedFindProvider.java new file mode 100755 index 0000000000..aa471c2fa3 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/IExtendedFindProvider.java @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +import org.eclipse.jface.action.Action; + +/** + * Interface for providing an extended find provider. + * + * Sequence Diagram loaders which implement this interface provide an action for finding in the sequence diagram. + * + * Action provider are associated to a Sequence Diagram view by calling <code>SDView.setExtendedFindProvider()</code>.<br> + * + * Note that either provider implementing ISDFindProvider or IExtendedFindProvider can be active in the SDView.<br> + * + * @version 1.0 + * @author sveyrier + * + */ +public interface IExtendedFindProvider { + + /** + * Returns an extended find action. + * + * @return an extended find action + */ + Action getFindAction(); +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDAdvancedPagingProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDAdvancedPagingProvider.java new file mode 100755 index 0000000000..45014914cd --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDAdvancedPagingProvider.java @@ -0,0 +1,51 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +/** + * Interface for providing an extended find provider. + * + * An advanced paging provider is able to compute number of pages, and to display the number of items it treats on each + * page and for total counts.<br> + * An item can be a message, a node or anything meaningful from loader standpoint.<br> + * Items are only here for information to the user. + * + * @version 1.0 + * @author sveyrier + */ +public interface ISDAdvancedPagingProvider extends ISDPagingProvider { + + /** + * Returns the current page. + * + * @return the current page the loader is dealing with. <b>Note</b> that first page has the 0 index (indexes are from + * 0 to pagesCount()-1). + */ + int currentPage(); + + /** + * Returns the number of pages. + * + * @return number of pages the loader is dealing with + */ + int pagesCount(); + + /** + * Instructs a load of the <pageNumber_><i>th</i> page.<br> + * <b>Note</b> that first page has the index 0 (indexes are from 0 to pagesCount()-1). + * + * @param pageNumber index of the page to load + */ + void pageNumberChanged(int pageNumber); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDCollapseProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDCollapseProvider.java new file mode 100755 index 0000000000..29d0fa6c87 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDCollapseProvider.java @@ -0,0 +1,36 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline; + +/** + * Interface for providing a collapse provider. + * + * Sequence diagram loaders which want to support Drag and Drop collapsing in the sequence diagram must implement this + * interface and register this implementation using <code>SDView.setCollapsingProvider();</code> + * + * @version 1.0 + * @author sveyrier + */ +public interface ISDCollapseProvider { + + /** + * Called back when the sequence diagram is requesting 2 lifelines collapsing + * + * @param lifeline1 - One of the lifeline to collapse + * @param lifeline2 - The other lifeline to collapse with + */ + void collapseTwoLifelines(Lifeline lifeline1, Lifeline lifeline2); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDExtendedActionBarProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDExtendedActionBarProvider.java new file mode 100755 index 0000000000..1ebd1aecb6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDExtendedActionBarProvider.java @@ -0,0 +1,37 @@ +/********************************************************************** + * Copyright (c) 2005, 2012 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +import org.eclipse.ui.IActionBars; + +/** + * Interface for providing an extended action bar provider. + * + * Sequence Diagram loaders which implement this interface provide their own action to the action bar. + * + * Action provider are associated to a Sequence Diagram SDWidget calling SDViewer.setExtendedActionBarProvider() + * + * @version 1.0 + * @author sveyrier + */ +public interface ISDExtendedActionBarProvider { + + /** + * The caller is supposed to add its own actions in the cool bar and the drop-down menu.<br> + * See examples in SDView.createCoolbarContent(). + * + * @param bar the bar + */ + void supplementCoolbarContent(IActionBars bar); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDFilterProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDFilterProvider.java new file mode 100755 index 0000000000..feb721f605 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDFilterProvider.java @@ -0,0 +1,43 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +import java.util.List; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.FilterCriteria; + +/** + * Interface for providing a filter provider. + * + * Sequence Diagram loaders which implement this class provide the actions filtering the sequence diagram.<br> + * This interface also allow the implementor to set which graph nodes are supporting filtering (thanks to + * ISDGraphNodeSupporter extension).<br> + * + * Action provider are associated to a Sequence Diagram view by calling <code>SDView.setSDFilterProvider</code><br> + * + * Filters to be applied to be managed by the loader in an ArrayList of FilterCriteria.<br> + * + * @version 1.0 + * @author sveyrier + */ +public interface ISDFilterProvider extends ISDGraphNodeSupporter { + + /** + * Called when the Filter dialog box OK button is pressed + * + * @param filters user selection made in the dialog box + * @return true if the filter applied + */ + boolean filter(List<FilterCriteria> filters); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDFindProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDFindProvider.java new file mode 100755 index 0000000000..e279d5fc8f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDFindProvider.java @@ -0,0 +1,46 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.Criteria; + +/** + * Interface for providing a find provider. + * + * Sequence Diagram loaders which implement this class provide the actions for finding the sequence diagram. This + * interface also allow the implementor to set which action/feature are supported.<br> + * + * Action provider are associated to a Sequence Diagram view by calling <code>SDView.setSDFindProvider()</code>.<br> + * + * Note that either provider implementing ISDFindProvider or IExtendedFindProvider can be active in the SDView.<br> + * + * @version 1.0 + * @author sveyrier + * + */ +public interface ISDFindProvider extends ISDGraphNodeSupporter { + + /** + * Called when the Find dialog box OK button is pressed + * + * @param toApply user selection made in the dialog box + * @return true if the find got a non empty result + */ + boolean find(Criteria toApply); + + /** + * Called when dialog is closed + */ + void cancel(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDGraphNodeSupporter.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDGraphNodeSupporter.java new file mode 100755 index 0000000000..52e5f61bd5 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDGraphNodeSupporter.java @@ -0,0 +1,83 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +/** + * Interface for providing a graph node supporter. + * + * Sequence Diagram loaders which implement this class provide the actions for finding or filtering the sequence + * diagram. This interface also allow the implementor to set which action/feature are supported + * + * Action provider are associated to a Sequence Diagram SDWidget calling <code>SDViewer.setSDFindProvider()</code> or + * <code>SDViewer.setSDFilterProvider()</code>. + * + * @version 1.0 + * @author sveyrier + * + */ +public interface ISDGraphNodeSupporter { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * Lifeline support ID + */ + static int LIFELINE = 0; + /** + * Synchronous message support ID + */ + static int SYNCMESSAGE = 1; + /** + * Synchronous message return support ID + */ + static int SYNCMESSAGERETURN = 2; + /** + * Asynchronous message support ID + */ + static int ASYNCMESSAGE = 3; + /** + * Asynchronous message return support ID + */ + static int ASYNCMESSAGERETURN = 4; + /** + * Stop support ID + */ + static int STOP = 5; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + /** + * Return true to enable this options, false otherwise + * + * @param nodeType + * The integer value matching the type of the node + * @return true to enable this options, false otherwise + */ + boolean isNodeSupported(int nodeType); + + /** + * Return the name to use in dialogs Not called if isNodeSupported return + * false + * + * @param nodeType + * The integer value matching the type of the node + * @param loaderClassName + * The name of the loader class + * @return the name to use in dialogs + */ + String getNodeName(int nodeType, String loaderClassName); +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDPagingProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDPagingProvider.java new file mode 100755 index 0000000000..1c32c0c5f9 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDPagingProvider.java @@ -0,0 +1,61 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +/** + * Paging provider interface. + * + * Sequence Diagram loaders which implement this class provide the actions for sequence diagram page navigation.<br> + * + * Action provider are associated to a Sequence Diagram view by calling <code>SDView.setSDPagingProvider()</code>.<br> + * + * @version 1.0 + * @author sveyrier + * + */ +public interface ISDPagingProvider { + + /** + * Return true to enable the next page button in the coolBar, false otherwise + * + * @return true if a next page exists false otherwise + */ + boolean hasNextPage(); + + /** + * Return true to enable the previous page button in the coolBar, false otherwise + * + * @return true if a previous page exists false otherwise + */ + boolean hasPrevPage(); + + /** + * Called back when next page button is pressed in the coolBar + */ + void nextPage(); + + /** + * Called back when previous page button is pressed in the coolBar + */ + void prevPage(); + + /** + * Called back when first page button is pressed in the coolBar + */ + void firstPage(); + + /** + * Called back when last page button is pressed in the coolBar + */ + void lastPage(); +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDPropertiesProvider.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDPropertiesProvider.java new file mode 100755 index 0000000000..19f5d728f2 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/handlers/provider/ISDPropertiesProvider.java @@ -0,0 +1,35 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider; + +import org.eclipse.ui.views.properties.IPropertySheetPage; + +/** + * Interface for providing sequence diagram property. + * + * Contract for loaders that want to provide information in the properties view. + * + * @version 1.0 + * @author sveyrier + + */ +public interface ISDPropertiesProvider { + + /** + * Returns the IPropertySheetEntry that will fill in the properties view + * + * @return the property sheet entry + */ + IPropertySheetPage getPropertySheetEntry(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/load/IUml2SDLoader.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/load/IUml2SDLoader.java new file mode 100755 index 0000000000..68a1c7fb09 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/load/IUml2SDLoader.java @@ -0,0 +1,46 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.load; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; + +/** + * The interface all UML2SD loaders must implement. + * + * @version 1.0 + * @author sveyrier + */ +public interface IUml2SDLoader { + + /** + * Set the viewer object to the loader that has been reloaded at the beginning + * of a new workbench session + * + * @param viewer The sequence diagram view + */ + void setViewer(SDView viewer); + + /** + * Returns title string for the UML2SD View when this loader is the one + * + * @return the string convenient for this loader + */ + String getTitleString(); + + /** + * When another loader becomes the one the previous one is replaced It's time clean-up + * if needed (listeners to be removed for example) + */ + void dispose(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/load/LoadersManager.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/load/LoadersManager.java new file mode 100644 index 0000000000..7c9b6726cd --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/load/LoadersManager.java @@ -0,0 +1,397 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.load; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * Manager class for the UML2SD extension point. + * + * @version 1.0 + * @author sveyrier + * @author Bernd Hufmann + */ +public class LoadersManager { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * The loader tag for the extension point. + */ + public static final String LOADER_TAG = "uml2SDLoader"; //$NON-NLS-1$ + /** + * The loader prefix. + */ + public static final String LOADER_PREFIX = LOADER_TAG + "."; //$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The LoadersManager singleton instance. + */ + private static LoadersManager fLoadersManager; + + /** + * Map for caching information (view ID to loader class) + */ + private Map<String, IUml2SDLoader> fViewLoaderMap = new HashMap<>(); + /** + * Map for caching information (view ID to list of configuration elements) + */ + private Map<String, ArrayList<IConfigurationElement>> fViewLoadersList = new HashMap<>(); + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + /** + * This should not be used by the clients + */ + protected LoadersManager() { + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + /** + * A static method to get the manager instance. + * + * @return the manager instance + */ + public static synchronized LoadersManager getInstance() { + if (fLoadersManager == null) { + fLoadersManager = new LoadersManager(); + } + return fLoadersManager; + } + + /** + * Creates a loader instance and associate it to the view. It requires + * that the loader-view-association was created by an eclipse extension. + * + * @param className The name of the class to create an instance from + * @param view The UML2 Sequence Diagram view instance + * @return The created loader + */ + public IUml2SDLoader createLoader(String className, SDView view) { + + // Safety check + if (view == null) { + return null; + } + + String viewId = view.getViewSite().getId(); + + // Get loaders from all extensions for given view + List<IConfigurationElement> loaderElements = getLoaderConfigurationElements(viewId); + IConfigurationElement ce = getLoaderConfigurationElement(className, loaderElements); + + if (ce != null) { + // Assign a loader instance to this view + createLoaderForView(viewId, ce); + IUml2SDLoader loader = fViewLoaderMap.get(viewId); + if (loader != null) { + loader.setViewer(view); + return loader; + } + } + return null; + } + + /** + * Sets the loader to null for this view, a kind of clean-up while disposing. + * + * @param viewId the id of the view + */ + public void resetLoader(String viewId) { + IUml2SDLoader loader = fViewLoaderMap.get(viewId); + if (loader != null) { + loader.dispose(); + } + fViewLoaderMap.put(viewId, null); + } + + /** + * Returns the loader in use in given Sequence Diagram View + * + * @param viewId The Sequence Diagram viewId. + * @return the current loader if any - null otherwise + */ + public IUml2SDLoader getCurrentLoader(String viewId) { + return getCurrentLoader(viewId, null); + } + + /** + * Returns the loader in use in this Sequence Diagram View + * + * @param viewId The Sequence Diagram viewId + * @param view The Sequence Diagram view (if known). Use null to reference the primary SD View. + * @return the current loader if any - null otherwise + */ + public IUml2SDLoader getCurrentLoader(String viewId, SDView view) { + if (viewId == null) { + return null; + } + + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + // During Eclipse shutdown the active workbench window is null + if (window == null) { + return null; + } + + IWorkbenchPage persp = window.getActivePage(); + + SDView sdView = view; + + try { + // Search the view corresponding to the viewId + if (sdView == null) { + IViewReference viewref = persp.findViewReference(viewId); + if (viewref != null) { + sdView = (SDView) viewref.getView(false); + } + + if (sdView == null) { + // no corresponding view exists -> return null for the loader + return null; + } + } + + // Return the loader corresponding to that view (if any) + IUml2SDLoader loader = fViewLoaderMap.get(viewId); + if (loader == null) { + createLastLoaderIfAny(viewId); + loader = fViewLoaderMap.get(viewId); + } + + return loader; + } catch (Exception e) { + Activator.getDefault().logError("Error getting loader class", e); //$NON-NLS-1$ + } + return null; + } + + /** + * Returns the loader class name that have been saved last time. + * + * @param viewId The view this loader belongs to + * @return the class name of the saved loader + */ + public String getSavedLoader(String viewId) { + IPreferenceStore p = Activator.getDefault().getPreferenceStore(); + return p.getString(LOADER_PREFIX + viewId); + } + + /** + * Saves the last loader in order to reload it on next session. + * + * @param id + * Standalone ID of the loader + * @param id2 + * Suffix ID of the loader + */ + public void saveLastLoader(String id, String id2) { + IPreferenceStore p = Activator.getDefault().getPreferenceStore(); + p.setValue(LOADER_PREFIX + id2, id); + } + + /** + * Changes the current unique loader to the given secondary viewId. + * + * @param loader The current loader + * @param id the view secondary id or null + */ + private void setCurrentLoader(IUml2SDLoader loader, String id) { + if (id == null) { + return; + } + + // Get the loader in use + IUml2SDLoader currentLoader = fViewLoaderMap.get(id); + + if ((currentLoader != null) && (currentLoader != loader)) { + if (loader != null) { + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + // During Eclipse shutdown the active workbench window is null + if (window == null) { + return; + } + IWorkbenchPage persp = window.getActivePage(); + try { + // Search view corresponding to the viewId + SDView sdview = null; + IViewReference viewref = persp.findViewReference(id); + if (viewref != null) { + sdview = (SDView) viewref.getView(false); + } + + // Make everything clean for the new loader + if (sdview != null) { + sdview.resetProviders(); + } + + } catch (Exception e) { + Activator.getDefault().logError("Error setting current loader class", e); //$NON-NLS-1$ + } + } + // The old loader is going to be kicked + currentLoader.dispose(); + } + + // Replace the current loader by the new one in the map + fViewLoaderMap.put(id, loader); + + // Store this loader in the preferences to be able to restore it when the workbench will be re-launched + if (loader != null) { + saveLastLoader(loader.getClass().getName(), id); + } + } + + /** + * Creates the last loader and saves it. If not last is not available, it creates + * and saves the default loader, else no loader is created. + * + * @param viewId The view ID. + */ + private void createLastLoaderIfAny(String viewId) { + // Get saved loader from preferences + String loaderName = getSavedLoader(viewId); + + // Get loaders from all extensions for given view + List<IConfigurationElement> loaderElements = getLoaderConfigurationElements(viewId); + IConfigurationElement ce = getLoaderConfigurationElement(loaderName, loaderElements); + + if (ce == null) { + ce = getDefaultLoader(loaderElements); + } + + if (ce != null) { + createLoaderForView(viewId, ce); + } + } + + /** + * Gets a list of loader configuration elements from the extension point registry for a given view. + * @param viewId The view ID + * @return List of extension point configuration elements. + */ + private List<IConfigurationElement> getLoaderConfigurationElements(String viewId) { + List<IConfigurationElement> list = fViewLoadersList.get(viewId); + if (list != null) { + return list; + } + + ArrayList<IConfigurationElement> ret = new ArrayList<>(); + IExtensionPoint iep = Platform.getExtensionRegistry().getExtensionPoint(Activator.PLUGIN_ID, LOADER_TAG); + if (iep == null) { + return ret; + } + + IExtension[] ie = iep.getExtensions(); + if (ie == null) { + return ret; + } + + for (int i = 0; i < ie.length; i++) { + IConfigurationElement c[] = ie[i].getConfigurationElements(); + for (int j = 0; j < c.length; j++) { + if (viewId.equals(c[j].getAttribute("view"))) { //$NON-NLS-1$ + ret.add(c[j]); + } + } + } + fViewLoadersList.put(viewId, ret); + return ret; + } + + /** + * Returns the loader configuration element for given loader class name and for the given + * list of configuration elements, if available else null. + * + * @param loaderClassName The loader class name. + * @param loaderElements The list of loader configuration elements + * @return Extension point configuration element + */ + private static IConfigurationElement getLoaderConfigurationElement( + String loaderClassName, List<IConfigurationElement> loaderElements) { + if (loaderClassName != null && loaderClassName.length() > 0) { + // Find configuration element corresponding to the saved loader + for (Iterator<IConfigurationElement> i = loaderElements.iterator(); i.hasNext();) { + IConfigurationElement ce = i.next(); + if (ce.getAttribute("class").equals(loaderClassName)) { //$NON-NLS-1$ + return ce; + } + } + } + return null; + } + + /** + * Returns the loader configuration element for the given list of configuration elements, if available else null. + * Note that if multiple default loaders are defined it selects the first one + + * @param loaderElements The list of loader configuration elements + * @return The default extension point configuration element. + */ + private static IConfigurationElement getDefaultLoader( + List<IConfigurationElement> loaderElements) { + // Look for a default loader + for (Iterator<IConfigurationElement> i = loaderElements.iterator(); i.hasNext();) { + IConfigurationElement ce = i.next(); + if (Boolean.valueOf(ce.getAttribute("default")).booleanValue()) { //$NON-NLS-1$ + return ce; + } + } + return null; + } + + /** + * Creates an instance of the loader class for a given extension point configuration element and + * also sets it as current loader for the given view. + * + * @param viewId The view ID. + * @param ce The extension point configuration element + */ + private void createLoaderForView(String viewId, IConfigurationElement ce) { + try { + Object obj = ce.createExecutableExtension("class"); //$NON-NLS-1$ + IUml2SDLoader l = (IUml2SDLoader) obj; + if (viewId != null) { + setCurrentLoader(l, viewId); + } + } catch (CoreException e4) { + Activator.getDefault().logError("Error 'uml2SDLoader' Extension point", e4); //$NON-NLS-1$ + } catch (Exception e5) { + Activator.getDefault().logError("Error 'uml2SDLoader' Extension point", e5); //$NON-NLS-1$ + } + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/Messages.java new file mode 100644 index 0000000000..95a2928483 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/Messages.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 Ericsson. + * 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: + * Bernd Hufmann - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.loader; + +import org.eclipse.osgi.util.NLS; + +/** + * Messages related to the sequence diagram loader + * + * @version 1.0 + * @author Bernd Hufmann + */ +@SuppressWarnings("javadoc") +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.tmf.ui.views.uml2sd.loader.messages"; //$NON-NLS-1$ + public static String TmfUml2SDSyncLoader_ViewName; + public static String TmfUml2SDSyncLoader_CategoryLifeline; + public static String TmfUml2SDSyncLoader_CategoryMessage; + public static String TmfUml2SDSyncLoader_FrameName; + public static String TmfUml2SDSyncLoader_SearchJobDescrition; + public static String TmfUml2SDSyncLoader_SearchNotFound; + + /** @since 2.0 */ + public static String TmfUml2SDSyncLoader_EventTypeSend; + /** @since 2.0 */ + public static String TmfUml2SDSyncLoader_EventTypeReceive; + /** @since 2.0 */ + public static String TmfUml2SDSyncLoader_FieldSender; + /** @since 2.0 */ + public static String TmfUml2SDSyncLoader_FieldReceiver; + /** @since 2.0 */ + public static String TmfUml2SDSyncLoader_FieldSignal; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfAsyncMessage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfAsyncMessage.java new file mode 100644 index 0000000000..5c59b96030 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfAsyncMessage.java @@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.loader; + +import org.eclipse.tracecompass.tmf.core.uml2sd.ITmfAsyncSequenceDiagramEvent; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessage; + +/** + * <p> + * Extends AsyncMessage class to provide additional information about the trace event. + * </p> + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class TmfAsyncMessage extends AsyncMessage implements ITmfAsyncSequenceDiagramEvent { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * A asynchronous sequence diagram event implementation + */ + private ITmfAsyncSequenceDiagramEvent fSdEvent; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Standard constructor + * + * @param sdEvent The asynchronous sequence diagram event implementation + * @param eventOccurrence The event index + */ + public TmfAsyncMessage(ITmfAsyncSequenceDiagramEvent sdEvent, int eventOccurrence) { + this.fSdEvent = sdEvent; + setEventOccurrence(eventOccurrence); + setName(sdEvent.getName()); + setStartTime(sdEvent.getStartTime()); + setEndTime(sdEvent.getEndTime()); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public String getSender() { + return fSdEvent.getSender(); + } + + @Override + public String getReceiver() { + return fSdEvent.getReceiver(); + } +}
\ No newline at end of file diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfSyncMessage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfSyncMessage.java new file mode 100644 index 0000000000..aec9f5fe4f --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfSyncMessage.java @@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.loader; + +import org.eclipse.tracecompass.tmf.core.uml2sd.ITmfSyncSequenceDiagramEvent; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessage; + + +/** + * <p> + * Extends SyncMessage class to provide additional information about the trace event. + * </p> + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class TmfSyncMessage extends SyncMessage implements ITmfSyncSequenceDiagramEvent { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * A synchronous sequence diagram event implementation + */ + private ITmfSyncSequenceDiagramEvent fSdEvent; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Standard constructor + * + * @param sdEvent The synchronous sequence diagram event implementation + * @param eventOccurrence The event index + */ + public TmfSyncMessage(ITmfSyncSequenceDiagramEvent sdEvent, int eventOccurrence) { + this.fSdEvent = sdEvent; + setEventOccurrence(eventOccurrence); + setName(sdEvent.getName()); + setTime(sdEvent.getStartTime()); + } + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public String getSender() { + return fSdEvent.getSender(); + } + + @Override + public String getReceiver() { + return fSdEvent.getReceiver(); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfUml2SDSyncLoader.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfUml2SDSyncLoader.java new file mode 100644 index 0000000000..61ab61ebe6 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/TmfUml2SDSyncLoader.java @@ -0,0 +1,1485 @@ +/********************************************************************** + * Copyright (c) 2011, 2013 Ericsson + * + * 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: + * Bernd Hufmann - Initial API and implementation + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.loader; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.core.component.TmfComponent; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; +import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest; +import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest; +import org.eclipse.tracecompass.tmf.core.signal.TmfRangeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; +import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.core.uml2sd.ITmfSyncSequenceDiagramEvent; +import org.eclipse.tracecompass.tmf.core.uml2sd.TmfSyncSequenceDiagramEvent; +import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Frame; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.Criteria; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.FilterCriteria; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.FilterListDialog; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDAdvancedPagingProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFilterProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFindProvider; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDGraphNodeSupporter; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.load.IUml2SDLoader; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.IProgressConstants; + +/** + * <p> + * This class is a reference implementation of the + * <code>org.eclipse.linuxtools.tmf.ui.Uml2SDLoader</code> extension point. It + * provides a Sequence Diagram loader for a user space trace with specific trace + * content for sending and receiving signals between components. I also includes + * a default implementation for the <code>ITmfEvent</code> parsing. + * </p> + * + * The class <code>TmfUml2SDSyncLoader</code> analyzes events from type + * <code>ITmfEvent</code> and creates events type + * <code>ITmfSyncSequenceDiagramEvent</code> if the <code>ITmfEvent</code> + * contains all relevant information. The analysis checks that the event type + * strings contains either string SEND or RECEIVE. If event type matches these + * key words, the analyzer will look for strings sender, receiver and signal in + * the event fields of type <code>ITmfEventField</code>. If all the data is + * found a sequence diagram event from can be created. Note that Sync Messages + * are assumed, which means start and end time are the same. <br> + * <br> + * The parsing of the <code>ITmfEvent</code> is done in the method + * <code>getSequnceDiagramEvent()</code> of class + * <code>TmfUml2SDSyncLoader</code>. By extending the class + * <code>TmfUml2SDSyncLoader</code> and overwriting + * <code>getSequnceDiagramEvent()</code> a customized parsing algorithm can be + * implemented.<br> + * <br> + * Note that combined traces of multiple components, that contain the trace + * information about the same interactions are not supported in the class + * <code>TmfUml2SDSyncLoader</code>. + * + * @version 1.0 + * @author Bernd Hufmann + */ +public class TmfUml2SDSyncLoader extends TmfComponent implements IUml2SDLoader, ISDFindProvider, ISDFilterProvider, ISDAdvancedPagingProvider, ISelectionListener { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * Default title name. + */ + protected static final String TITLE = Messages.TmfUml2SDSyncLoader_ViewName; + + /** + * Maximum number of messages per page. + */ + protected static final int MAX_NUM_OF_MSG = 10000; + + private static final int INDEXING_THREAD_SLEEP_VALUE = 100; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + // Experiment attributes + /** + * The TMF trace reference. + * @since 2.0 + */ + protected ITmfTrace fTrace = null; + /** + * The current indexing event request. + */ + protected ITmfEventRequest fIndexRequest = null; + /** + * The current request to fill a page. + */ + protected ITmfEventRequest fPageRequest = null; + /** + * Flag whether the time range signal was sent by this loader class or not + */ + protected volatile boolean fIsSignalSent = false; + + // The view and event attributes + /** + * The sequence diagram view reference. + */ + protected SDView fView = null; + /** + * The current sequence diagram frame reference. + */ + protected Frame fFrame = null; + /** + * The list of sequence diagram events of current page. + */ + protected List<ITmfSyncSequenceDiagramEvent> fEvents = new ArrayList<>(); + + // Checkpoint and page attributes + /** + * The checkpoints of the whole sequence diagram trace (i.e. start time stamp of each page) + */ + protected List<TmfTimeRange> fCheckPoints = new ArrayList<>(MAX_NUM_OF_MSG); + /** + * The current page displayed. + */ + protected volatile int fCurrentPage = 0; + /** + * The current time selected. + */ + protected ITmfTimestamp fCurrentTime = null; + /** + * Flag to specify that selection of message is done by selection or by signal. + */ + protected volatile boolean fIsSelect = false; + + // Search attributes + /** + * The job for searching across pages. + */ + protected SearchJob fFindJob = null; + /** + * List of found nodes within a page. + */ + protected List<GraphNode> fFindResults = new ArrayList<>(); + /** + * The current find criteria reference + */ + protected Criteria fFindCriteria = null; + /** + * The current find index within the list of found nodes (<code>fFindeResults</code> within a page. + */ + protected volatile int fCurrentFindIndex = 0; + + // Filter attributes + /** + * The list of active filters. + */ + protected List<FilterCriteria> fFilterCriteria = null; + + // Thread synchronization + /** + * The synchronization lock. + */ + protected ReentrantLock fLock = new ReentrantLock(); + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + /** + * Default constructor + */ + public TmfUml2SDSyncLoader() { + super(TITLE); + } + + /** + * Constructor + * + * @param name Name of loader + */ + public TmfUml2SDSyncLoader(String name) { + super(name); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * Returns the current time if available else null. + * + * @return the current time if available else null + * @since 2.0 + */ + public ITmfTimestamp getCurrentTime() { + fLock.lock(); + try { + if (fCurrentTime != null) { + return fCurrentTime; + } + return null; + } finally { + fLock.unlock(); + } + } + + /** + * Waits for the page request to be completed + */ + public void waitForCompletion() { + fLock.lock(); + ITmfEventRequest request = fPageRequest; + fLock.unlock(); + if (request != null) { + try { + request.waitForCompletion(); + } catch (InterruptedException e) { + // ignore + } + } + } + + /** + * Handler for the trace opened signal. + * @param signal The trace opened signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceOpened(TmfTraceOpenedSignal signal) { + fTrace = signal.getTrace(); + loadTrace(); + } + + + /** + * Signal handler for the trace selected signal. + * + * Spawns a request to index the trace (checkpoints creation) as well as it fills + * the first page. + * + * @param signal The trace selected signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceSelected(TmfTraceSelectedSignal signal) { + // Update the trace reference + ITmfTrace trace = signal.getTrace(); + if (!trace.equals(fTrace)) { + fTrace = trace; + } + loadTrace(); + } + + /** + * Method for loading the current selected trace into the view. + * Sub-class need to override this method to add the view specific implementation. + * @since 2.0 + */ + protected void loadTrace() { + ITmfEventRequest indexRequest = null; + fLock.lock(); + + try { + final Job job = new IndexingJob("Indexing " + getName() + "..."); //$NON-NLS-1$ //$NON-NLS-2$ + job.setUser(false); + job.schedule(); + + indexRequest = fIndexRequest; + + cancelOngoingRequests(); + + TmfTimeRange window = TmfTimeRange.ETERNITY; + + fIndexRequest = new TmfEventRequest(ITmfEvent.class, window, 0, + ITmfEventRequest.ALL_DATA, ITmfEventRequest.ExecutionType.BACKGROUND) { + + private ITmfTimestamp fFirstTime = null; + private ITmfTimestamp fLastTime = null; + private int fNbSeqEvents = 0; + private final List<ITmfSyncSequenceDiagramEvent> fSdEvents = new ArrayList<>(MAX_NUM_OF_MSG); + + @Override + public void handleData(ITmfEvent event) { + super.handleData(event); + + ITmfSyncSequenceDiagramEvent sdEvent = getSequenceDiagramEvent(event); + + if (sdEvent != null) { + ++fNbSeqEvents; + + if (fFirstTime == null) { + fFirstTime = event.getTimestamp(); + } + + fLastTime = event.getTimestamp(); + + if ((fNbSeqEvents % MAX_NUM_OF_MSG) == 0) { + fLock.lock(); + try { + fCheckPoints.add(new TmfTimeRange(fFirstTime, fLastTime)); + if (fView != null) { + fView.updateCoolBar(); + } + } finally { + fLock.unlock(); + } + fFirstTime = null; + + } + + if (fNbSeqEvents > MAX_NUM_OF_MSG) { + // page is full + return; + } + + fSdEvents.add(sdEvent); + + if (fNbSeqEvents == MAX_NUM_OF_MSG) { + fillCurrentPage(fSdEvents); + } + } + } + + @Override + public void handleSuccess() { + if ((fFirstTime != null) && (fLastTime != null)) { + + fLock.lock(); + try { + fCheckPoints.add(new TmfTimeRange(fFirstTime, fLastTime)); + if (fView != null) { + fView.updateCoolBar(); + } + } finally { + fLock.unlock(); + } + } + + if (fNbSeqEvents <= MAX_NUM_OF_MSG) { + fillCurrentPage(fSdEvents); + } + + super.handleSuccess(); + } + + @Override + public void handleCompleted() { + if (fEvents.isEmpty()) { + fFrame = new Frame(); + // make sure that view is not null when setting frame + SDView sdView; + fLock.lock(); + try { + sdView = fView; + } finally { + fLock.unlock(); + } + if (sdView != null) { + sdView.setFrameSync(fFrame); + } + } + super.handleCompleted(); + job.cancel(); + } + }; + + } finally { + fLock.unlock(); + } + if (indexRequest != null && !indexRequest.isCompleted()) { + indexRequest.cancel(); + } + resetLoader(); + fTrace.sendRequest(fIndexRequest); + } + + /** + * Signal handler for the trace closed signal. + * + * @param signal The trace closed signal + * @since 2.0 + */ + @TmfSignalHandler + public void traceClosed(TmfTraceClosedSignal signal) { + if (signal.getTrace() != fTrace) { + return; + } + ITmfEventRequest indexRequest = null; + fLock.lock(); + try { + indexRequest = fIndexRequest; + fIndexRequest = null; + + cancelOngoingRequests(); + + if (fFilterCriteria != null) { + fFilterCriteria.clear(); + } + + FilterListDialog.deactivateSavedGlobalFilters(); + } finally { + fTrace = null; + fLock.unlock(); + } + if (indexRequest != null && !indexRequest.isCompleted()) { + indexRequest.cancel(); + } + + resetLoader(); + } + + /** + * Moves to the page that contains the time provided by the signal. The messages will be selected + * if the provided time is the time of a message. + * + * @param signal The Time synch signal. + */ + @TmfSignalHandler + public void synchToTime(TmfTimeSynchSignal signal) { + fLock.lock(); + try { + if ((signal.getSource() != this) && (fFrame != null) && (fCheckPoints.size() > 0)) { + fCurrentTime = signal.getBeginTime(); + fIsSelect = true; + moveToMessage(); + } + } finally { + fLock.unlock(); + } + } + + /** + * Moves to the page that contains the current time provided by signal. + * No message will be selected however the focus will be set to the message + * if the provided time is the time of a message. + * + * @param signal The time range sync signal + */ + @TmfSignalHandler + public void synchToTimeRange(TmfRangeSynchSignal signal) { + fLock.lock(); + try { + if ((signal.getSource() != this) && (fFrame != null) && !fIsSignalSent && (fCheckPoints.size() > 0)) { + TmfTimeRange newTimeRange = signal.getCurrentRange(); + + fIsSelect = false; + fCurrentTime = newTimeRange.getStartTime(); + + moveToMessage(); + } + } finally { + fLock.unlock(); + } + + } + + @Override + public void setViewer(SDView viewer) { + + fLock.lock(); + try { + fView = viewer; + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().addPostSelectionListener(this); + fView.setSDFindProvider(this); + fView.setSDPagingProvider(this); + fView.setSDFilterProvider(this); + + resetLoader(); + IEditorPart editor = fView.getSite().getPage().getActiveEditor(); + if (editor instanceof ITmfTraceEditor) { + ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace(); + if (trace != null) { + traceSelected(new TmfTraceSelectedSignal(this, trace)); + } + } + } finally { + fLock.unlock(); + } + } + + @Override + public String getTitleString() { + return getName(); + } + + @Override + public void dispose() { + super.dispose(); + ITmfEventRequest indexRequest = null; + fLock.lock(); + try { + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + // During Eclipse shutdown the active workbench window is null + if (window != null) { + window.getSelectionService().removePostSelectionListener(this); + } + + indexRequest = fIndexRequest; + fIndexRequest = null; + cancelOngoingRequests(); + + fView.setSDFindProvider(null); + fView.setSDPagingProvider(null); + fView.setSDFilterProvider(null); + fView = null; + } finally { + fLock.unlock(); + } + if (indexRequest != null && !indexRequest.isCompleted()) { + indexRequest.cancel(); + } + } + + @Override + public boolean isNodeSupported(int nodeType) { + switch (nodeType) { + case ISDGraphNodeSupporter.LIFELINE: + case ISDGraphNodeSupporter.SYNCMESSAGE: + return true; + + default: + break; + } + return false; + } + + @Override + public String getNodeName(int nodeType, String loaderClassName) { + switch (nodeType) { + case ISDGraphNodeSupporter.LIFELINE: + return Messages.TmfUml2SDSyncLoader_CategoryLifeline; + case ISDGraphNodeSupporter.SYNCMESSAGE: + return Messages.TmfUml2SDSyncLoader_CategoryMessage; + default: + break; + } + return ""; //$NON-NLS-1$ + } + + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + ISelection sel = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection(); + if ((sel != null) && (sel instanceof StructuredSelection)) { + StructuredSelection stSel = (StructuredSelection) sel; + if (stSel.getFirstElement() instanceof TmfSyncMessage) { + TmfSyncMessage syncMsg = ((TmfSyncMessage) stSel.getFirstElement()); + broadcast(new TmfTimeSynchSignal(this, syncMsg.getStartTime())); + } + } + } + + @Override + public boolean find(Criteria toSearch) { + fLock.lock(); + try { + if (fFrame == null) { + return false; + } + + if ((fFindResults == null) || (fFindCriteria == null) || !fFindCriteria.compareTo(toSearch)) { + fFindResults = new CopyOnWriteArrayList<>(); + fFindCriteria = toSearch; + if (fFindCriteria.isLifeLineSelected()) { + for (int i = 0; i < fFrame.lifeLinesCount(); i++) { + if (fFindCriteria.matches(fFrame.getLifeline(i).getName())) { + fFindResults.add(fFrame.getLifeline(i)); + } + } + } + + ArrayList<GraphNode> msgs = new ArrayList<>(); + if (fFindCriteria.isSyncMessageSelected()) { + for (int i = 0; i < fFrame.syncMessageCount(); i++) { + if (fFindCriteria.matches(fFrame.getSyncMessage(i).getName())) { + msgs.add(fFrame.getSyncMessage(i)); + } + } + } + + if (!msgs.isEmpty()) { + fFindResults.addAll(msgs); + } + + List<GraphNode> selection = fView.getSDWidget().getSelection(); + if ((selection != null) && (selection.size() == 1)) { + fCurrentFindIndex = fFindResults.indexOf(selection.get(0)) + 1; + } else { + fCurrentFindIndex = 0; + } + } else { + fCurrentFindIndex++; + } + + if (fFindResults.size() > fCurrentFindIndex) { + GraphNode current = fFindResults.get(fCurrentFindIndex); + fView.getSDWidget().moveTo(current); + return true; + } + fFindResults = null; + fCurrentFindIndex =0; + return findInNextPages(fFindCriteria); // search in other page + } finally { + fLock.unlock(); + } + } + + @Override + public void cancel() { + cancelOngoingRequests(); + } + + @Override + public boolean filter(List<FilterCriteria> filters) { + fLock.lock(); + try { + cancelOngoingRequests(); + + if (filters == null) { + fFilterCriteria = new ArrayList<>(); + } else { + List<FilterCriteria> list = filters; + fFilterCriteria = new ArrayList<>(list); + } + + fillCurrentPage(fEvents); + + } finally { + fLock.unlock(); + } + return true; + } + + @Override + public boolean hasNextPage() { + fLock.lock(); + try { + int size = fCheckPoints.size(); + if (size > 0) { + return fCurrentPage < (size - 1); + } + } finally { + fLock.unlock(); + } + return false; + } + + @Override + public boolean hasPrevPage() { + fLock.lock(); + try { + return fCurrentPage > 0; + } finally { + fLock.unlock(); + } + } + + @Override + public void nextPage() { + fLock.lock(); + try { + // Safety check + if (fCurrentPage >= (fCheckPoints.size() - 1)) { + return; + } + + cancelOngoingRequests(); + fCurrentTime = null; + fCurrentPage++; + moveToPage(); + } finally { + fLock.unlock(); + } + } + + @Override + public void prevPage() { + fLock.lock(); + try { + // Safety check + if (fCurrentPage <= 0) { + return; + } + + cancelOngoingRequests(); + fCurrentTime = null; + fCurrentPage--; + moveToPage(); + } finally { + fLock.unlock(); + } + } + + @Override + public void firstPage() { + fLock.lock(); + try { + + cancelOngoingRequests(); + fCurrentTime = null; + fCurrentPage = 0; + moveToPage(); + } finally { + fLock.unlock(); + } + } + + @Override + public void lastPage() { + fLock.lock(); + try { + cancelOngoingRequests(); + fCurrentTime = null; + fCurrentPage = fCheckPoints.size() - 1; + moveToPage(); + } finally { + fLock.unlock(); + } + } + + @Override + public int currentPage() { + fLock.lock(); + try { + return fCurrentPage; + } finally { + fLock.unlock(); + } + } + + @Override + public int pagesCount() { + fLock.lock(); + try { + return fCheckPoints.size(); + } finally { + fLock.unlock(); + } + } + + @Override + public void pageNumberChanged(int pagenNumber) { + int localPageNumber = pagenNumber; + + fLock.lock(); + try { + cancelOngoingRequests(); + + if (localPageNumber < 0) { + localPageNumber = 0; + } + int size = fCheckPoints.size(); + if (localPageNumber > (size - 1)) { + localPageNumber = size - 1; + } + fCurrentPage = localPageNumber; + moveToPage(); + } finally { + fLock.unlock(); + } + } + + @Override + public void broadcast(TmfSignal signal) { + fIsSignalSent = true; + super.broadcast(signal); + fIsSignalSent = false; + } + + /** + * Cancels any ongoing find operation + */ + protected void cancelOngoingRequests() { + fLock.lock(); + ITmfEventRequest pageRequest = null; + try { + // Cancel the search thread + if (fFindJob != null) { + fFindJob.cancel(); + } + + fFindResults = null; + fFindCriteria = null; + fCurrentFindIndex = 0; + + pageRequest = fPageRequest; + fPageRequest = null; + } finally { + fLock.unlock(); + } + if (pageRequest != null && !pageRequest.isCompleted()) { + pageRequest.cancel(); + } + } + + /** + * Resets loader attributes + */ + protected void resetLoader() { + fLock.lock(); + try { + fCurrentTime = null; + fEvents.clear(); + fCheckPoints.clear(); + fCurrentPage = 0; + fCurrentFindIndex = 0; + fFindCriteria = null; + fFindResults = null; + fView.setFrameSync(new Frame()); + fFrame = null; + } + finally { + fLock.unlock(); + } + + } + + /** + * Fills current page with sequence diagram content. + * + * @param events sequence diagram events + */ + protected void fillCurrentPage(List<ITmfSyncSequenceDiagramEvent> events) { + + fLock.lock(); + try { + fEvents = new ArrayList<>(events); + if (fView != null && !events.isEmpty()) { + fView.toggleWaitCursorAsync(true); + } + } finally { + fLock.unlock(); + } + + final Frame frame = new Frame(); + + if (!events.isEmpty()) { + Map<String, Lifeline> nodeToLifelineMap = new HashMap<>(); + + frame.setName(Messages.TmfUml2SDSyncLoader_FrameName); + + for (int i = 0; i < events.size(); i++) { + + ITmfSyncSequenceDiagramEvent sdEvent = events.get(i); + + if ((nodeToLifelineMap.get(sdEvent.getSender()) == null) && (!filterLifeLine(sdEvent.getSender()))) { + Lifeline lifeline = new Lifeline(); + lifeline.setName(sdEvent.getSender()); + nodeToLifelineMap.put(sdEvent.getSender(), lifeline); + frame.addLifeLine(lifeline); + } + + if ((nodeToLifelineMap.get(sdEvent.getReceiver()) == null) && (!filterLifeLine(sdEvent.getReceiver()))) { + Lifeline lifeline = new Lifeline(); + lifeline.setName(sdEvent.getReceiver()); + nodeToLifelineMap.put(sdEvent.getReceiver(), lifeline); + frame.addLifeLine(lifeline); + } + } + + int eventOccurence = 1; + + for (int i = 0; i < events.size(); i++) { + ITmfSyncSequenceDiagramEvent sdEvent = events.get(i); + + // Check message filter + if (filterMessage(sdEvent)) { + continue; + } + + // Set the message sender and receiver + Lifeline startLifeline = nodeToLifelineMap.get(sdEvent.getSender()); + Lifeline endLifeline = nodeToLifelineMap.get(sdEvent.getReceiver()); + + // Check if any of the lifelines were filtered + if ((startLifeline == null) || (endLifeline == null)) { + continue; + } + + int tmp = Math.max(startLifeline.getEventOccurrence(), endLifeline.getEventOccurrence()); + eventOccurence = Math.max(eventOccurence, tmp); + + startLifeline.setCurrentEventOccurrence(eventOccurence); + endLifeline.setCurrentEventOccurrence(eventOccurence); + + TmfSyncMessage message = new TmfSyncMessage(sdEvent, eventOccurence++); + + message.setStartLifeline(startLifeline); + message.setEndLifeline(endLifeline); + + message.setTime(sdEvent.getStartTime()); + + // add the message to the frame + frame.addMessage(message); + + } + fLock.lock(); + try { + if (!fView.getSDWidget().isDisposed()) { + fView.getSDWidget().getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + + fLock.lock(); + try { + // check if view was disposed in the meanwhile + if ((fView != null) && (!fView.getSDWidget().isDisposed())) { + fFrame = frame; + fView.setFrame(fFrame); + + if (fCurrentTime != null) { + moveToMessageInPage(); + } + + if (fFindCriteria != null) { + find(fFindCriteria); + } + + fView.toggleWaitCursorAsync(false); + } + } finally { + fLock.unlock(); + } + + } + }); + } + } + finally { + fLock.unlock(); + } + } + } + + /** + * Moves to a certain message defined by timestamp (across pages) + */ + protected void moveToMessage() { + int page = 0; + + fLock.lock(); + try { + page = getPage(fCurrentTime); + + if (page == fCurrentPage) { + moveToMessageInPage(); + return; + } + fCurrentPage = page; + moveToPage(false); + } finally { + fLock.unlock(); + } + } + + /** + * Moves to a certain message defined by timestamp in current page + */ + protected void moveToMessageInPage() { + fLock.lock(); + try { + if (!fView.getSDWidget().isDisposed()) { + // Check for GUI thread + if(Display.getCurrent() != null) { + // Already in GUI thread - execute directly + TmfSyncMessage prevMessage = null; + TmfSyncMessage syncMessage = null; + boolean isExactTime = false; + for (int i = 0; i < fFrame.syncMessageCount(); i++) { + if (fFrame.getSyncMessage(i) instanceof TmfSyncMessage) { + syncMessage = (TmfSyncMessage) fFrame.getSyncMessage(i); + if (syncMessage.getStartTime().compareTo(fCurrentTime, false) == 0) { + isExactTime = true; + break; + } else if ((syncMessage.getStartTime().compareTo(fCurrentTime, false) > 0) && (prevMessage != null)) { + syncMessage = prevMessage; + break; + } + prevMessage = syncMessage; + } + } + if (fIsSelect && isExactTime) { + fView.getSDWidget().moveTo(syncMessage); + } + else { + fView.getSDWidget().ensureVisible(syncMessage); + fView.getSDWidget().clearSelection(); + fView.getSDWidget().redraw(); + } + } + else { + // Not in GUI thread - queue action in GUI thread. + fView.getSDWidget().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + moveToMessageInPage(); + } + }); + } + } + } + finally { + fLock.unlock(); + } + } + + /** + * Moves to a certain message defined by timestamp (across pages) + */ + protected void moveToPage() { + moveToPage(true); + } + + /** + * Moves to a certain page. + * + * @param notifyAll true to broadcast time range signal to other signal handlers else false + */ + protected void moveToPage(boolean notifyAll) { + + TmfTimeRange window = null; + + fLock.lock(); + try { + // Safety check + if (fCurrentPage > fCheckPoints.size()) { + return; + } + window = fCheckPoints.get(fCurrentPage); + } finally { + fLock.unlock(); + } + + if (window == null) { + window = TmfTimeRange.ETERNITY; + } + + fPageRequest = new TmfEventRequest(ITmfEvent.class, window, 0, + ITmfEventRequest.ALL_DATA, ITmfEventRequest.ExecutionType.FOREGROUND) { + private final List<ITmfSyncSequenceDiagramEvent> fSdEvent = new ArrayList<>(); + + @Override + public void handleData(ITmfEvent event) { + super.handleData(event); + + ITmfSyncSequenceDiagramEvent sdEvent = getSequenceDiagramEvent(event); + + if (sdEvent != null) { + fSdEvent.add(sdEvent); + } + } + + @Override + public void handleSuccess() { + fillCurrentPage(fSdEvent); + super.handleSuccess(); + } + + }; + + fTrace.sendRequest(fPageRequest); + + if (notifyAll) { + TmfTimeRange timeRange = getSignalTimeRange(window.getStartTime()); + broadcast(new TmfRangeSynchSignal(this, timeRange)); + } + } + + /** + * Gets page that contains timestamp + * + * @param time The timestamp + * @return page that contains the time + * @since 2.0 + */ + protected int getPage(ITmfTimestamp time) { + int page; + int size; + fLock.lock(); + try { + size = fCheckPoints.size(); + for (page = 0; page < size; page++) { + TmfTimeRange timeRange = fCheckPoints.get(page); + if (timeRange.getEndTime().compareTo(time, false) >= 0) { + break; + } + } + if (page >= size) { + page = size - 1; + } + return page; + } finally { + fLock.unlock(); + } + } + + /** + * Background search in trace for expression in criteria. + * + * @param findCriteria The find criteria + * @return true if background request was started else false + */ + protected boolean findInNextPages(Criteria findCriteria) { + fLock.lock(); + try { + if (fFindJob != null) { + return true; + } + + int nextPage = fCurrentPage + 1; + + if ((nextPage) >= fCheckPoints.size()) { + // we are at the end + return false; + } + + TmfTimeRange window = new TmfTimeRange(fCheckPoints.get(nextPage).getStartTime(), fCheckPoints.get(fCheckPoints.size()-1).getEndTime()); + fFindJob = new SearchJob(findCriteria, window); + fFindJob.schedule(); + fView.toggleWaitCursorAsync(true); + } finally { + fLock.unlock(); + } + return true; + } + + /** + * Gets time range for time range signal. + * + * @param startTime The start time of time range. + * @return the time range + * @since 2.0 + */ + protected TmfTimeRange getSignalTimeRange(ITmfTimestamp startTime) { + fLock.lock(); + try { + TmfTimeRange currentRange = TmfTraceManager.getInstance().getCurrentRange(); + long offset = fTrace == null ? 0 : currentRange.getEndTime().getDelta(currentRange.getStartTime()).normalize(0, startTime.getScale()).getValue(); + TmfTimestamp initialEndOfWindow = new TmfTimestamp(startTime.getValue() + offset, startTime.getScale(), startTime.getPrecision()); + return new TmfTimeRange(startTime, initialEndOfWindow); + } + finally { + fLock.unlock(); + } + } + + /** + * Checks if filter criteria matches the message name in given SD event. + * + * @param sdEvent The SD event to check + * @return true if match else false. + */ + protected boolean filterMessage(ITmfSyncSequenceDiagramEvent sdEvent) { + fLock.lock(); + try { + if (fFilterCriteria != null) { + for(FilterCriteria criteria : fFilterCriteria) { + if (criteria.isActive() && criteria.getCriteria().isSyncMessageSelected() && criteria.getCriteria().matches(sdEvent.getName())) { + return true; + } + } + } + } finally { + fLock.unlock(); + } + return false; + } + + /** + * Checks if filter criteria matches a lifeline name (sender or receiver) in given SD event. + * + * @param lifeline the message receiver + * @return true if match else false. + */ + protected boolean filterLifeLine(String lifeline) { + fLock.lock(); + try { + if (fFilterCriteria != null) { + for(FilterCriteria criteria : fFilterCriteria) { + if (criteria.isActive() && criteria.getCriteria().isLifeLineSelected() && criteria.getCriteria().matches(lifeline)) { + return true; + } + } + } + } finally { + fLock.unlock(); + } + return false; + } + + /** + * Job to search in trace for given time range. + */ + protected class SearchJob extends Job { + + /** + * The search event request. + */ + protected final SearchEventRequest fSearchRequest; + + /** + * Constructor + * + * @param findCriteria The search criteria + * @param window Time range to search in + * @since 2.0 + */ + public SearchJob(Criteria findCriteria, TmfTimeRange window) { + super(Messages.TmfUml2SDSyncLoader_SearchJobDescrition); + fSearchRequest = new SearchEventRequest(window, ITmfEventRequest.ALL_DATA, + ITmfEventRequest.ExecutionType.FOREGROUND, findCriteria); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + fSearchRequest.setMonitor(monitor); + + fTrace.sendRequest(fSearchRequest); + + try { + fSearchRequest.waitForCompletion(); + } catch (InterruptedException e) { + Activator.getDefault().logError("Search request interrupted!", e); //$NON-NLS-1$ + } + + IStatus status = Status.OK_STATUS; + if (fSearchRequest.isFound() && (fSearchRequest.getFoundTime() != null)) { + fCurrentTime = fSearchRequest.getFoundTime(); + + // Avoid double-selection. Selection will be done when calling find(criteria) + // after moving to relevant page + fIsSelect = false; + if (!fView.getSDWidget().isDisposed()) { + fView.getSDWidget().getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + moveToMessage(); + } + }); + } + } + else { + if (monitor.isCanceled()) { + status = Status.CANCEL_STATUS; + } + else { + // String was not found + status = new Status(IStatus.WARNING, Activator.PLUGIN_ID, Messages.TmfUml2SDSyncLoader_SearchNotFound); + } + setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE); + } + monitor.done(); + + fLock.lock(); + try { + fView.toggleWaitCursorAsync(false); + fFindJob = null; + } finally { + fLock.unlock(); + } + + return status; + } + + @Override + protected void canceling() { + fSearchRequest.cancel(); + fLock.lock(); + try { + fFindJob = null; + } finally { + fLock.unlock(); + } + } + } + + /** + * TMF event request for searching within trace. + */ + protected class SearchEventRequest extends TmfEventRequest { + + /** + * The find criteria. + */ + private final Criteria fCriteria; + /** + * A progress monitor + */ + private IProgressMonitor fMonitor; + /** + * Flag to indicate that node was found according the criteria . + */ + private boolean fIsFound = false; + /** + * Time stamp of found item. + */ + private ITmfTimestamp fFoundTime = null; + + /** + * Constructor + * @param range @see org.eclipse.linuxtools.tmf.request.TmfEventRequest#TmfEventRequest(...) + * @param nbRequested @see org.eclipse.linuxtools.tmf.request.TmfEventRequest#handleData(...) + * @param execType @see org.eclipse.linuxtools.tmf.request.TmfEventRequest#handleData(...) + * @param criteria The search criteria + */ + public SearchEventRequest(TmfTimeRange range, int nbRequested, ExecutionType execType, Criteria criteria) { + this(range, nbRequested, execType, criteria, null); + } + + /** + * Constructor + * @param range @see org.eclipse.linuxtools.tmf.request.TmfEventRequest#TmfEventRequest(...) + * @param nbRequested @see org.eclipse.linuxtools.tmf.request.TmfEventRequest#TmfEventRequest(...) + * @param execType @see org.eclipse.linuxtools.tmf.request.TmfEventRequest#TmfEventRequest(...) + * @param criteria The search criteria + * @param monitor progress monitor + */ + public SearchEventRequest(TmfTimeRange range, int nbRequested, ExecutionType execType, Criteria criteria, IProgressMonitor monitor) { + super(ITmfEvent.class, range, 0, nbRequested, execType); + fCriteria = new Criteria(criteria); + fMonitor = monitor; + } + + @Override + public void handleData(ITmfEvent event) { + super.handleData(event); + + if ((fMonitor!= null) && fMonitor.isCanceled()) { + super.cancel(); + return; + } + + ITmfSyncSequenceDiagramEvent sdEvent = getSequenceDiagramEvent(event); + + if (sdEvent != null) { + + if (fCriteria.isLifeLineSelected()) { + if (fCriteria.matches(sdEvent.getSender())) { + fFoundTime = event.getTimestamp(); + fIsFound = true; + super.cancel(); + } + + if (fCriteria.matches(sdEvent.getReceiver())) { + fFoundTime = event.getTimestamp(); + fIsFound = true; + super.cancel(); + } + } + + if (fCriteria.isSyncMessageSelected() && fCriteria.matches(sdEvent.getName())) { + fFoundTime = event.getTimestamp(); + fIsFound = true; + super.cancel(); + } + } + } + + /** + * Set progress monitor. + * + * @param monitor The monitor to assign + */ + public void setMonitor(IProgressMonitor monitor) { + fMonitor = monitor; + } + + /** + * Check if find criteria was met. + * + * @return true if find criteria was met. + */ + public boolean isFound() { + return fIsFound; + } + + /** + * Returns timestamp of found time. + * + * @return timestamp of found time. + * @since 2.0 + */ + public ITmfTimestamp getFoundTime() { + return fFoundTime; + } + } + + /** + * Job class to provide progress monitor feedback. + * + * @version 1.0 + * @author Bernd Hufmann + * + */ + protected static class IndexingJob extends Job { + + /** + * @param name The job name + */ + public IndexingJob(String name) { + super(name); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + while (!monitor.isCanceled()) { + try { + Thread.sleep(INDEXING_THREAD_SLEEP_VALUE); + } catch (InterruptedException e) { + return Status.OK_STATUS; + } + } + monitor.done(); + return Status.OK_STATUS; + } + } + + + /** + * Returns sequence diagram event if details in given event are available else null. + * + * @param tmfEvent Event to parse for sequence diagram event details + * @return sequence diagram event if details are available else null + * @since 2.0 + */ + protected ITmfSyncSequenceDiagramEvent getSequenceDiagramEvent(ITmfEvent tmfEvent){ + //type = .*RECEIVE.* or .*SEND.* + //content = sender:<sender name>:receiver:<receiver name>,signal:<signal name> + String eventType = tmfEvent.getType().toString(); + if (eventType.contains(Messages.TmfUml2SDSyncLoader_EventTypeSend) || eventType.contains(Messages.TmfUml2SDSyncLoader_EventTypeReceive)) { + Object sender = tmfEvent.getContent().getField(Messages.TmfUml2SDSyncLoader_FieldSender); + Object receiver = tmfEvent.getContent().getField(Messages.TmfUml2SDSyncLoader_FieldReceiver); + Object name = tmfEvent.getContent().getField(Messages.TmfUml2SDSyncLoader_FieldSignal); + if ((sender instanceof ITmfEventField) && (receiver instanceof ITmfEventField) && (name instanceof ITmfEventField)) { + ITmfSyncSequenceDiagramEvent sdEvent = new TmfSyncSequenceDiagramEvent(tmfEvent, + ((ITmfEventField) sender).getValue().toString(), + ((ITmfEventField) receiver).getValue().toString(), + ((ITmfEventField) name).getValue().toString()); + + return sdEvent; + } + } + return null; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/messages.properties new file mode 100644 index 0000000000..359c257e24 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/loader/messages.properties @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2011, 2013 Ericsson +# +# 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: +# Bernd Hufmann - Initial API and implementation +############################################################################### +TmfUml2SDSyncLoader_ViewName=Component Interactions +TmfUml2SDSyncLoader_CategoryLifeline=Lifeline +TmfUml2SDSyncLoader_CategoryMessage=Interaction +TmfUml2SDSyncLoader_FrameName=Sequence Diagram +TmfUml2SDSyncLoader_SearchJobDescrition=Searching in sequence diagram ... +TmfUml2SDSyncLoader_EventTypeSend=SEND +TmfUml2SDSyncLoader_EventTypeReceive=RECEIVE +TmfUml2SDSyncLoader_FieldSender=sender +TmfUml2SDSyncLoader_FieldReceiver=receiver +TmfUml2SDSyncLoader_FieldSignal=signal +TmfUml2SDSyncLoader_SearchNotFound=String not found! diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/ISDPreferences.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/ISDPreferences.java new file mode 100755 index 0000000000..eda1e24a1a --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/ISDPreferences.java @@ -0,0 +1,147 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IFont; + +/** + * Interface for accessing sequence diagram preferences. + * + * @version 1.0 + * @author sveyrier + */ +public interface ISDPreferences { + + /** + * The link font with zoom preference name + */ + static final String PREF_LINK_FONT = "PREF_LINK_FONT"; //$NON-NLS-1$ + /** + * The exclude preference time preference name + */ + static final String PREF_EXCLUDE_EXTERNAL_TIME = "PREF_EXCLUDE_EXTERNAL_TIME"; //$NON-NLS-1$ + /** + * The use gradient color preferences name + */ + static final String PREF_USE_GRADIENT = "PREF_USE_GRADIENT"; //$NON-NLS-1$ + /** + * The lifeline spacing width preference name + */ + static final String PREF_LIFELINE_WIDTH = "PREF_LIFELINE_WIDTH"; //$NON-NLS-1$ + /** + * The time compression bar font preference name + */ + static final String PREF_TIME_COMP = "PREF_TIME_COMP"; //$NON-NLS-1$ + /** + * The lifeline font preference name + */ + static final String PREF_LIFELINE = "PREF_LIFELINE"; //$NON-NLS-1$ + /** + * The frame font preference name + */ + static final String PREF_FRAME = "PREF_FRAME"; //$NON-NLS-1$ + /** + * The frame name font preference name + */ + static final String PREF_FRAME_NAME = "PREF_FRAME_NAME"; //$NON-NLS-1$ + /** + * The execution occurrence font preference name + */ + static final String PREF_EXEC = "PREF_EXEC"; //$NON-NLS-1$ + /** + * The synchronous message font preference name + */ + static final String PREF_SYNC_MESS = "PREF_SYNC_MESS"; //$NON-NLS-1$ + /** + * The synchronous message return font preference name + */ + static final String PREF_SYNC_MESS_RET = "PREF_SYNC_MESS_RET"; //$NON-NLS-1$ + /** + * The asynchronous message font preference name + */ + static final String PREF_ASYNC_MESS = "PREF_ASYNC_MESS"; //$NON-NLS-1$ + /** + * The asynchronous message return font preference name + */ + static final String PREF_ASYNC_MESS_RET = "PREF_ASYNC_MESS_RET"; //$NON-NLS-1$ + /** + * The lifeline header font (header = the always visible part of a lifeline) + */ + static final String PREF_LIFELINE_HEADER = "PREF_LIFELINE_HEADER"; //$NON-NLS-1$ + /** + * The enable tooltip preference name + */ + static final String PREF_TOOLTIP = "PREF_TOOLTIP"; //$NON-NLS-1$ + + /** + * Returns the background color for the given preference name (font preference name) + * + * @param prefId The preference name + * @return the color + */ + IColor getBackGroundColor(String prefId); + + /** + * Returns the foreground color for the given preference name (font preference name) + * + * @param prefId A preference name + * @return the color + */ + IColor getForeGroundColor(String prefId); + + /** + * Returns the font color for the given preference name (font preference name) + * + * @param prefId A preference name + * @return the color + */ + IColor getFontColor(String prefId); + + /** + * Returns the font for the given preference name + * + * @param prefId the preference name + * @return the font + */ + IFont getFont(String prefId); + + /** + * Returns the time compression bar selection color + * + * @return the time compression bar selection color + */ + IColor getTimeCompressionSelectionColor(); + + /** + * Returns the background color used to draw selection + * + * @return the background color + */ + IColor getBackGroundColorSelection(); + + /** + * Returns the foreground color used to draw selection + * + * @return the foreground color + */ + IColor getForeGroundColorSelection(); + + /** + * Returns whether to use gradient color or not + * + * @return whether to use gradient color or not + */ + boolean useGradienColor(); + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/SDViewPref.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/SDViewPref.java new file mode 100755 index 0000000000..59f4aaf5af --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/SDViewPref.java @@ -0,0 +1,527 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IColor; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IFont; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.impl.ColorImpl; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.impl.FontImpl; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; + +/** + * This is the Sequence Diagram preference handler. This class is responsible for accessing the current user preferences + * selection This class also provider getters for each modifiable preferences. + * + * @version 1.0 + * @author sveyrier + */ +public class SDViewPref implements ISDPreferences, IPropertyChangeListener { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * Postfix string for background color property + */ + public static final String BACK_COLOR_POSTFIX = "_BACK_COLOR";//$NON-NLS-1$ + /** + * Postfix string for foreground color property + */ + public static final String FORE_COLOR_POSTFIX = "_FORE_COLOR";//$NON-NLS-1$ + /** + * Postfix string for text color property + */ + public static final String TEXT_COLOR_POSTFIX = "_TEXT_COLOR";//$NON-NLS-1$ + /** + * Array of preference names + */ + private static final String[] FONT_LIST = { PREF_LIFELINE, PREF_EXEC, PREF_SYNC_MESS, PREF_SYNC_MESS_RET, PREF_ASYNC_MESS, PREF_ASYNC_MESS_RET, PREF_FRAME, PREF_LIFELINE_HEADER, PREF_FRAME_NAME }; + /** + * A 2nd array of preference names + */ + private static final String[] FONT_LIST2 = { Messages.SequenceDiagram_Lifeline, Messages.SequenceDiagram_ExecutionOccurrence, Messages.SequenceDiagram_SyncMessage, Messages.SequenceDiagram_SyncMessageReturn, Messages.SequenceDiagram_AsyncMessage, Messages.SequenceDiagram_AsyncMessageReturn, Messages.SequenceDiagram_Frame, Messages.SequenceDiagram_LifelineHeader, Messages.SequenceDiagram_FrameTitle }; + /** + * Array of background color preference names + */ + private static final String[] PREF_BACK_COLOR_LIST = { PREF_LIFELINE, PREF_EXEC, PREF_FRAME, PREF_LIFELINE_HEADER, PREF_FRAME_NAME }; + /** + * Array of foreground color preference names + */ + private static final String[] PREF_FORE_COLOR_LIST = { PREF_LIFELINE, PREF_EXEC, PREF_SYNC_MESS, PREF_SYNC_MESS_RET, PREF_ASYNC_MESS, PREF_ASYNC_MESS_RET, PREF_FRAME, PREF_LIFELINE_HEADER, PREF_FRAME_NAME }; + /** + * Array of text color preference names + */ + private static final String[] PREF_TEXT_COLOR_LIST = { PREF_LIFELINE, PREF_SYNC_MESS, PREF_SYNC_MESS_RET, PREF_ASYNC_MESS, PREF_ASYNC_MESS_RET, PREF_LIFELINE_HEADER, PREF_FRAME_NAME }; + /** + * Temporary tag + * @since 2.0 + */ + public static final String TEMP_TAG = "_TEMP";//$NON-NLS-1$ + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + /** + * The sequence diagram preferences singleton instance + */ + private static SDViewPref fHandle = null; + /** + * Hashtable for font preferences + */ + private Map<String, IFont> fFontPref; + + /** + * Hashtable for foreground color preferences + */ + private Map<String, IColor> fForeColorPref; + /** + * Hashtable for background color preferences + */ + private Map<String, IColor> fBackColorPref; + /** + * Hashtable for text color preferences + */ + private Map<String, IColor> fTextColorPref; + /** + * The reference to the preference store. + */ + private IPreferenceStore fPrefStore = null; + /** + * Color for the time compression selection + */ + private IColor fTimeCompressionSelectionColor = null; + /** + * Flag whether no focus selection or not. + */ + private boolean fNoFocusSelection = false; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Builds the Sequence Diagram preference handler: - Define the preference default values. - Load the currently used + * preferences setting + */ + protected SDViewPref() { + fPrefStore = Activator.getDefault().getPreferenceStore(); + + fPrefStore.setDefault(PREF_LINK_FONT, true); + fPrefStore.setDefault(PREF_EXCLUDE_EXTERNAL_TIME, true); + fPrefStore.setDefault(PREF_LIFELINE_WIDTH, 200); + fPrefStore.setDefault(PREF_USE_GRADIENT, true); + fPrefStore.setDefault(PREF_TOOLTIP, true); + + fFontPref = new Hashtable<>(); + fForeColorPref = new Hashtable<>(); + fBackColorPref = new Hashtable<>(); + fTextColorPref = new Hashtable<>(); + + for (int i = 0; i < FONT_LIST.length; i++) { + if (FONT_LIST[i].equals(PREF_FRAME_NAME)) { + FontData[] data = Display.getDefault().getSystemFont().getFontData(); + data[0].setStyle(SWT.BOLD); + PreferenceConverter.setDefault(fPrefStore, FONT_LIST[i], data[0]); + PreferenceConverter.setDefault(fPrefStore, FONT_LIST[i] + TEMP_TAG, data[0]); + } else { + PreferenceConverter.setDefault(fPrefStore, FONT_LIST[i], Display.getDefault().getSystemFont().getFontData()); + PreferenceConverter.setDefault(fPrefStore, FONT_LIST[i] + TEMP_TAG, Display.getDefault().getSystemFont().getFontData()); + } + } + + for (int i = 0; i < PREF_BACK_COLOR_LIST.length; i++) { + IColor color; + if ((PREF_BACK_COLOR_LIST[i].equals(PREF_EXEC)) || PREF_BACK_COLOR_LIST[i].equals(PREF_FRAME_NAME)) { + color = new ColorImpl(Display.getDefault(), 201, 222, 233); + } else if (PREF_BACK_COLOR_LIST[i].equals(PREF_LIFELINE)) { + color = new ColorImpl(Display.getDefault(), 220, 220, 220); + } else if (PREF_BACK_COLOR_LIST[i].equals(PREF_LIFELINE_HEADER)) { + color = new ColorImpl(Display.getDefault(), 245, 244, 244); + } else { + color = new ColorImpl(Display.getDefault(), 255, 255, 255); + } + PreferenceConverter.setDefault(fPrefStore, PREF_BACK_COLOR_LIST[i] + BACK_COLOR_POSTFIX, ((Color) color.getColor()).getRGB()); + PreferenceConverter.setDefault(fPrefStore, PREF_BACK_COLOR_LIST[i] + BACK_COLOR_POSTFIX + TEMP_TAG, ((Color) color.getColor()).getRGB()); + color.dispose(); + } + + for (int i = 0; i < PREF_FORE_COLOR_LIST.length; i++) { + IColor color; + if (PREF_FORE_COLOR_LIST[i].equals(PREF_LIFELINE)) { + color = new ColorImpl(Display.getDefault(), 129, 129, 129); + } else if (PREF_FORE_COLOR_LIST[i].equals(PREF_FRAME_NAME)) { + color = new ColorImpl(Display.getDefault(), 81, 153, 200); + } else if (PREF_FORE_COLOR_LIST[i].equals(PREF_LIFELINE_HEADER)) { + color = new ColorImpl(Display.getDefault(), 129, 127, 137); + } else { + color = new ColorImpl(Display.getDefault(), 134, 176, 212); + } + PreferenceConverter.setDefault(fPrefStore, PREF_FORE_COLOR_LIST[i] + FORE_COLOR_POSTFIX, ((Color) color.getColor()).getRGB()); + PreferenceConverter.setDefault(fPrefStore, PREF_FORE_COLOR_LIST[i] + FORE_COLOR_POSTFIX + TEMP_TAG, ((Color) color.getColor()).getRGB()); + color.dispose(); + } + + for (int i = 0; i < PREF_TEXT_COLOR_LIST.length; i++) { + IColor color; + if (PREF_TEXT_COLOR_LIST[i].equals(PREF_LIFELINE)) { + color = new ColorImpl(Display.getDefault(), 129, 129, 129); + } else if (PREF_TEXT_COLOR_LIST[i].equals(PREF_FRAME_NAME)) { + color = new ColorImpl(Display.getDefault(), 0, 0, 0); + } else if (PREF_TEXT_COLOR_LIST[i].equals(PREF_LIFELINE_HEADER)) { + color = new ColorImpl(Display.getDefault(), 129, 127, 137); + } else { + color = new ColorImpl(Display.getDefault(), 134, 176, 212); + } + PreferenceConverter.setDefault(fPrefStore, PREF_TEXT_COLOR_LIST[i] + TEXT_COLOR_POSTFIX, ((Color) color.getColor()).getRGB()); + PreferenceConverter.setDefault(fPrefStore, PREF_TEXT_COLOR_LIST[i] + TEXT_COLOR_POSTFIX + TEMP_TAG, ((Color) color.getColor()).getRGB()); + color.dispose(); + } + + IColor color = new ColorImpl(Display.getDefault(), 218, 232, 238); + PreferenceConverter.setDefault(fPrefStore, PREF_TIME_COMP, ((Color) color.getColor()).getRGB()); + color.dispose(); + + buildFontsAndColors(); + + fPrefStore.addPropertyChangeListener(this); + } + + /** + * Returns the PreferenceStore + * + * @return the PreferenceStore + */ + public IPreferenceStore getPreferenceStore() { + return fPrefStore; + } + + /** + * Apply the preferences in the preferences handler + */ + public void apply() { + buildFontsAndColors(); + fPrefStore.firePropertyChangeEvent("PREFOK", null, null); //$NON-NLS-1$ + } + + /** + * Returns an unique instance of the Sequence Diagram preference handler + * + * @return the preference handler instance + */ + public static synchronized SDViewPref getInstance() { + if (fHandle == null) { + fHandle = new SDViewPref(); + } + return fHandle; + } + + @Override + public IColor getForeGroundColor(String prefName) { + if ((fForeColorPref.get(prefName + FORE_COLOR_POSTFIX) != null) && (fForeColorPref.get(prefName + FORE_COLOR_POSTFIX) instanceof ColorImpl)) { + return fForeColorPref.get(prefName + FORE_COLOR_POSTFIX); + } + return ColorImpl.getSystemColor(SWT.COLOR_BLACK); + } + + @Override + public IColor getBackGroundColor(String prefName) { + if ((fBackColorPref.get(prefName + BACK_COLOR_POSTFIX) != null) && (fBackColorPref.get(prefName + BACK_COLOR_POSTFIX) instanceof ColorImpl)) { + return fBackColorPref.get(prefName + BACK_COLOR_POSTFIX); + } + return ColorImpl.getSystemColor(SWT.COLOR_WHITE); + } + + @Override + public IColor getFontColor(String prefName) { + if ((fTextColorPref.get(prefName + TEXT_COLOR_POSTFIX) != null) && (fTextColorPref.get(prefName + TEXT_COLOR_POSTFIX) instanceof ColorImpl)) { + return fTextColorPref.get(prefName + TEXT_COLOR_POSTFIX); + } + return ColorImpl.getSystemColor(SWT.COLOR_BLACK); + } + + @Override + public IColor getForeGroundColorSelection() { + if (fNoFocusSelection) { + return ColorImpl.getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND); + } + return ColorImpl.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT); + } + + @Override + public IColor getBackGroundColorSelection() { + if (fNoFocusSelection) { + return ColorImpl.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + } + return ColorImpl.getSystemColor(SWT.COLOR_LIST_SELECTION); + } + + @Override + public IFont getFont(String prefName) { + if (fFontPref.get(prefName) != null) { + return fFontPref.get(prefName); + } + return FontImpl.getSystemFont(); + } + + /** + * Returns the SwimLane width chosen + * + * @return the SwimLane width + */ + public int getLifelineWidth() { + return fPrefStore.getInt(PREF_LIFELINE_WIDTH); + } + + /** + * Returns if font linkage with zoom has been chosen + * + * @return true if checked false otherwise + */ + public boolean fontLinked() { + return fPrefStore.getBoolean(PREF_LINK_FONT); + } + + /** + * Returns the tooltip enablement + * + * @return true if checked false otherwise + */ + public boolean tooltipEnabled() { + return fPrefStore.getBoolean(PREF_TOOLTIP); + } + + /** + * Return true if the user do not want to take external time (basically found and lost messages with time) into + * account in the min max computation + * + * @return true if checked false otherwise + */ + public boolean excludeExternalTime() { + return fPrefStore.getBoolean(PREF_EXCLUDE_EXTERNAL_TIME); + } + + @Override + public boolean useGradienColor() { + return fPrefStore.getBoolean(PREF_USE_GRADIENT); + } + + @Override + public IColor getTimeCompressionSelectionColor() { + return fTimeCompressionSelectionColor; + } + + /** + * Builds the new colors and fonts according the current user selection when the OK or Apply button is clicked + */ + private void buildFontsAndColors() { + + Display display = Display.getDefault(); + + for (int i = 0; i < FONT_LIST.length; i++) { + FontData fontData = PreferenceConverter.getFontData(fPrefStore, FONT_LIST[i]); + if (fFontPref.get(FONT_LIST[i]) != null) { + fFontPref.get(FONT_LIST[i]).dispose(); + } + fFontPref.put(FONT_LIST[i], new FontImpl(display, fontData)); + } + + for (int i = 0; i < PREF_BACK_COLOR_LIST.length; i++) { + RGB rgb = PreferenceConverter.getColor(fPrefStore, PREF_BACK_COLOR_LIST[i] + BACK_COLOR_POSTFIX); + if (fBackColorPref.get(PREF_BACK_COLOR_LIST[i] + BACK_COLOR_POSTFIX) != null) { + fBackColorPref.get(PREF_BACK_COLOR_LIST[i] + BACK_COLOR_POSTFIX).dispose(); + } + fBackColorPref.put(PREF_BACK_COLOR_LIST[i] + BACK_COLOR_POSTFIX, new ColorImpl(display, rgb.red, rgb.green, rgb.blue)); + } + + for (int i = 0; i < PREF_FORE_COLOR_LIST.length; i++) { + RGB rgb = PreferenceConverter.getColor(fPrefStore, PREF_FORE_COLOR_LIST[i] + FORE_COLOR_POSTFIX); + if (fForeColorPref.get(PREF_FORE_COLOR_LIST[i] + FORE_COLOR_POSTFIX) != null) { + fForeColorPref.get(PREF_FORE_COLOR_LIST[i] + FORE_COLOR_POSTFIX).dispose(); + } + fForeColorPref.put(PREF_FORE_COLOR_LIST[i] + FORE_COLOR_POSTFIX, new ColorImpl(display, rgb.red, rgb.green, rgb.blue)); + } + + for (int i = 0; i < PREF_TEXT_COLOR_LIST.length; i++) { + RGB rgb = PreferenceConverter.getColor(fPrefStore, PREF_TEXT_COLOR_LIST[i] + TEXT_COLOR_POSTFIX); + if (fTextColorPref.get(PREF_TEXT_COLOR_LIST[i] + TEXT_COLOR_POSTFIX) != null) { + fTextColorPref.get(PREF_TEXT_COLOR_LIST[i] + TEXT_COLOR_POSTFIX).dispose(); + } + fTextColorPref.put(PREF_TEXT_COLOR_LIST[i] + TEXT_COLOR_POSTFIX, new ColorImpl(display, rgb.red, rgb.green, rgb.blue)); + } + + RGB rgb = PreferenceConverter.getColor(fPrefStore, PREF_TIME_COMP); + if (fTimeCompressionSelectionColor != null) { + fTimeCompressionSelectionColor.dispose(); + } + fTimeCompressionSelectionColor = new ColorImpl(display, rgb.red, rgb.green, rgb.blue); + } + + /** + * Add a property-change listener + * + * @param listener + * The listener to add + */ + public void addPropertyChangeListener(IPropertyChangeListener listener) { + fPrefStore.addPropertyChangeListener(listener); + } + + /** + * Remove a property-change listener + * + * @param listener + * The listerner to remove + */ + public void removePropertyChangeListener(IPropertyChangeListener listener) { + fPrefStore.removePropertyChangeListener(listener); + } + + @Override + public void propertyChange(PropertyChangeEvent event) { + if (!event.getProperty().equals("PREFOK")) { //$NON-NLS-1$ + buildFontsAndColors(); + fPrefStore.firePropertyChangeEvent("PREFOK", null, null); //$NON-NLS-1$ + } + } + + /** + * Set the "no focus selection" preference + * + * @param v + * New value to use + */ + public void setNoFocusSelection(boolean v) { + fNoFocusSelection = v; + } + + /** + * Returns a unmodifiable map with font preferences. + * + * @return map with font preferences + * @since 2.0 + */ + protected Map<String, IFont> getFontPref() { + return Collections.unmodifiableMap(fFontPref); + } + + /** + * Returns a unmodifiable map with foreground color preferences + * + * @return map with foreground color preferences + * @since 2.0 + */ + public Map<String, IColor> getForeColorPref() { + return Collections.unmodifiableMap(fForeColorPref); + } + + /** + * Returns a unmodifiable map with background color preferences + * + * @return map with background color preferences + * @since 2.0 + */ + public Map<String, IColor> getBackColorPref() { + return Collections.unmodifiableMap(fBackColorPref); + } + + /** + * Returns a unmodifiable map with text color preferences + * + * @return map with text color preferences + * @since 2.0 + */ + public Map<String, IColor> getTextColorPref() { + return Collections.unmodifiableMap(fTextColorPref); + } + + /** + * Returns the preference store. + * + * @return the preference store + * @since 2.0 + */ + public IPreferenceStore getPrefStore() { + return fPrefStore; + } + + /** + * Returns flag about focus selection + * + * @return flag about focus selection + * @since 2.0 + */ + public boolean isNoFocusSelection() { + return fNoFocusSelection; + } + + /** + * Returns the static font list. + * + * @return static font list + */ + public static String[] getFontList() { + return Arrays.copyOf(FONT_LIST, FONT_LIST.length); + } + + /** + * Returns the 2nd static font list. + * + * @return 2nd static font list + */ + public static String[] getFontList2() { + return Arrays.copyOf(FONT_LIST2, FONT_LIST2.length); + } + + /** + * Returns the preference background color list. + * + * @return preference background color list + */ + public static String[] getPrefBackColorList() { + return Arrays.copyOf(PREF_BACK_COLOR_LIST, PREF_BACK_COLOR_LIST.length); + } + + /** + * Returns the preference foreground color list. + * + * @return preference foreground color list + */ + public static String[] getPrefForeColorList() { + return Arrays.copyOf(PREF_FORE_COLOR_LIST, PREF_FORE_COLOR_LIST.length); + } + + /** + * Returns the preference text color list color list. + * + * @return preference text color list color list + */ + public static String[] getPrefTextColorList() { + return Arrays.copyOf(PREF_TEXT_COLOR_LIST, PREF_TEXT_COLOR_LIST.length); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/SDViewerPage.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/SDViewerPage.java new file mode 100755 index 0000000000..98ba43fef4 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/preferences/SDViewerPage.java @@ -0,0 +1,431 @@ +/********************************************************************** + * Copyright (c) 2005, 2014 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences; + +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.ColorFieldEditor; +import org.eclipse.jface.preference.FontFieldEditor; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +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.swt.widgets.Label; +import org.eclipse.swt.widgets.List; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.Messages; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +/** + * The Sequence Diagram preferences page implementation. + * + * @version 1.0 + * @author sveyrier + */ +public class SDViewerPage extends PreferencePage implements IWorkbenchPreferencePage, SelectionListener { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * Temporary preferences tag + */ + private static final String TEMP_TAG = SDViewPref.TEMP_TAG; + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + /** + * The preference handler used to access the PreferenceStore + */ + private SDViewPref fPreferences = null; + /** + * BackGround color selector + */ + private ColorFieldEditor fLineColor = null; + /** + * Foreground color selector + */ + private ColorFieldEditor fBackGroundColor = null; + /** + * Font color selector + */ + private ColorFieldEditor fTextColor = null; + /** + * List which display all modifiable sequence Diagram font + */ + private List fClassItemList = null; + /** + * Font selector (The same is used for each modifiable font) + */ + private FontFieldEditor fFont = null; + /** + * Link font when zooming selector + */ + private BooleanFieldEditor fLink = null; + /** + * Enable tooltip selector + */ + private BooleanFieldEditor fTooltip = null; + /** + * Do not take external time into account in the min max computation + */ + private BooleanFieldEditor fNoExternalTime = null; + /** + * Use gradient color selector + */ + private BooleanFieldEditor fUseGrad = null; + /** + * A button area. + */ + private Composite fButtonArea; + /** + * SwimLane width selector + */ + private IntegerFieldEditor fLifelineWidth = null; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + protected Control createContents(Composite parent) { + GridLayout gl = new GridLayout(); + gl.marginHeight = 0; + gl.marginWidth = 0; + parent.setLayout(gl); + Composite page = new Composite(parent, SWT.NONE); + GridLayout pageLayout = new GridLayout(); + pageLayout.numColumns = 2; + GridData pageLayoutdata = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL); + page.setLayoutData(pageLayoutdata); + page.setLayout(pageLayout); + + fTooltip = new BooleanFieldEditor(ISDPreferences.PREF_TOOLTIP, Messages.SequenceDiagram_ShowTooltips, page); + fTooltip.setPreferenceStore(fPreferences.getPreferenceStore()); + fTooltip.load(); + + // link font with zoom pref + fLink = new BooleanFieldEditor(ISDPreferences.PREF_LINK_FONT, Messages.SequenceDiagram_IncreaseFontSizeWhenZooming, page); + fLink.setPreferenceStore(fPreferences.getPreferenceStore()); + fLink.load(); + + fNoExternalTime = new BooleanFieldEditor(ISDPreferences.PREF_EXCLUDE_EXTERNAL_TIME, Messages.SequenceDiagram_ExcludeExternalTime, page); + fNoExternalTime.setPreferenceStore(fPreferences.getPreferenceStore()); + fNoExternalTime.load(); + + // use gradient color pref + fUseGrad = new BooleanFieldEditor(ISDPreferences.PREF_USE_GRADIENT, Messages.SequenceDiagram_UseGradientColor, page); + fUseGrad.setPreferenceStore(fPreferences.getPreferenceStore()); + fUseGrad.load(); + + Label separator = new Label(page, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.SHADOW_NONE); + GridData sepData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL); + separator.setLayoutData(sepData); + + Composite prefPage = new Composite(page, SWT.NONE); + GridLayout prefPageLayout = new GridLayout(); + prefPage.setLayoutData(pageLayoutdata); + prefPageLayout.numColumns = 1; + prefPage.setLayout(prefPageLayout); + + // swimLane width pref + fLifelineWidth = new IntegerFieldEditor(ISDPreferences.PREF_LIFELINE_WIDTH, Messages.SequenceDiagram_LifelineWidth, prefPage); + fLifelineWidth.setPreferenceStore(fPreferences.getPreferenceStore()); + fLifelineWidth.setValidRange(119, 500); + fLifelineWidth.load(); + + // not very nice + new Label(prefPage, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.SHADOW_NONE); + new Label(prefPage, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.SHADOW_NONE); + + // Font list pref + fClassItemList = new List(prefPage, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); + GridData tabItemLayoutdata = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL); + fClassItemList.setLayoutData(tabItemLayoutdata); + + String[] fontList2 = SDViewPref.getFontList2(); + for (int i = 0; i < fontList2.length; i++) { + fClassItemList.add(fontList2[i]); + } + fClassItemList.setSelection(0); + fClassItemList.addSelectionListener(this); + fButtonArea = new Composite(prefPage, SWT.NONE); + GridData tabItemLayoutdata2 = new GridData(GridData.HORIZONTAL_ALIGN_FILL/* |GridData.GRAB_HORIZONTAL */| GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL); + fButtonArea.setLayoutData(tabItemLayoutdata2); + GridLayout buttonAreaLayout = new GridLayout(); + buttonAreaLayout.numColumns = 1; + fButtonArea.setLayout(buttonAreaLayout); + + // font selector initialise for the lifeline font pref + String[] fontList = SDViewPref.getFontList(); + fFont = new FontFieldEditor(fontList[0], "",//$NON-NLS-1$ + Messages.SequenceDiagram_AaBbYyZz, fButtonArea); + fFont.getPreviewControl().setSize(500, 500); + fFont.setPreferenceStore(fPreferences.getPreferenceStore()); + fFont.load(); + + fBackGroundColor = new ColorFieldEditor(fontList[0] + SDViewPref.BACK_COLOR_POSTFIX, Messages.SequenceDiagram_Background, fButtonArea); + fBackGroundColor.setPreferenceStore(fPreferences.getPreferenceStore()); + fBackGroundColor.load(); + + fLineColor = new ColorFieldEditor(fontList[0] + SDViewPref.FORE_COLOR_POSTFIX, Messages.SequenceDiagram_Lines, fButtonArea); + fLineColor.setPreferenceStore(fPreferences.getPreferenceStore()); + fLineColor.load(); + + fTextColor = new ColorFieldEditor(fontList[0] + SDViewPref.TEXT_COLOR_POSTFIX, Messages.SequenceDiagram_Text, fButtonArea); + fTextColor.setPreferenceStore(fPreferences.getPreferenceStore()); + fTextColor.load(); + swapPref(true); + Dialog.applyDialogFont(page); + + return page; + } + + @Override + public void init(IWorkbench workbench) { + fPreferences = SDViewPref.getInstance(); + } + + @Override + protected void performApply() { + // Store the prefrences in the PreferenceStore + if (!fLifelineWidth.isValid()) { + fLifelineWidth.showErrorMessage(); + return; + } + fFont.store(); + fBackGroundColor.store(); + fLineColor.store(); + fLink.store(); + fTooltip.store(); + fNoExternalTime.store(); + fTextColor.store(); + fUseGrad.store(); + fLifelineWidth.store(); + swapPref(false); + // then save them in the preference file + fPreferences.apply(); + swapPref(true); + } + + @Override + public boolean performOk() { + performApply(); + return true; + } + + @Override + protected void performDefaults() { + fLink.loadDefault(); + fTooltip.loadDefault(); + fNoExternalTime.loadDefault(); + fUseGrad.loadDefault(); + fLifelineWidth.loadDefault(); + + // and all the fonts and colors + // fonts and colors are stored for each time because + // we are using only one FontFieldEditor + Set<String> keySet = SDViewPref.getInstance().getFontPref().keySet(); + Iterator<String> it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fFont.setPreferenceName((String) prefName); + fFont.loadDefault(); + fFont.setPreferenceName((String) prefName + TEMP_TAG); + fFont.store(); + } + } + + keySet = SDViewPref.getInstance().getBackColorPref().keySet(); + it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fBackGroundColor.setPreferenceName((String) prefName); + fBackGroundColor.loadDefault(); + fBackGroundColor.setPreferenceName((String) prefName + TEMP_TAG); + fBackGroundColor.store(); + } + + } + + String[] fontList = SDViewPref.getFontList(); + fBackGroundColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.BACK_COLOR_POSTFIX + TEMP_TAG); + fBackGroundColor.load(); + + keySet = SDViewPref.getInstance().getForeColorPref().keySet(); + it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fLineColor.setPreferenceName((String) prefName); + fLineColor.loadDefault(); + fLineColor.setPreferenceName((String) prefName + TEMP_TAG); + fLineColor.store(); + } + } + + fLineColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.FORE_COLOR_POSTFIX + TEMP_TAG); + fLineColor.load(); + + keySet = SDViewPref.getInstance().getTextColorPref().keySet(); + it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fTextColor.setPreferenceName((String) prefName); + fTextColor.loadDefault(); + fTextColor.setPreferenceName((String) prefName + TEMP_TAG); + fTextColor.store(); + } + } + fTextColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.TEXT_COLOR_POSTFIX + TEMP_TAG); + fTextColor.load(); + } + + @Override + public void widgetSelected(SelectionEvent e) { + // Store the past set font preference or else the + // FontFieldEditor reassignment will make us loose the current modification + fFont.store(); + fLineColor.store(); + fBackGroundColor.store(); + fTextColor.store(); + + String[] fontList = SDViewPref.getFontList(); + + // set the FontFieldEditor for the new selected graphNode font + fFont.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + TEMP_TAG); + fFont.load(); + + fBackGroundColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.BACK_COLOR_POSTFIX + TEMP_TAG); + fBackGroundColor.load(); + + fLineColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.FORE_COLOR_POSTFIX + TEMP_TAG); + fLineColor.load(); + + fTextColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.TEXT_COLOR_POSTFIX + TEMP_TAG); + fTextColor.load(); + + // No Background for message graphNodes + if ((fontList[fClassItemList.getSelectionIndex()].equals(ISDPreferences.PREF_SYNC_MESS)) || (fontList[fClassItemList.getSelectionIndex()].equals(ISDPreferences.PREF_SYNC_MESS_RET)) + || (fontList[fClassItemList.getSelectionIndex()].equals(ISDPreferences.PREF_ASYNC_MESS)) || (fontList[fClassItemList.getSelectionIndex()].equals(ISDPreferences.PREF_ASYNC_MESS_RET))) { + fBackGroundColor.setEnabled(false, fButtonArea); + } else { + fBackGroundColor.setEnabled(true, fButtonArea); + } + + // No font used for execution occurrence and global frame + if ((fontList[fClassItemList.getSelectionIndex()].equals(ISDPreferences.PREF_EXEC)) || (fontList[fClassItemList.getSelectionIndex()].equals(ISDPreferences.PREF_FRAME))) { + fTextColor.setEnabled(false, fButtonArea); + } else { + fTextColor.setEnabled(true, fButtonArea); + } + + if (fontList[fClassItemList.getSelectionIndex()].equals(ISDPreferences.PREF_FRAME)) { + fFont.setEnabled(false, fButtonArea); + } else { + fFont.setEnabled(true, fButtonArea); + } + } + + /** + * Swap viewer preferences. + * + * @param toTemp Switch to the temporary preferences + */ + protected void swapPref(boolean toTemp) { + String tag1 = "";//$NON-NLS-1$ + String tag2 = TEMP_TAG; + if (!toTemp) { + tag1 = TEMP_TAG; + tag2 = "";//$NON-NLS-1$ + } + Set<String> keySet = SDViewPref.getInstance().getFontPref().keySet(); + Iterator<String> it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fFont.setPreferenceName((String) prefName + tag1); + fFont.load(); + fFont.setPreferenceName((String) prefName + tag2); + fFont.store(); + } + } + + keySet = SDViewPref.getInstance().getBackColorPref().keySet(); + it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fBackGroundColor.setPreferenceName((String) prefName + tag1); + fBackGroundColor.load(); + fBackGroundColor.setPreferenceName((String) prefName + tag2); + fBackGroundColor.store(); + } + } + + keySet = SDViewPref.getInstance().getForeColorPref().keySet(); + it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fLineColor.setPreferenceName((String) prefName + tag1); + fLineColor.load(); + fLineColor.setPreferenceName((String) prefName + tag2); + fLineColor.store(); + } + } + + keySet = SDViewPref.getInstance().getTextColorPref().keySet(); + it = keySet.iterator(); + while (it.hasNext()) { + Object prefName = it.next(); + if (prefName instanceof String) { + fTextColor.setPreferenceName((String) prefName + tag1); + fTextColor.load(); + fTextColor.setPreferenceName((String) prefName + tag2); + fTextColor.store(); + } + } + String[] fontList = SDViewPref.getFontList(); + if (toTemp) { + // set the FontFieldEditor for the new selected graphNode font + fFont.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + TEMP_TAG); + fFont.load(); + + fBackGroundColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.BACK_COLOR_POSTFIX + TEMP_TAG); + fBackGroundColor.load(); + + fLineColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.FORE_COLOR_POSTFIX + TEMP_TAG); + fLineColor.load(); + + fTextColor.setPreferenceName(fontList[fClassItemList.getSelectionIndex()] + SDViewPref.TEXT_COLOR_POSTFIX + TEMP_TAG); + fTextColor.load(); + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/Messages.java new file mode 100755 index 0000000000..0a7f99f150 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/Messages.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2000, 2013 IBM Corporation, Ericsson + * 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 + * Bernd Hufmann - Updated for TMF + * Alexandre Montplaisir - Renamed variables + *******************************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.util; + +import org.eclipse.osgi.util.NLS; + +/** + * Messages related to the sequence diagram + * + * @version 1.0 + * @author Bernd Hufmann + * @since 2.0 + */ +@SuppressWarnings("javadoc") +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.messages"; //$NON-NLS-1$ + + private Messages() { + // Do not instantiate + } + + public static String SequenceDiagram_LifelineNode; + public static String SequenceDiagram_MessageNode; + public static String SequenceDiagram_LostMessageNode; + public static String SequenceDiagram_FoundMessageNode; + public static String SequenceDiagram_ExecutionOccurrenceWithParams; + + public static String SequenceDiagram_Find; + public static String SequenceDiagram_Close; + public static String SequenceDiagram_StringNotFound; + public static String SequenceDiagram_SequenceDiagramFind; + public static String SequenceDiagram_SearchFor; + public static String SequenceDiagram_MatchingString; + public static String SequenceDiagram_CaseSensitive; + public static String SequenceDiagram_Lifeline; + public static String SequenceDiagram_Stop; + public static String SequenceDiagram_SynchronousMessage; + public static String SequenceDiagram_SynchronousMessageReturn; + public static String SequenceDiagram_AsynchronousMessage; + public static String SequenceDiagram_AsynchronousMessageReturn; + public static String SequenceDiagram_or; + public static String SequenceDiagram_PreviousPage; + public static String SequenceDiagram_NextPage; + public static String SequenceDiagram_GoToPreviousPage; + public static String SequenceDiagram_GoToNextPage; + public static String SequenceDiagram_GoToMessage; + public static String SequenceDiagram_GoToMessageReturn; + public static String SequenceDiagram_EditFilters; + public static String SequenceDiagram_HidePatterns; + public static String SequenceDiagram_Pages; + + public static String SequenceDiagram_ZoomIn; + public static String SequenceDiagram_ZoomInTheDiagram; + public static String SequenceDiagram_ResetZoomFactor; + public static String SequenceDiagram_ZoomOut; + public static String SequenceDiagram_ZoomOutTheDiagram; + public static String SequenceDiagram_Select; + public static String SequenceDiagram_Max; + public static String SequenceDiagram_Min; + public static String SequenceDiagram_ListOfHideDisplayPatterns; + public static String SequenceDiagram_hide; + public static String SequenceDiagram_display; + public static String SequenceDiagram_EditIt; + public static String SequenceDiagram_Add; + public static String SequenceDiagram_Create; + public static String SequenceDiagram_Update; + public static String SequenceDiagram_Remove; + public static String SequenceDiagram_SequenceDiagramHidePatterns; + public static String SequenceDiagram_DefinitionOfHidePattern; + public static String SequenceDiagram_PageNavigation; + public static String SequenceDiagram_SequenceDiagramPages; + public static String SequenceDiagram_IsInBetween; + public static String SequenceDiagram_Total; + public static String SequenceDiagram_pages; + public static String SequenceDiagram_page; + + public static String SequenceDiagram_CurrentPage; + + public static String SequenceDiagram_Navigation; + public static String SequenceDiagram_OpenOverviewTooltip; + + public static String SequenceDiagram_LifelineWidth; + public static String SequenceDiagram_AaBbYyZz; + public static String SequenceDiagram_IncreaseFontSizeWhenZooming; + public static String SequenceDiagram_ExcludeExternalTime; + public static String SequenceDiagram_UseGradientColor; + public static String SequenceDiagram_Background; + public static String SequenceDiagram_Lines; + public static String SequenceDiagram_Text; + + public static String SequenceDiagram_ExecutionOccurrence; + public static String SequenceDiagram_SyncMessage; + public static String SequenceDiagram_SyncMessageReturn; + public static String SequenceDiagram_AsyncMessage; + public static String SequenceDiagram_AsyncMessageReturn; + public static String SequenceDiagram_Frame; + public static String SequenceDiagram_LifelineHeader; + public static String SequenceDiagram_FrameTitle; + public static String SequenceDiagram_ShowTooltips; + public static String SequenceDiagram_Error; + public static String SequenceDiagram_InvalidRange; + public static String SequenceDiagram_InvalidNbVertical; + public static String SequenceDiagram_InvalidNbHorizontal; + public static String SequenceDiagram_NoPageSelected; + public static String SequenceDiagram_FromPage; + + public static String SequenceDiagram_to; + public static String SequenceDiagram_SelectedPages; + public static String SequenceDiagram_CurrentView; + public static String SequenceDiagram_AllPages; + public static String TotalNumberOfPages; + public static String SequenceDiagram_NumberOfHorizontalPages; + public static String SequenceDiagram_NumberOfVerticalPages; + public static String SequenceDiagram_UseCurrentZoom; + public static String SequenceDiagram_ZoomOption; + public static String SequenceDiagram_Print; + public static String SequenceDiagram_Printer; + public static String SequenceDiagram_plus; + public static String SequenceDiagram_Page; + public static String SequenceDiagram_PrintRange; + public static String SequenceDiagram_Preview; + + public static String SequenceDiagram_TimeCompressionBarConfig; + public static String SequenceDiagram_MinTime; + public static String SequenceDiagram_MaxTime; + public static String SequenceDiagram_Default; + + public static String SequenceDiagram_NoPrinterSelected; + public static String SequenceDiagram_Scale; + public static String SequenceDiagram_Precision; + public static String SequenceDiagram_Delta; + + public static String SequenceDiagram_FirstPage; + public static String SequenceDiagram_GoToFirstPage; + public static String SequenceDiagram_LastPage; + public static String SequenceDiagram_GoToLastPage; + + public static String SequenceDiagram_ShowNodeEnd; + public static String SequenceDiagram_ShowNodeStart; + public static String SequenceDiagram_ConfigureMinMax; + + static { + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortAsyncForBackward.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortAsyncForBackward.java new file mode 100755 index 0000000000..004c23e8fa --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortAsyncForBackward.java @@ -0,0 +1,96 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.util; + +import java.io.Serializable; +import java.util.Comparator; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; + +/** + * Asynchronous message comparator. + * + * Compares two asyncMessages only taking into account the event occurrence when their + * appear.<br> + * + * Used to order the AsyncMessage list insuring that the previous node has both of his ends smaller than the current node + * + * @version 1.0 + * @author sveyrier + * + */ +public class SortAsyncForBackward implements Comparator<GraphNode>, Serializable { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * Serial version UID + */ + private static final long serialVersionUID = 603959931263853359L; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int compare(GraphNode arg0, GraphNode arg1) { + if (arg0 instanceof AsyncMessage && arg1 instanceof AsyncMessage) { + AsyncMessage m1 = (AsyncMessage) arg0; + AsyncMessage m2 = (AsyncMessage) arg1; + int m1Max, m2Max; + // AsyncMessage has two ends which may have different event occurrences + // Search for the greater event occurrence for each messages + if (m1.getStartOccurrence() > m1.getEndOccurrence()) { + m1Max = m1.getStartOccurrence(); + } else { + m1Max = m1.getEndOccurrence(); + } + if (m2.getStartOccurrence() > m2.getEndOccurrence()) { + m2Max = m2.getStartOccurrence(); + } else { + m2Max = m2.getEndOccurrence(); + } + + int m1Min, m2Min; + // Search for the smaller event occurrence for each messages + if (m1.getStartOccurrence() > m1.getEndOccurrence()) { + m1Min = m1.getEndOccurrence(); + } else { + m1Min = m1.getStartOccurrence(); + } + if (m2.getStartOccurrence() > m2.getEndOccurrence()) { + m2Min = m2.getEndOccurrence(); + } else { + m2Min = m2.getStartOccurrence(); + } + + if (m1Max > m2Max) { + return 1; + } else if (m1Max == m2Max) { + if (m1Min == m2Min) { + return 0; + } else if (m1Min > m2Min) { + return -1; + } else { + return 1; + } + } else { + return -1; + } + } + return 0; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortAsyncMessageComparator.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortAsyncMessageComparator.java new file mode 100755 index 0000000000..251eb46bbf --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortAsyncMessageComparator.java @@ -0,0 +1,94 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.util; + +import java.io.Serializable; +import java.util.Comparator; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.AsyncMessage; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; + +/** + * Asynchronous message comparator Compare two AsyncMessages only taking into account the event occurrence when their + * appear.<br> + * + * Used to order the AsyncMessage list insuring that next node has one of his ends greater than the current node + * + * @version 1.0 + * @author sveyrier + * + */ +public class SortAsyncMessageComparator implements Comparator<GraphNode>, Serializable { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + /** + * Serial version UID + */ + private static final long serialVersionUID = 1L; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int compare(GraphNode arg0, GraphNode arg1) { + if (arg0 instanceof AsyncMessage && arg1 instanceof AsyncMessage) { + AsyncMessage m1 = (AsyncMessage) arg0; + AsyncMessage m2 = (AsyncMessage) arg1; + int m1Min, m2Min; + // AsyncMessage has two ends which may have different event occurrences + // Search for the smaller event occurrence for each messages + if (m1.getStartOccurrence() > m1.getEndOccurrence()) { + m1Min = m1.getEndOccurrence(); + } else { + m1Min = m1.getStartOccurrence(); + } + if (m2.getStartOccurrence() > m2.getEndOccurrence()) { + m2Min = m2.getEndOccurrence(); + } else { + m2Min = m2.getStartOccurrence(); + } + + int m1Max, m2Max; + // Search for the greater event occurrence for each messages + if (m1.getStartOccurrence() > m1.getEndOccurrence()) { + m1Max = m1.getStartOccurrence(); + } else { + m1Max = m1.getEndOccurrence(); + } + if (m2.getStartOccurrence() > m2.getEndOccurrence()) { + m2Max = m2.getStartOccurrence(); + } else { + m2Max = m2.getEndOccurrence(); + } + + if (m1Min > m2Min) { + return 1; + } else if ((m1Min == m2Min)) { + if (m1Max == m2Max) { + return 0; + } else if (m1Max > m2Max) { + return -1; + } else { + return 1; + } + } else { + return -1; + } + } + return 0; + } + +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortSyncMessageComparator.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortSyncMessageComparator.java new file mode 100755 index 0000000000..a19d28b1f1 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/SortSyncMessageComparator.java @@ -0,0 +1,61 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.util; + +import java.io.Serializable; +import java.util.Comparator; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode; +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SyncMessage; + +/** + * Synchronous message comparator Compare two syncMessages only taking into account the event occurrence when their + * appear.<br> + * + * The message with the greater event occurrence is considered to be the greater.<br> + * + * @version 1.0 + * @author sveyrier + * + */ +public class SortSyncMessageComparator implements Comparator<GraphNode>, Serializable { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * Serial version UID + */ + private static final long serialVersionUID = 4781250984753283718L; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int compare(GraphNode arg0, GraphNode arg1) { + if (arg0 instanceof SyncMessage && arg1 instanceof SyncMessage) { + SyncMessage m1 = (SyncMessage) arg0; + SyncMessage m2 = (SyncMessage) arg1; + if (m1.getEventOccurrence() > m2.getEventOccurrence()) { + return 1; + } else if (m1.getEventOccurrence() == m2.getEventOccurrence()) { + return 0; + } else { + return -1; + } + } + return 0; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/TimeEventComparator.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/TimeEventComparator.java new file mode 100755 index 0000000000..3c97af2df7 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/TimeEventComparator.java @@ -0,0 +1,53 @@ +/********************************************************************** + * Copyright (c) 2005, 2013 IBM Corporation, Ericsson + * 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 - Initial API and implementation + * Bernd Hufmann - Updated for TMF + **********************************************************************/ + +package org.eclipse.tracecompass.tmf.ui.views.uml2sd.util; + +import java.io.Serializable; +import java.util.Comparator; + +import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.SDTimeEvent; + +/** + * Time event comparator + * + * @version 1.0 + * @author sveyrier + */ +public class TimeEventComparator implements Comparator<SDTimeEvent>, Serializable { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + /** + * Serial version UID + */ + private static final long serialVersionUID = 5885497718872575669L; + + // ------------------------------------------------------------------------ + // Methods + // ------------------------------------------------------------------------ + + @Override + public int compare(SDTimeEvent arg0, SDTimeEvent arg1) { + SDTimeEvent t1 = arg0; + SDTimeEvent t2 = arg1; + if (t1.getEvent() > t2.getEvent()) { + return 1; + } + else if (t1.getEvent() == t2.getEvent()) { + return 0; + } + return -1; + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/messages.properties new file mode 100755 index 0000000000..f8ef1c7c63 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/uml2sd/util/messages.properties @@ -0,0 +1,132 @@ +############################################################################### +# Copyright (c) 2005, 2013 IBM Corporation, Ericsson +# +# 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 +# Bernd Hufmann - Updated for TMF +# Alexandre Montplaisir - Renamed variables +############################################################################### + +# NLS_MESSAGEFORMAT_VAR +# NLS_ENCODING=UTF-8 + +##SD +SequenceDiagram_LifelineNode=Lifeline node {0} +SequenceDiagram_MessageNode=Message node {0} from Lifeline {1} at event occurrence {2} to Lifeline {3} at event occurrence {4} +SequenceDiagram_LostMessageNode=Lost Message node {0} from Lifeline {1} at event occurrence {2} +SequenceDiagram_FoundMessageNode=Found Message node {0} to Lifeline {1} at event occurrence {2} +SequenceDiagram_ExecutionOccurrenceWithParams=Execution occurrence {0} on Lifeline {1} from event occurrence {2} to {3} + +SequenceDiagram_Find=Find +SequenceDiagram_Close=Close +SequenceDiagram_StringNotFound=String not found +SequenceDiagram_SequenceDiagramFind=Sequence Diagram Find +SequenceDiagram_SearchFor=Search for +SequenceDiagram_MatchingString=Matching string (regular expression): +SequenceDiagram_CaseSensitive=Case sensitive +SequenceDiagram_Lifeline=Lifeline +SequenceDiagram_Stop=Stop +SequenceDiagram_SynchronousMessage=Synchronous message +SequenceDiagram_SynchronousMessageReturn=Synchronous message return +SequenceDiagram_AsynchronousMessage=Asynchronous message +SequenceDiagram_AsynchronousMessageReturn=Asynchronous message return +SequenceDiagram_or=or +SequenceDiagram_PreviousPage=Previous page +SequenceDiagram_NextPage=Next page +SequenceDiagram_GoToPreviousPage=Go to previous page +SequenceDiagram_GoToNextPage=Go to next page +SequenceDiagram_GoToMessage=Go to message +SequenceDiagram_GoToMessageReturn=Go to message return +SequenceDiagram_EditFilters=Edit filters... +SequenceDiagram_HidePatterns=Hide Patterns... +SequenceDiagram_Pages=Pages... +SequenceDiagram_ZoomIn=Zoom in +SequenceDiagram_ZoomInTheDiagram=Zoom in the diagram +SequenceDiagram_ResetZoomFactor=Reset zoom factor +SequenceDiagram_ZoomOut=Zoom out +SequenceDiagram_ZoomOutTheDiagram=Zoom out the diagram +SequenceDiagram_Select=Select +SequenceDiagram_Max=Max +SequenceDiagram_Min=Min +SequenceDiagram_ListOfHideDisplayPatterns=List of hide/display patterns +SequenceDiagram_hide=hide +SequenceDiagram_display=display +SequenceDiagram_EditIt=Edit it +SequenceDiagram_Add=Add... +SequenceDiagram_Create=Create +SequenceDiagram_Update=Update +SequenceDiagram_Remove=Remove +SequenceDiagram_SequenceDiagramHidePatterns=Sequence Diagram Hide Patterns +SequenceDiagram_DefinitionOfHidePattern=Definition of Hide Pattern +SequenceDiagram_PageNavigation=Page navigation +SequenceDiagram_SequenceDiagramPages=Sequence Diagram Pages +SequenceDiagram_IsInBetween={0,number} <= value <= {1,number} +SequenceDiagram_Total=Total: +SequenceDiagram_pages=pages +SequenceDiagram_page=page +SequenceDiagram_CurrentPage=Current page: +SequenceDiagram_Navigation=Navigation +SequenceDiagram_OpenOverviewTooltip=Click to open Overview\nDrag mouse to scroll\nUse Shift+Drag, Ctrl+Drag to scroll slower\nUse Shift+Ctrl+Drag to scroll very fine +SequenceDiagram_LifelineWidth=&Lifeline Width (in pixels) +SequenceDiagram_AaBbYyZz=AaBbYyZz +SequenceDiagram_IncreaseFontSizeWhenZooming=&Increase font size when zooming +SequenceDiagram_ExcludeExternalTime=&Exclude external time +SequenceDiagram_UseGradientColor=&Use gradient color +SequenceDiagram_Background=Background +SequenceDiagram_Lines=Lines +SequenceDiagram_Text=Text +SequenceDiagram_ExecutionOccurrence=Execution Occurrence +SequenceDiagram_SyncMessage=Sync Message +SequenceDiagram_SyncMessageReturn=Sync Message Return +SequenceDiagram_AsyncMessage=Async Message +SequenceDiagram_AsyncMessageReturn=Async Message Return +SequenceDiagram_Frame=Frame +SequenceDiagram_LifelineHeader=Lifeline Header +SequenceDiagram_FrameTitle=Frame Title +SequenceDiagram_ShowTooltips=&Show tooltips +SequenceDiagram_Error=Error +SequenceDiagram_InvalidRange=Invalid range +SequenceDiagram_InvalidNbVertical=Number of vertical pages is invalid +SequenceDiagram_InvalidNbHorizontal=Number of horizontal pages is invalid +SequenceDiagram_NoPageSelected=There is no page selected +SequenceDiagram_FromPage=F&rom page +SequenceDiagram_to=&to +SequenceDiagram_SelectedPages=Selected pages +SequenceDiagram_CurrentView=Current view +SequenceDiagram_AllPages=&All pages +TotalNumberOfPages=Total number of pages: +SequenceDiagram_NumberOfHorizontalPages=Number of horizontal pages: +SequenceDiagram_NumberOfVerticalPages=Number of vertical pages: +SequenceDiagram_UseCurrentZoom=Use current zoom +SequenceDiagram_ZoomOption=Zoom options +SequenceDiagram_Print=Print +SequenceDiagram_Printer=Printer... +SequenceDiagram_plus={0} + {1} +SequenceDiagram_Page=Page {0} +SequenceDiagram_PrintRange=Print range +SequenceDiagram_Preview=Preview + +SequenceDiagram_TimeCompressionBarConfig=TimeCompression bar configuration +SequenceDiagram_MinTime=Min time +SequenceDiagram_MaxTime=Max time +SequenceDiagram_Default=Default + +SequenceDiagram_NoPrinterSelected=No default printer is selected. Click Printer button first and select a printer. + +SequenceDiagram_Scale=Scale +SequenceDiagram_Precision=Precision +SequenceDiagram_Delta=Delta + +SequenceDiagram_FirstPage=First page +SequenceDiagram_GoToFirstPage=Go to first page +SequenceDiagram_LastPage=Last page +SequenceDiagram_GoToLastPage=Go to last page + +SequenceDiagram_ShowNodeEnd=Show the node end +SequenceDiagram_ShowNodeStart=Show the node start +SequenceDiagram_ConfigureMinMax=Configure Min Max... |