org.eclipse.tracecompass.tmf.remote.ui",
org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg;x-friends:="org.eclipse.tracecompass.tmf.remote.ui,org.eclipse.tracecompass.tmf.remote.ui.tests",
org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.importexport;x-friends:="org.eclipse.tracecompass.tmf.remote.ui",
+ org.eclipse.tracecompass.internal.tmf.ui.views;x-internal:=true,
org.eclipse.tracecompass.tmf.ui,
org.eclipse.tracecompass.tmf.ui.analysis,
org.eclipse.tracecompass.tmf.ui.editors,
org.eclipse.tracecompass.tmf.ui.project.model,
org.eclipse.tracecompass.tmf.ui.project.wizards,
org.eclipse.tracecompass.tmf.ui.properties,
+ org.eclipse.tracecompass.tmf.ui.signal,
org.eclipse.tracecompass.tmf.ui.viewers,
org.eclipse.tracecompass.tmf.ui.viewers.events,
org.eclipse.tracecompass.tmf.ui.viewers.events.columns,
String IMG_UI_FOLLOW_ARROW_BACKWARD = ICONS_PATH + "elcl16/follow_arrow_bwd.gif";
String IMG_UI_SHOW_LOST_EVENTS = ICONS_PATH + "elcl16/hide_lost_events.gif";
String IMG_UI_SHOW_HIST_TRACES = ICONS_PATH + "elcl16/show_hist_traces.gif";
+ String IMG_UI_LINK = ICONS_PATH + "elcl16/link.gif";
/* eview16 */
String IMG_UI_SEQ_DIAGRAM_OBJ = ICONS_PATH + "eview16/sequencediagram_view.gif";
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * Marc-Andre Laperle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.ui;
+
+/**
+ * Preferences for TMF UI
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITmfUIPreferences {
+ /**
+ * Preference for aligning the views based on the time axis
+ */
+ String PREF_ALIGN_VIEWS = "PREF_ALIGN_VIEWS"; //$NON-NLS-1$
+}
public static String TmfView_PinActionNameText;
public static String TmfView_PinActionToolTipText;
+ public static String TmfView_AlignViewsActionNameText;
+ public static String TmfView_AlignViewsActionToolTipText;
public static String CallStackPresentationProvider_Thread;
public static String CallStackView_FunctionColumn;
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * Marc-Andre Laperle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.ui;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+
+/**
+ * Initializes TMF UI preferences
+ */
+public class TmfUIPreferenceInitializer extends AbstractPreferenceInitializer {
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IEclipsePreferences defaultPreferences = DefaultScope.INSTANCE.getNode(Activator.PLUGIN_ID);
+ defaultPreferences.putBoolean(ITmfUIPreferences.PREF_ALIGN_VIEWS, true);
+ }
+}
TmfView_PinActionNameText=Pin View
TmfView_PinActionToolTipText=Pin View
+TmfView_AlignViewsActionNameText=Align Views
+TmfView_AlignViewsActionToolTipText=Align Views
# Call Stack View
CallStackPresentationProvider_Thread=Thread
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * Marc-Andre Laperle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.ui.views;
+
+import org.eclipse.core.runtime.preferences.InstanceScope;
+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.ITmfUIPreferences;
+import org.eclipse.tracecompass.internal.tmf.ui.Messages;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
+
+/**
+ * An action that toggles time alignment of views.
+ *
+ * @see ITmfTimeAligned
+ */
+public class TimeAlignViewsAction extends Action {
+ /**
+ * Creates a AlignViewsAction
+ */
+ public TimeAlignViewsAction() {
+ super(Messages.TmfView_AlignViewsActionNameText, IAction.AS_CHECK_BOX);
+
+ setId("org.eclipse.tracecompass.tmf.ui.views.TimeAlignViewsAction"); //$NON-NLS-1$
+ setToolTipText(Messages.TmfView_AlignViewsActionToolTipText);
+ setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_LINK));
+ setChecked(isPreferenceEnabled());
+ }
+
+ @Override
+ public void run() {
+ boolean newValue = !isPreferenceEnabled();
+ InstanceScope.INSTANCE.getNode(Activator.PLUGIN_ID).putBoolean(ITmfUIPreferences.PREF_ALIGN_VIEWS, newValue);
+ setChecked(newValue);
+ }
+
+ private static boolean isPreferenceEnabled() {
+ return InstanceScope.INSTANCE.getNode(Activator.PLUGIN_ID).getBoolean(ITmfUIPreferences.PREF_ALIGN_VIEWS, true);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * Marc-Andre Laperle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.ui.views;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tracecompass.internal.tmf.ui.Activator;
+import org.eclipse.tracecompass.internal.tmf.ui.ITmfUIPreferences;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
+import org.eclipse.tracecompass.tmf.ui.views.TmfView;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Receives various notifications for realignment and
+ * performs the alignment on the appropriate views.
+ *
+ * @since 1.0
+ */
+public class TmfAlignmentSynchronizer {
+
+ private static final long THROTTLE_DELAY = 500;
+ private static final int NEAR_THRESHOLD = 10;
+ private final Timer fTimer;
+ private final List<AlignmentOperation> fPendingOperations = Collections.synchronizedList(new ArrayList<AlignmentOperation>());
+
+ private TimerTask fCurrentTask;
+
+ /**
+ * Constructor
+ */
+ public TmfAlignmentSynchronizer() {
+ TmfSignalManager.register(this);
+ fTimer = new Timer();
+ createPreferenceListener();
+ fCurrentTask = new TimerTask() {
+ @Override
+ public void run() {
+ /* Do nothing */
+ }
+ };
+ }
+
+ private IPreferenceChangeListener createPreferenceListener() {
+ IPreferenceChangeListener listener = new IPreferenceChangeListener() {
+
+ @Override
+ public void preferenceChange(PreferenceChangeEvent event) {
+ if (event.getKey().equals(ITmfUIPreferences.PREF_ALIGN_VIEWS)) {
+ Object oldValue = event.getOldValue();
+ Object newValue = event.getNewValue();
+ if (Boolean.toString(false).equals(oldValue) && Boolean.toString(true).equals(newValue)) {
+ realignViews();
+ } else if (Boolean.toString(true).equals(oldValue) && Boolean.toString(false).equals(newValue)) {
+ restoreViews();
+ }
+ }
+ }
+ };
+ InstanceScope.INSTANCE.getNode(Activator.PLUGIN_ID).addPreferenceChangeListener(listener);
+ return listener;
+ }
+
+ private class AlignmentOperation {
+ final TmfView fView;
+ final TmfTimeViewAlignmentInfo fAlignmentInfo;
+
+ public AlignmentOperation(TmfView view, TmfTimeViewAlignmentInfo timeViewAlignmentInfo) {
+ fView = view;
+ fAlignmentInfo = timeViewAlignmentInfo;
+ }
+ }
+
+ private class AlignTask extends TimerTask {
+
+ @Override
+ public void run() {
+ final List<AlignmentOperation> fCopy;
+ synchronized (fPendingOperations) {
+ fCopy = new ArrayList<>(fPendingOperations);
+ fPendingOperations.clear();
+ }
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ performAllAlignments(fCopy);
+ }
+ });
+ }
+ }
+
+ /**
+ * Handle a view that was just resized.
+ *
+ * @param view
+ * the view that was resized
+ */
+ public void handleViewResized(TmfView view) {
+ TmfTimeViewAlignmentInfo alignmentInfo = new TmfTimeViewAlignmentInfo(view.getParentComposite().getShell(), getViewLocation(view), 0);
+
+ // Don't use a view that was just resized as a reference view.
+ // Otherwise, a view that was just
+ // created might use itself as a reference but we want to
+ // keep the existing alignment from the other views.
+ ITmfTimeAligned referenceView = getReferenceView(alignmentInfo, view);
+ if (referenceView != null) {
+ queueAlignment(referenceView.getTimeViewAlignmentInfo(), false);
+ }
+ }
+
+ /**
+ * Handle a view that was just closed.
+ *
+ * @param view
+ * the view that was closed
+ */
+ public void handleViewClosed(TmfView view) {
+ // Realign views so that they can use the maximum available width in the
+ // event that a narrow view was just closed
+ realignViews(view.getSite().getPage());
+ }
+
+ /**
+ * Process signal for alignment.
+ *
+ * @param signal the alignment signal
+ */
+ @TmfSignalHandler
+ public void timeViewAlignmentUpdated(TmfTimeViewAlignmentSignal signal) {
+ queueAlignment(signal.getTimeViewAlignmentInfo(), signal.IsSynchronous());
+ }
+
+ /**
+ * Perform all alignment operations for the specified alignment
+ * informations.
+ *
+ * <pre>
+ * - The alignment algorithm chooses the narrowest width to accommodate all views.
+ * - View positions are recomputed for extra accuracy since the views could have been moved or resized.
+ * - Based on the up-to-date view positions, only views that are near and aligned with each other
+ * </pre>
+ */
+ private static void performAllAlignments(final List<AlignmentOperation> alignments) {
+ for (final AlignmentOperation info : alignments) {
+ performAlignment(info);
+ }
+ }
+
+ private static void performAlignment(AlignmentOperation info) {
+
+ TmfView referenceView = info.fView;
+ if (isDisposedView(referenceView)) {
+ return;
+ }
+
+ TmfTimeViewAlignmentInfo alignmentInfo = info.fAlignmentInfo;
+ // The location of the view might have changed (resize, etc). Update the alignment info.
+ alignmentInfo = new TmfTimeViewAlignmentInfo(alignmentInfo.getShell(), getViewLocation(referenceView), getClampedTimeAxisOffset(alignmentInfo));
+
+ TmfView narrowestView = getNarrowestView(alignmentInfo);
+ if (narrowestView == null) {
+ // No valid view found for this alignment. This could mean that the views for this alignment are now too narrow (width == 0) or that shell is not a workbench window.
+ return;
+ }
+
+ int narrowestWidth = ((ITmfTimeAligned) narrowestView).getAvailableWidth(getClampedTimeAxisOffset(alignmentInfo));
+ narrowestWidth = getClampedTimeAxisWidth(alignmentInfo, narrowestWidth);
+ IViewReference[] viewReferences = referenceView.getSite().getPage().getViewReferences();
+ for (IViewReference ref : viewReferences) {
+ IViewPart view = ref.getView(false);
+ if (isTimeAlignedView(view)) {
+ TmfView tmfView = (TmfView) view;
+ ITmfTimeAligned alignedView = (ITmfTimeAligned) view;
+ if (!isDisposedView(tmfView) && isViewLocationNear(getViewLocation(tmfView), alignmentInfo.getViewLocation())) {
+ alignedView.performAlign(getClampedTimeAxisOffset(alignmentInfo), narrowestWidth);
+ }
+ }
+ }
+ }
+
+ /**
+ * Realign all views
+ */
+ private void realignViews() {
+ for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+ for (IWorkbenchPage page : window.getPages()) {
+ realignViews(page);
+ }
+ }
+ }
+
+ /**
+ * Realign views inside a given page
+ *
+ * @param page
+ * the workbench page
+ */
+ private void realignViews(IWorkbenchPage page) {
+ IViewReference[] viewReferences = page.getViewReferences();
+ for (IViewReference ref : viewReferences) {
+ IViewPart view = ref.getView(false);
+ if (isTimeAlignedView(view)) {
+ queueAlignment(((ITmfTimeAligned) view).getTimeViewAlignmentInfo(), false);
+ }
+ }
+ }
+
+ /**
+ * Restore the views to their respective maximum widths
+ */
+ private static void restoreViews() {
+ // We set the width to Integer.MAX_VALUE so that the
+ // views remove any "filler" space they might have.
+ for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+ for (IWorkbenchPage page : window.getPages()) {
+ for (IViewReference ref : page.getViewReferences()) {
+ restoreView(ref);
+ }
+ }
+ }
+ }
+
+ private static void restoreView(IViewReference ref) {
+ IViewPart view = ref.getView(false);
+ if (isTimeAlignedView(view)) {
+ ITmfTimeAligned alignedView = (ITmfTimeAligned) view;
+ alignedView.performAlign(getClampedTimeAxisOffset(alignedView.getTimeViewAlignmentInfo()), Integer.MAX_VALUE);
+ }
+ }
+
+ private static boolean isTimeAlignedView(IViewPart view) {
+ if (view instanceof TmfView && view instanceof ITmfTimeAligned) {
+ Composite parentComposite = ((TmfView) view).getParentComposite();
+ if (parentComposite != null && !parentComposite.isDisposed()) {
+ return true;
+ }
+ }
+ return view instanceof TmfView && view instanceof ITmfTimeAligned;
+ }
+
+ private static boolean isDisposedView(TmfView view) {
+ Composite parentComposite = (view).getParentComposite();
+ return parentComposite != null && parentComposite.isDisposed();
+ }
+
+ /**
+ * Queue the operation for processing. If an operation is considered the
+ * same alignment (shell, location) as a previously queued one, it will
+ * replace the old one. This way, only one up-to-date alignment operation is
+ * kept per set of time-axis aligned views. The processing of the operation
+ * is also throttled (TimerTask).
+ *
+ * @param operation
+ * the operation to queue
+ */
+ private void queue(AlignmentOperation operation) {
+ synchronized(fPendingOperations) {
+ fCurrentTask.cancel();
+ for (AlignmentOperation pendingOperation : fPendingOperations) {
+ if (isSameAlignment(operation, pendingOperation)) {
+ fPendingOperations.remove(pendingOperation);
+ break;
+ }
+ }
+ fPendingOperations.add(operation);
+ fCurrentTask = new AlignTask();
+ fTimer.schedule(fCurrentTask, THROTTLE_DELAY);
+ }
+ }
+
+ /**
+ * Two operations are considered to be for the same set of time-axis aligned
+ * views if they are on the same Shell and near the same location.
+ */
+ private static boolean isSameAlignment(AlignmentOperation operation1, AlignmentOperation operation2) {
+ if (operation1.fView == operation2.fView) {
+ return true;
+ }
+
+ if (operation1.fAlignmentInfo.getShell() != operation2.fAlignmentInfo.getShell()) {
+ return false;
+ }
+
+ if (isViewLocationNear(getViewLocation(operation1.fView), getViewLocation(operation2.fView))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean isViewLocationNear(Point location1, Point location2) {
+ return Math.abs(location1.x - location2.x) < NEAR_THRESHOLD;
+ }
+
+ private static Point getViewLocation(TmfView view) {
+ return view.getParentComposite().toDisplay(0, 0);
+ }
+
+ private void queueAlignment(TmfTimeViewAlignmentInfo timeViewAlignmentInfo, boolean synchronous) {
+ if (isAlignViewsPreferenceEnabled()) {
+ IWorkbenchWindow workbenchWindow = getWorkbenchWindow(timeViewAlignmentInfo.getShell());
+ if (workbenchWindow == null || workbenchWindow.getActivePage() == null) {
+ // Only time aligned views that are part of a workbench window are supported
+ return;
+ }
+
+ // We need a view so that we can compute position right as we are
+ // about to realign the views. The view could have been resized,
+ // moved, etc.
+ TmfView view = (TmfView) getReferenceView(timeViewAlignmentInfo, null);
+ if (view == null) {
+ // No valid view found for this alignment
+ return;
+ }
+
+ AlignmentOperation operation = new AlignmentOperation(view, timeViewAlignmentInfo);
+ if (synchronous) {
+ performAlignment(operation);
+ } else {
+ queue(operation);
+ }
+ }
+ }
+
+ private static boolean isAlignViewsPreferenceEnabled() {
+ return InstanceScope.INSTANCE.getNode(Activator.PLUGIN_ID).getBoolean(ITmfUIPreferences.PREF_ALIGN_VIEWS, true);
+ }
+
+ /**
+ * Get a view that corresponds to the alignment information. The view is
+ * meant to be used as a "reference" for other views to align on. Heuristics
+ * are applied to choose the best view. For example, the view has to be
+ * visible. It also will prioritize the view with lowest time axis offset
+ * because most of the interesting data should be in the time widget.
+ *
+ * @param alignmentInfo
+ * alignment information
+ * @param blackListedView
+ * an optional black listed view that will not be used as
+ * reference (useful for a view that just got created)
+ * @return the reference view
+ */
+ private static ITmfTimeAligned getReferenceView(TmfTimeViewAlignmentInfo alignmentInfo, TmfView blackListedView) {
+ IWorkbenchWindow workbenchWindow = getWorkbenchWindow(alignmentInfo.getShell());
+ if (workbenchWindow == null || workbenchWindow.getActivePage() == null) {
+ // Only time aligned views that are part of a workbench window are supported
+ return null;
+ }
+ IWorkbenchPage page = workbenchWindow.getActivePage();
+
+ int lowestTimeAxisOffset = Integer.MAX_VALUE;
+ ITmfTimeAligned referenceView = null;
+ for (IViewReference ref : page.getViewReferences()) {
+ IViewPart view = ref.getView(false);
+ if (view != blackListedView && isTimeAlignedView(view)) {
+ if (isCandidateForReferenceView((TmfView) view, alignmentInfo, lowestTimeAxisOffset)) {
+ referenceView = (ITmfTimeAligned) view;
+ lowestTimeAxisOffset = getClampedTimeAxisOffset(referenceView.getTimeViewAlignmentInfo());
+ break;
+ }
+ }
+ }
+ return referenceView;
+ }
+
+ private static boolean isCandidateForReferenceView(TmfView tmfView, TmfTimeViewAlignmentInfo alignmentInfo, int lowestTimeAxisOffset) {
+ ITmfTimeAligned alignedView = (ITmfTimeAligned) tmfView;
+ TmfTimeViewAlignmentInfo timeViewAlignmentInfo = alignedView.getTimeViewAlignmentInfo();
+ if (timeViewAlignmentInfo == null) {
+ return false;
+ }
+
+ if (isDisposedView(tmfView)) {
+ return false;
+ }
+
+ Composite parentComposite = tmfView.getParentComposite();
+ boolean isVisible = parentComposite != null && parentComposite.isVisible();
+ if (isVisible) {
+ boolean isViewLocationNear = isViewLocationNear(alignmentInfo.getViewLocation(), getViewLocation(tmfView));
+ boolean isLowestTimeAxisOffset = getClampedTimeAxisOffset(timeViewAlignmentInfo) < lowestTimeAxisOffset;
+ if (isViewLocationNear && isLowestTimeAxisOffset) {
+ int availableWidth = alignedView.getAvailableWidth(getClampedTimeAxisOffset(timeViewAlignmentInfo));
+ availableWidth = getClampedTimeAxisWidth(timeViewAlignmentInfo, availableWidth);
+ if (availableWidth > 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the narrowest view that corresponds to the given alignment information.
+ */
+ private static TmfView getNarrowestView(TmfTimeViewAlignmentInfo alignmentInfo) {
+ IWorkbenchWindow workbenchWindow = getWorkbenchWindow(alignmentInfo.getShell());
+ if (workbenchWindow == null || workbenchWindow.getActivePage() == null) {
+ // Only time aligned views that are part of a workbench window are supported
+ return null;
+ }
+ IWorkbenchPage page = workbenchWindow.getActivePage();
+
+ int narrowestWidth = Integer.MAX_VALUE;
+ TmfView narrowestView = null;
+ for (IViewReference ref : page.getViewReferences()) {
+ IViewPart view = ref.getView(false);
+ if (isTimeAlignedView(view)) {
+ TmfView tmfView = (TmfView) view;
+ if (isCandidateForNarrowestView(tmfView, alignmentInfo, narrowestWidth)) {
+ narrowestWidth = ((ITmfTimeAligned) tmfView).getAvailableWidth(getClampedTimeAxisOffset(alignmentInfo));
+ narrowestWidth = getClampedTimeAxisWidth(alignmentInfo, narrowestWidth);
+ narrowestView = tmfView;
+ }
+ }
+ }
+
+ return narrowestView;
+ }
+
+ private static int getClampedTimeAxisWidth(TmfTimeViewAlignmentInfo alignmentInfo, int width) {
+ int max = getMaxInt(alignmentInfo.getShell());
+ if (validateInt(width, max)) {
+ Activator.getDefault().logError("Time-axis width out of range (" + width + ")", new Throwable()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ return Math.min(max, Math.max(0, width));
+ }
+
+ private static int getClampedTimeAxisOffset(TmfTimeViewAlignmentInfo alignmentInfo) {
+ int timeAxisOffset = alignmentInfo.getTimeAxisOffset();
+ int max = getMaxInt(alignmentInfo.getShell());
+ if (validateInt(timeAxisOffset, max)) {
+ Activator.getDefault().logError("Time-axis offset out of range (" + timeAxisOffset + ")", new Throwable()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ return Math.min(max, Math.max(0, timeAxisOffset));
+ }
+
+ private static boolean validateInt(int value, int max) {
+ return value < 0 || value > max;
+ }
+
+ private static int getMaxInt(Shell shell) {
+ // Consider an integer to be buggy if it's bigger than 10 times the
+ // width of *all* monitors combined.
+ final int DISPLAY_WIDTH_FACTOR = 10;
+ return shell.getDisplay().getBounds().width * DISPLAY_WIDTH_FACTOR;
+ }
+
+ private static boolean isCandidateForNarrowestView(TmfView tmfView, TmfTimeViewAlignmentInfo alignmentInfo, int narrowestWidth) {
+ ITmfTimeAligned alignedView = (ITmfTimeAligned) tmfView;
+ TmfTimeViewAlignmentInfo timeViewAlignmentInfo = alignedView.getTimeViewAlignmentInfo();
+ if (timeViewAlignmentInfo == null) {
+ return false;
+ }
+
+ if (isDisposedView(tmfView)) {
+ return false;
+ }
+
+ Composite parentComposite = tmfView.getParentComposite();
+ boolean isVisible = parentComposite != null && parentComposite.isVisible();
+ if (isVisible) {
+ if (isViewLocationNear(getViewLocation(tmfView), alignmentInfo.getViewLocation())) {
+ int availableWidth = alignedView.getAvailableWidth(getClampedTimeAxisOffset(alignmentInfo));
+ availableWidth = getClampedTimeAxisWidth(alignmentInfo, availableWidth);
+ boolean isNarrower = availableWidth < narrowestWidth && availableWidth > 0;
+ return isNarrower;
+ }
+ }
+
+ return false;
+ }
+
+ private static IWorkbenchWindow getWorkbenchWindow(Shell shell) {
+ for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+ if (window.getShell().equals(shell)) {
+ return window;
+ }
+ }
+
+ return null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * Marc-Andre Laperle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.signal;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * The responsibility of this class is to provide information necessary to
+ * decide whether or not views should be time-aligned with each other and at
+ * what offset.
+ *
+ * @see TmfTimeViewAlignmentSignal
+ *
+ * @since 1.0
+ */
+public class TmfTimeViewAlignmentInfo {
+ private final Point fViewLocation;
+ private final int fTimeAxisOffset;
+ private final Shell fShell;
+
+ /**
+ * Constructs a new TmfTimeViewAlignmentInfo.
+ *
+ * @param shell
+ * used to determine whether or not views should be aligned
+ * together
+ * @param viewLocation
+ * location of the view, used to determine whether or not views
+ * should be aligned together
+ * @param timeAxisOffset
+ * offset relative to the view. This offset will be communicated
+ * to the other views
+ */
+ public TmfTimeViewAlignmentInfo(Shell shell, Point viewLocation, int timeAxisOffset) {
+ fShell = shell;
+ fViewLocation = viewLocation;
+ fTimeAxisOffset = timeAxisOffset;
+ }
+
+ /**
+ * Get the shell containing this alignment.
+ *
+ * @return the shell
+ */
+ public Shell getShell() {
+ return fShell;
+ }
+
+ /**
+ * Get the absolute view location. This value is only valid at the time of
+ * the TmfTimeViewAlignmentInfo creation so extra care must be given in
+ * cases where the particular view might have been resized, moved, etc.
+ *
+ * @return the absolute view location
+ */
+ public Point getViewLocation() {
+ return fViewLocation;
+ }
+
+ /**
+ * Offset relative to the view corresponding to the start of the time axis.
+ *
+ * @return the offset in pixels
+ */
+ public int getTimeAxisOffset() {
+ return fTimeAxisOffset;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * Marc-Andre Laperle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.signal;
+
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
+
+/**
+ * A signal to inform about the state of time alignment. Typically, the emitter
+ * will inform the receivers about the position of a sash that separates the
+ * time axis on right side and extra information on the left side.
+ *
+ * @see TmfTimeViewAlignmentInfo
+ *
+ * @since 1.0
+ */
+public class TmfTimeViewAlignmentSignal extends TmfSignal {
+
+ private final TmfTimeViewAlignmentInfo fTimeViewAlignmentInfo;
+ private final boolean fIsSynchronous;
+
+ /**
+ * Creates a new TmfTimeViewAlignmentSignal
+ *
+ * @param source
+ * the source of the signal
+ * @param alignmentInfo
+ * information about the time alignment
+ */
+ public TmfTimeViewAlignmentSignal(Object source, TmfTimeViewAlignmentInfo alignmentInfo) {
+ this(source, alignmentInfo, false);
+ }
+
+ /**
+ * Creates a new TmfTimeViewAlignmentSignal
+ *
+ * @param source
+ * the source of the signal
+ * @param alignmentInfo
+ * information about the time alignment
+ * @param synchronous
+ * whether or not the signal should be processed right away. This
+ * is useful for signals that are sent not repetitively.
+ * For example, a sash being dragged would not be synchronous
+ * because the signal gets fired repeatedly. A view that has
+ * completed computing it's data could send a synchronous signal.
+ */
+ public TmfTimeViewAlignmentSignal(Object source, TmfTimeViewAlignmentInfo alignmentInfo, boolean synchronous) {
+ super(source);
+ fTimeViewAlignmentInfo = alignmentInfo;
+ fIsSynchronous = synchronous;
+ }
+
+ /**
+ * Get the time alignment information.
+ *
+ * @return the time alignment information
+ */
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ return fTimeViewAlignmentInfo;
+ }
+
+ @Override
+ public String toString() {
+ return "[TmfTimeViewAlignmentSignal (" + fTimeViewAlignmentInfo.toString() + ")]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Get whether or not the signal should be processed right away, without
+ * being throttled.
+ *
+ * @return whether or not the signal should be processed right away
+ */
+ public boolean IsSynchronous() {
+ return fIsSynchronous;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * Marc-Andre Laperle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.views;
+
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
+
+/**
+ * An interface that adds the time-axis alignment feature to a view. This
+ * interface provides information about the current state of alignment of the
+ * view as well as performs alignment operations in case the views become
+ * misaligned (resize, moved, etc).
+ *
+ * @since 1.0
+ */
+public interface ITmfTimeAligned {
+
+ /**
+ * Get the time alignment information. The view provides information about
+ * where the time-axis is in addition to information necessary to decide
+ * whether or not views should be time-aligned with each other (Shell,
+ * location).
+ *
+ * @return the time alignment information
+ */
+ TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo();
+
+ /**
+ * Get the available width for the specified time-axis offset. The
+ * implementation should return the width that would be available if the
+ * time-axis was to be at that offset. When about to perform a re-alignment,
+ * the alignment algorithm will choose the narrowest width to accommodate
+ * all views.
+ *
+ * @param requestedOffset
+ * the requested time-axis offset. Greater or equal to zero.
+ * @return the available width. Should be greater or equal to zero.
+ */
+ int getAvailableWidth(int requestedOffset);
+
+ /**
+ * Perform the alignment by moving the time-axis to the specified offset and
+ * the resizing it to the specified width. Implementations should handle
+ * cases were the requested width is greater than the width of the view. For
+ * example, Integer.MAX_VALUE can be requested in order to obtain the
+ * largest width possible.
+ *
+ * @param offset
+ * the time-axis offset. Greater or equal to zero.
+ * @param width
+ * the time-axis width. Greater or equal to zero. Can be
+ * Integer.MAX_VALUE.
+ */
+ void performAlign(int offset, int width);
+}
/*******************************************************************************
- * Copyright (c) 2009, 2014 Ericsson
+ * Copyright (c) 2009, 2015 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* Contributors:
* Francois Chouinard - Initial API and implementation
* Bernd Hufmann - Added possibility to pin view
+ * Marc-Andre Laperle - Support for view alignment
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.views;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.internal.tmf.ui.views.TimeAlignViewsAction;
+import org.eclipse.tracecompass.internal.tmf.ui.views.TmfAlignmentSynchronizer;
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.ui.IPartListener;
import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.ViewPart;
/**
public abstract class TmfView extends ViewPart implements ITmfComponent {
private final String fName;
+ /** This allows us to keep track of the view sizes */
+ private Composite fParentComposite;
+ private ControlAdapter fControlListener;
+ private static final TmfAlignmentSynchronizer TIME_ALIGNMENT_SYNCHRONIZER = new TmfAlignmentSynchronizer();
/**
* Action class for pinning of TmfView.
*/
protected PinTmfViewAction fPinAction;
+ private static TimeAlignViewsAction fAlignViewsAction;
// ------------------------------------------------------------------------
// Constructor
toolBarManager.add(fPinAction);
}
}
+
+ @Override
+ public void createPartControl(final Composite parent) {
+ fParentComposite = parent;
+ if (this instanceof ITmfTimeAligned) {
+ contributeAlignViewsActionToToolbar();
+
+ fControlListener = new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ TIME_ALIGNMENT_SYNCHRONIZER.handleViewResized(TmfView.this);
+ }
+ };
+ parent.addControlListener(fControlListener);
+
+ getSite().getPage().addPartListener(new IPartListener() {
+ @Override
+ public void partOpened(IWorkbenchPart part) {
+ // do nothing
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPart part) {
+ // do nothing
+ }
+
+ @Override
+ public void partClosed(IWorkbenchPart part) {
+ if (part == TmfView.this && fControlListener != null && !fParentComposite.isDisposed()) {
+ fParentComposite.removeControlListener(fControlListener);
+ fControlListener = null;
+ getSite().getPage().removePartListener(this);
+ TIME_ALIGNMENT_SYNCHRONIZER.handleViewClosed(TmfView.this);
+ }
+ }
+
+ @Override
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // do nothing
+ }
+
+ @Override
+ public void partActivated(IWorkbenchPart part) {
+ // do nothing
+ }
+ });
+ }
+ }
+
+ private void contributeAlignViewsActionToToolbar() {
+ if (fAlignViewsAction == null) {
+ fAlignViewsAction = new TimeAlignViewsAction();
+ }
+
+ IToolBarManager toolBarManager = getViewSite().getActionBars()
+ .getToolBarManager();
+ toolBarManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ toolBarManager.add(fAlignViewsAction);
+ }
+
+ /**
+ * Returns the parent control of the view
+ *
+ * @return the parent control
+ *
+ * @since 1.0
+ */
+ public Composite getParentComposite() {
+ return fParentComposite;
+ }
}
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
import org.eclipse.tracecompass.tmf.ui.views.TmfView;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphRangeListener;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener;
*
* @author Patrick Tasse
*/
-public class CallStackView extends TmfView {
+public class CallStackView extends TmfView implements ITmfTimeAligned {
// ------------------------------------------------------------------------
// Constants
@Override
public void createPartControl(Composite parent) {
+ super.createPartControl(parent);
fTimeGraphCombo = new TimeGraphCombo(parent, SWT.NONE);
fTimeGraphCombo.setTreeContentProvider(new TimeGraphContentProvider());
return ret;
}
+ /**
+ * @since 1.0
+ */
+ @Override
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ if (fTimeGraphCombo == null) {
+ return null;
+ }
+ return fTimeGraphCombo.getTimeViewAlignmentInfo();
+ }
+
+ /**
+ * @since 1.0
+ */
+ @Override
+ public int getAvailableWidth(int requestedOffset) {
+ if (fTimeGraphCombo == null) {
+ return 0;
+ }
+ return fTimeGraphCombo.getAvailableWidth(requestedOffset);
+ }
+
+ /**
+ * @since 1.0
+ */
+ @Override
+ public void performAlign(int offset, int width) {
+ fTimeGraphCombo.performAlign(offset, width);
+ }
}
*/
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$
+ private static final Image LINK_IMG = Activator.getDefault().getImageFromPath(ITmfImageConstants.IMG_UI_LINK);
// ------------------------------------------------------------------------
// Attributes
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.TmfWindowRangeUpdatedSignal;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+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.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
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.TmfTraceContext;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
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;
* @version 1.0
* @author Patrick Tasse
*/
-public class TimeChartView extends TmfView implements ITimeGraphRangeListener, ITimeGraphSelectionListener, ITimeGraphTimeListener, IColorSettingsListener, IResourceChangeListener {
+public class TimeChartView extends TmfView implements ITimeGraphRangeListener, ITimeGraphSelectionListener, ITimeGraphTimeListener, IColorSettingsListener, IResourceChangeListener, ITmfTimeAligned {
/** TimeChartView's ID */
public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.timechart"; //$NON-NLS-1$
@Override
public void createPartControl(Composite parent) {
+ super.createPartControl(parent);
fViewer = new TimeGraphViewer(parent, SWT.NONE);
fPresentationProvider = new TimeChartAnalysisProvider();
fViewer.setTimeGraphProvider(fPresentationProvider);
redecorate();
}
+ /**
+ * @since 1.0
+ */
+ @Override
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ if (fViewer == null) {
+ return null;
+ }
+ return fViewer.getTimeViewAlignmentInfo();
+ }
+
+ /**
+ * @since 1.0
+ */
+ @Override
+ public int getAvailableWidth(int requestedOffset) {
+ return fViewer.getAvailableWidth(requestedOffset);
+ }
+
+ /**
+ * @since 1.0
+ */
+ @Override
+ public void performAlign(int offset, int width) {
+ fViewer.performAlign(offset, width);
+ }
}
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.TmfWindowRangeUpdatedSignal;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
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.signal.TmfWindowRangeUpdatedSignal;
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.TmfTraceContext;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.ui.TmfUiRefreshHandler;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
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;
* 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.
*/
-public abstract class AbstractTimeGraphView extends TmfView {
+public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeAligned {
/** Constant indicating that all levels of the time graph should be expanded */
protected static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
void setAutoExpandLevel(int level);
+ void performAlign(int offset, int width);
+
+ TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo();
+
+ int getAvailableWidth(int requestedOffset);
}
private class TimeGraphViewerWrapper implements ITimeGraphWrapper {
public void setAutoExpandLevel(int level) {
viewer.setAutoExpandLevel(level);
}
+
+ @Override
+ public void performAlign(int offset, int width) {
+ viewer.performAlign(offset, width);
+ }
+
+ @Override
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ return viewer.getTimeViewAlignmentInfo();
+ }
+
+ @Override
+ public int getAvailableWidth(int requestedOffset) {
+ return viewer.getAvailableWidth(requestedOffset);
+ }
}
private class TimeGraphComboWrapper implements ITimeGraphWrapper {
IAction getShowFilterAction() {
return combo.getShowFilterAction();
}
+
+ @Override
+ public void performAlign(int offset, int width) {
+ combo.performAlign(offset, width);
+ }
+
+ @Override
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ return combo.getTimeViewAlignmentInfo();
+ }
+
+ @Override
+ public int getAvailableWidth(int requestedOffset) {
+ return combo.getAvailableWidth(requestedOffset);
+ }
}
/**
@Override
public void createPartControl(Composite parent) {
+ super.createPartControl(parent);
if (fColumns == null || fLabelProvider == null) {
fTimeGraphWrapper = new TimeGraphViewerWrapper(parent, SWT.NONE);
TimeGraphViewer viewer = fTimeGraphWrapper.getTimeGraphViewer();
manager.add(fTimeGraphWrapper.getTimeGraphViewer().getZoomOutAction());
manager.add(new Separator());
}
+
+ /**
+ * @since 1.0
+ */
+ @Override
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ if (fTimeGraphWrapper == null) {
+ return null;
+ }
+ return fTimeGraphWrapper.getTimeViewAlignmentInfo();
+ }
+
+ /**
+ * @since 1.0
+ */
+ @Override
+ public int getAvailableWidth(int requestedOffset) {
+ if (fTimeGraphWrapper == null) {
+ return 0;
+ }
+ return fTimeGraphWrapper.getAvailableWidth(requestedOffset);
+ }
+
+ /**
+ * @since 1.0
+ */
+ @Override
+ public void performAlign(int offset, int width) {
+ if (fTimeGraphWrapper != null) {
+ fTimeGraphWrapper.performAlign(offset, width);
+ }
+ }
}
/*******************************************************************************
- * Copyright (c) 2012, 2014 Ericsson, others
+ * Copyright (c) 2012, 2015 Ericsson, others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridLayout;
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.Sash;
import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
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.TmfSignalManager;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ITimeGraphEntryActiveProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.TimeGraphFilterDialog;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
/** List of all expanded items whose parents are also expanded */
private List<TreeItem> fVisibleExpandedItems = null;
+ private Listener fSashDragListener;
+ private SashForm fSashForm;
+
// ------------------------------------------------------------------------
// Classes
// ------------------------------------------------------------------------
* @param style
* the style of widget to construct
* @param weights
- * The relative weights of each side of the sash form
+ * The array (length 2) of relative weights of each side of the sash form
*/
public TimeGraphCombo(Composite parent, int style, int[] weights) {
super(parent, style);
setLayout(new FillLayout());
- final SashForm sash = new SashForm(this, SWT.NONE);
+ fSashForm = new SashForm(this, SWT.NONE);
- fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);
+ fTreeViewer = new TreeViewer(fSashForm, SWT.FULL_SELECTION | SWT.H_SCROLL);
fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
final Tree tree = fTreeViewer.getTree();
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
- fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);
+ fTimeGraphViewer = new TimeGraphViewer(fSashForm, SWT.NONE);
fTimeGraphViewer.setItemHeight(getItemHeight(tree));
fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());
// to a value that would cause blank space to be drawn at the bottom of the tree.
fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);
- sash.setWeights(weights);
+ fSashForm.setWeights(weights);
+
+ fTimeGraphViewer.getTimeGraphControl().addPaintListener(new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ // Sashes in a SashForm are being created on layout so add the
+ // drag listener here
+ if (fSashDragListener == null) {
+ for (Control control : fSashForm.getChildren()) {
+ if (control instanceof Sash) {
+ fSashDragListener = new Listener() {
+
+ @Override
+ public void handleEvent(Event event) {
+ sendTimeViewAlignmentChanged();
+
+ }
+ };
+ control.removePaintListener(this);
+ control.addListener(SWT.Selection, fSashDragListener);
+ // There should be only one sash
+ break;
+ }
+ }
+ }
+ }
+ });
+ }
+
+ private void sendTimeViewAlignmentChanged() {
+ TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(fSashForm, getTimeViewAlignmentInfo()));
}
// ------------------------------------------------------------------------
}
}
+ /**
+ * Return the time alignment information
+ *
+ * @return the time alignment information
+ *
+ * @see ITmfTimeAligned
+ *
+ * @since 1.0
+ */
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ int[] weights = fSashForm.getWeights();
+ int leftWidth = (int) (((float) weights[0] / (weights[0] + weights[1])) * fSashForm.getBounds().width) + fSashForm.getSashWidth();
+ Point location = fSashForm.toDisplay(0, 0);
+ return new TmfTimeViewAlignmentInfo(fSashForm.getShell(), location, leftWidth);
+ }
+
+ /**
+ * Return the available width for the time-axis.
+ *
+ * @see ITmfTimeAligned
+ *
+ * @param requestedOffset
+ * the requested offset
+ * @return the available width for the time-axis
+ *
+ * @since 1.0
+ */
+ public int getAvailableWidth(int requestedOffset) {
+ int totalWidth = fSashForm.getBounds().width;
+ int timeWidth = totalWidth - requestedOffset;
+ return timeWidth;
+ }
+
+ /**
+ * Perform the alignment operation.
+ *
+ * @param offset
+ * the alignment offset
+ * @param width
+ * the alignment width
+ *
+ * @see ITmfTimeAligned
+ *
+ * @since 1.0
+ */
+ public void performAlign(int offset, int width) {
+ int total = fSashForm.getBounds().width;
+ int timeAxisOffset = Math.min(offset, total);
+ int sash1Width = (int) (timeAxisOffset / (float) total * 1000);
+ int sash2Width = (int) ((total - timeAxisOffset) / (float) total * 1000);
+ fSashForm.setWeights(new int[] { sash1Width, sash2Width });
+ fSashForm.layout();
+
+ Composite composite = fTimeGraphViewer.getTimeAlignedComposite();
+ GridLayout layout = (GridLayout) composite.getLayout();
+ int timeBasedControlsWidth = composite.getSize().x;
+ int marginSize = timeBasedControlsWidth - width;
+ layout.marginRight = Math.max(0, marginSize);
+ composite.layout();
+ }
}
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.ui.signal.TmfTimeViewAlignmentInfo;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.TimeGraphLegend;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
private ListenerNotifier fListenerNotifier;
private final Object fListenerNotifierLock = new Object();
+ private Composite fTimeAlignedComposite;
+
private class ListenerNotifier extends Thread {
private static final long DELAY = 400L;
private static final long POLLING_INTERVAL = 10L;
gl.horizontalSpacing = 0;
fDataViewer.setLayout(gl);
- fTimeScaleCtrl = new TimeGraphScale(fDataViewer, fColorScheme);
+ fTimeAlignedComposite = new Composite(fDataViewer, style) {
+ @Override
+ public void redraw() {
+ fDataViewer.redraw();
+ super.redraw();
+ }
+ };
+ GridLayout gl2 = new GridLayout(1, false);
+ gl2.marginHeight = fBorderWidth;
+ gl2.marginWidth = 0;
+ gl2.verticalSpacing = 0;
+ gl2.horizontalSpacing = 0;
+ fTimeAlignedComposite.setLayout(gl2);
+ fTimeAlignedComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ fTimeScaleCtrl = new TimeGraphScale(fTimeAlignedComposite, fColorScheme);
fTimeScaleCtrl.setTimeProvider(fTimeDataProvider);
fTimeScaleCtrl.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false));
fTimeScaleCtrl.setHeight(fTimeScaleHeight);
}
});
- fVerticalScrollBar = new Slider(fDataViewer, SWT.VERTICAL | SWT.NO_FOCUS);
- fVerticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 2));
- fVerticalScrollBar.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- setTopIndex(fVerticalScrollBar.getSelection());
- }
- });
-
- fTimeGraphCtrl = createTimeGraphControl(fDataViewer, fColorScheme);
+ fTimeGraphCtrl = createTimeGraphControl(fTimeAlignedComposite, fColorScheme);
fTimeGraphCtrl.setTimeProvider(this);
fTimeGraphCtrl.setTimeGraphScale(fTimeScaleCtrl);
}
});
+ fVerticalScrollBar = new Slider(fDataViewer, SWT.VERTICAL | SWT.NO_FOCUS);
+ fVerticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 1));
+ fVerticalScrollBar.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setTopIndex(fVerticalScrollBar.getSelection());
+ }
+ });
+
fHorizontalScrollBar = new Slider(fDataViewer, SWT.HORIZONTAL | SWT.NO_FOCUS);
fHorizontalScrollBar.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false));
fHorizontalScrollBar.addListener(SWT.MouseWheel, new Listener() {
return fTimeScaleCtrl;
}
+ /**
+ * Returns the composite containing all the controls that are time aligned,
+ * i.e. TimeGraphScale, TimeGraphControl.
+ *
+ * @return the time based composite
+ * @since 1.0
+ */
+ public Composite getTimeAlignedComposite() {
+ return fTimeAlignedComposite;
+ }
+
/**
* Return the x coordinate corresponding to a time
*
refresh();
}
+ /**
+ * Return the time alignment information
+ *
+ * @return the time alignment information
+ *
+ * @see ITmfTimeAligned
+ *
+ * @since 1.0
+ */
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ return fTimeGraphCtrl.getTimeViewAlignmentInfo();
+ }
+
+ /**
+ * Return the available width for the time-axis.
+ *
+ * @see ITmfTimeAligned
+ *
+ * @param requestedOffset
+ * the requested offset
+ * @return the available width for the time-axis
+ *
+ * @since 1.0
+ */
+ public int getAvailableWidth(int requestedOffset) {
+ return fTimeAlignedComposite.getSize().x - requestedOffset;
+ }
+
+ /**
+ * Perform the alignment operation.
+ *
+ * @param offset
+ * the alignment offset
+ * @param width
+ * the alignment width
+ *
+ * @see ITmfTimeAligned
+ *
+ * @since 1.0
+ */
+ public void performAlign(int offset, int width) {
+ fTimeGraphCtrl.performAlign(offset);
+ int alignmentWidth = width;
+ int size = fTimeAlignedComposite.getSize().x;
+ GridLayout layout = (GridLayout) fTimeAlignedComposite.getLayout();
+ int marginSize = size - alignmentWidth - offset;
+ layout.marginRight = Math.max(0, marginSize);
+ fTimeAlignedComposite.layout();
+ }
+
}
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
+import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
+import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphColorListener;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2;
} else if (DRAG_SPLIT_LINE == fDragState) {
fDragX = e.x;
fTimeProvider.setNameSpace(e.x);
+ TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(this, getTimeViewAlignmentInfo()));
} else if (DRAG_SELECTION == fDragState) {
fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN);
redraw();
redraw();
}
fMouseOverSplitLine = mouseOverSplitLine;
+ TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(this, getTimeViewAlignmentInfo()));
return;
}
int idx = getItemIndexAtY(e.y);
}
}
+ /**
+ * Perform the alignment operation.
+ *
+ * @param offset
+ * the alignment offset
+ *
+ * @see ITmfTimeAligned
+ *
+ * @since 1.0
+ */
+ public void performAlign(int offset) {
+ fTimeProvider.setNameSpace(offset);
+ }
+
+ /**
+ * Return the time alignment information
+ *
+ * @return the time alignment information
+ *
+ * @see ITmfTimeAligned
+ *
+ * @since 1.0
+ */
+ public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
+ return new TmfTimeViewAlignmentInfo(getShell(), toDisplay(0, 0), fTimeProvider.getNameSpace());
+ }
}