/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* 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:
* Del Myers -- initial API and implementation
*******************************************************************************/
package org.eclipse.zest.custom.sequence.widgets;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.FreeformViewport;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.zest.custom.sequence.events.SequenceEvent;
import org.eclipse.zest.custom.sequence.events.SequenceListener;
import org.eclipse.zest.custom.sequence.events.internal.ListenerList;
import org.eclipse.zest.custom.sequence.figures.internal.SuspendableDeferredUpdateManager;
import org.eclipse.zest.custom.sequence.figures.internal.SuspendableLightweightSystem;
import org.eclipse.zest.custom.sequence.internal.AbstractSimpleProgressRunnable;
import org.eclipse.zest.custom.sequence.internal.IUIProgressService;
import org.eclipse.zest.custom.sequence.internal.SimpleProgressMonitor;
import org.eclipse.zest.custom.sequence.internal.UIJobProcessor;
import org.eclipse.zest.custom.sequence.tools.IWidgetTool;
import org.eclipse.zest.custom.sequence.tools.ToolEventDispatcher;
import org.eclipse.zest.custom.sequence.visuals.MessageBasedSequenceVisuals;
import org.eclipse.zest.custom.sequence.widgets.internal.CustomSashForm;
import org.eclipse.zest.custom.sequence.widgets.internal.IWidgetProperties;
import org.eclipse.zest.custom.sequence.widgets.internal.SimpleProgressComposite;
import org.eclipse.zest.custom.sequence.widgets.internal.ThrownErrorDialog;
/**
* Special uml chart for sequence diagrams.
* @author Del Myers
*/
public class UMLSequenceChart extends UMLChart {
private class SequenceChartProgressService implements IUIProgressService, ControlListener {
/**
* Composite used to display progress services.
*/
private SimpleProgressComposite progressComposite;
private Button cancelButton;
private SimpleProgressMonitor monitor;
private Composite parent;
private Control oldTop;
/**
* Used to gauge when to open up the actual progress composite. It will open
* after 3 seconds.
*/
private final int DELAY = 3000;
//private AbstractSimpleProgressRunnable runnable;
Timer openTimer = null;
public void runInUIThread(AbstractSimpleProgressRunnable runnable,
final boolean enableCancelButton) throws InvocationTargetException {
if (this.monitor != null) {
//if this happens, there has been an error in the scheduling.
throw new InvocationTargetException(new IllegalStateException("Cannot run concurrent ui jobs"));
}
synchronized (this) {
if (openTimer == null) {
openTimer = new Timer();
openTimer.schedule(new TimerTask(){
public void run() {
getDisplay().asyncExec(new Runnable(){
public void run() {
open(enableCancelButton);
}
}
);
}
}, DELAY);
}
}
this.monitor = new SimpleProgressMonitor(this, enableCancelButton) {
@Override
public void cancel() {
if (enableCancelButton) {
super.cancel();
close();
}
}
};
try {
if (isRedrawing()) {
((SuspendableDeferredUpdateManager)sequenceCanvas.getLightweightSystem().getUpdateManager()).suspend();
}
runnable.runInUIThread(monitor);
} finally {
if (isRedrawing()) {
((SuspendableDeferredUpdateManager)sequenceCanvas.getLightweightSystem().getUpdateManager()).resume();
}
close();
this.monitor = null;
}
}
public void setSubTask(String taskName) {
if (progressComposite != null && !progressComposite.isDisposed()) {
progressComposite.setSubTask(taskName);
}
}
public void setTask(String taskName, int totalWork) {
if (progressComposite != null && !progressComposite.isDisposed()) {
progressComposite.setTask(taskName, totalWork);
}
}
public void setTaskName(String taskName) {
if (progressComposite != null && !progressComposite.isDisposed()) {
progressComposite.setSubTask(taskName);
}
}
public void setWorked(int work) {
// if (progressComposite == null || progressComposite.isDisposed()) {
// long currentTime = System.currentTimeMillis();
// if (currentTime-startTime >= DELAY) {
// open(enableCancelButton);
// }
// }
if (progressComposite != null && !progressComposite.isDisposed()) {
progressComposite.setWorked(work);
if (work == WORKED_DONE) {
close();
}
}
}
/**
* Creates the composite. It is expected that the parent will be layed out
* using a grid layout.
* @param parent
*/
protected SequenceChartProgressService(Composite parent) {
this.parent = parent;
if (!(parent.getLayout() instanceof StackLayout)) {
throw new IllegalArgumentException("Expected a stack layout");
}
this.oldTop = ((StackLayout)parent.getLayout()).topControl;
progressComposite = new SimpleProgressComposite(parent);
cancelButton = new Button(progressComposite, SWT.PUSH);
cancelButton.setText("Stop");
GridData gd = new GridData(GridData.END, GridData.FILL, false, false);
cancelButton.setLayoutData(gd);
cancelButton.addSelectionListener(new SelectionListener(){
public void widgetDefaultSelected(SelectionEvent e) {
if (monitor != null) {
monitor.cancel();
}
}
public void widgetSelected(SelectionEvent e) {
if (monitor != null) {
monitor.cancel();
}
}
});
gd = new GridData(SWT.FILL, SWT.FILL, true, false);
int width = progressComposite.getParent().getSize().x;
gd.minimumWidth = width;
gd.widthHint = width;
progressComposite.setLayoutData(gd);
}
private synchronized void open(boolean showCancelButton) {
StackLayout parentLayout = (StackLayout) parent.getLayout();
this.oldTop = parentLayout.topControl;
if (monitor == null) {
return;
}
progressComposite.setVisible(true);
cancelButton.setVisible(showCancelButton);
parentLayout.topControl = progressComposite;
parent.layout();
}
private synchronized void close() {
if (openTimer != null) {
openTimer.cancel();
openTimer = null;
}
this.monitor = null;
if (progressComposite != null && !progressComposite.isDisposed()) {
StackLayout parentLayout = (StackLayout) parent.getLayout();
if (oldTop != null) {
parentLayout.topControl = oldTop;
}
parent.layout();
}
}
public void controlMoved(ControlEvent e) {
if (progressComposite != null && !progressComposite.isDisposed()) {
UMLSequenceChart chart = UMLSequenceChart.this;
Point location = chart.getLocation();
location = chart.toControl(chart.getParent().toDisplay(location));
Point size = progressComposite.getSize();
location.y = location.y + chart.getSize().y - size.y;
progressComposite.setLocation(location);
}
}
public void controlResized(ControlEvent e) {
if (progressComposite != null && !progressComposite.isDisposed()) {
UMLSequenceChart chart = UMLSequenceChart.this;
int height = chart.getSize().y/4;
int preferredHeight = progressComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
if (preferredHeight != SWT.DEFAULT && preferredHeight < height) {
height = preferredHeight;
}
progressComposite.setSize(chart.getSize().x, height);
Point location = chart.getLocation();
location = chart.toControl(chart.getParent().toDisplay(location));
location.y = location.y + chart.getSize().y - height;
progressComposite.setLocation(location);
}
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.internal.IUIProgressService#handleException(java.lang.Throwable)
*/
public void handleException(Throwable t) {
if (!isDisposed() && !getShell().isDisposed()) {
new ThrownErrorDialog(getShell()).open(t);
} else {
t.printStackTrace();
}
}
}
private class SequenceNavigator implements KeyListener, SelectionListener {
/**
* Needed to decide where to go next in the case of up/down
* on messages.
*/
Lifeline lastLifeline;
/* (non-Javadoc)
* @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
*/
public void keyPressed(KeyEvent e) {
if (e.stateMask != 0) return;
int code = e.keyCode;
UMLItem[] selected = getSelection();
UMLItem selectedItem = null;
if (selected.length > 0) {
selectedItem = selected[0];
}
if (selectedItem == null) {
return;
}
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
switch (code) {
case SWT.ARROW_LEFT:
if (selectedItem instanceof Lifeline) {
Lifeline line = (Lifeline) selectedItem;
Lifeline[] visibleLines = getVisibleLifelines();
int i = 0;
for (; i < visibleLines.length; i++) {
if (visibleLines[i] == line) {
break;
}
}
if (i-1 >= 0) {
internalSetSelection(new UMLItem[]{visibleLines[i-1]});
navigate(visibleLines[i-1]);
}
} else if (selectedItem instanceof Activation) {
Activation a = (Activation) selectedItem;
if (a.isExpanded()) {
a.setExpanded(false);
}
} else if (selectedItem instanceof Message) {
Message m = (Message) selectedItem;
Activation source = m.getSource();
Activation target = m.getTarget();
if (source != null && target != null) {
Rectangle sourceBounds = getItemBounds(source);
Rectangle targetBounds = getItemBounds(target);
if (sourceBounds!=null && targetBounds != null) {
if (sourceBounds.x >= targetBounds.x) {
//move to the target.
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = target.getVisibleLifeline();
show(target);
} else {
//move to the source.
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = source.getVisibleLifeline();
show(source);
}
}
}
}
break;
case SWT.ARROW_RIGHT:
if (selectedItem instanceof Lifeline) {
Lifeline line = (Lifeline) selectedItem;
Lifeline[] visibleLines = getVisibleLifelines();
int i = 0;
for (; i < visibleLines.length; i++) {
if (visibleLines[i] == line) {
break;
}
}
if (i+1 < visibleLines.length) {
internalSetSelection(new UMLItem[]{visibleLines[i+1]});
navigate(visibleLines[i+1]);
}
} else if (selectedItem instanceof Activation) {
Activation a = (Activation) selectedItem;
if (!a.isExpanded()) {
a.setExpanded(true);
}
} else if (selectedItem instanceof Message) {
Message m = (Message) selectedItem;
Activation source = m.getSource();
Activation target = m.getTarget();
if (source != null && target != null) {
Rectangle sourceBounds = getItemBounds(source);
Rectangle targetBounds = getItemBounds(target);
if (sourceBounds!=null && targetBounds != null) {
if (sourceBounds.x < targetBounds.x) {
//move to the target.
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = target.getVisibleLifeline();
//internalSetSelection(new UMLItem[]{target});
show(target);
} else {
//move to the source.
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = source.getVisibleLifeline();
show(source);
}
}
}
}
break;
case SWT.ARROW_UP:
if (selectedItem instanceof Lifeline) {
Lifeline line = (Lifeline) selectedItem;
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = line;
} else if ((selectedItem instanceof Activation) || (selectedItem instanceof Message)) {
Lifeline l;
if (selectedItem instanceof Activation) {
l = ((Activation)selectedItem).getVisibleLifeline();
} else {
if (lastLifeline != null) {
l = lastLifeline;
} else {
l = ((Message)selectedItem).getSource().getVisibleLifeline();
}
}
List<UMLItem> ordered = getVisibleOrderedItems(l);
int index = ordered.indexOf(selectedItem);
if (index <= 0) {
internalSetSelection(new UMLItem[]{l});
} else {
index--;
if (index < ordered.size()) {
UMLItem newItem = ordered.get(index);
internalSetSelection(new UMLItem[]{newItem});
navigate(ordered.get(index));
}
}
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = l;
}
break;
case SWT.ARROW_DOWN:
if (selectedItem instanceof Lifeline) {
Lifeline line = (Lifeline) selectedItem;
Activation[] as = line.getOrderedActivations();
if (as.length > 0) {
UMLItem reveal = as[0];
if (as[0].getSourceCall() != null && as[0].getSourceCall().isVisible()) {
reveal = as[0].getSourceCall();
}
internalSetSelection(new UMLItem[]{reveal});
navigate(reveal);
}
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = line;
} else if ((selectedItem instanceof Activation) || (selectedItem instanceof Message)) {
Lifeline l;
if (selectedItem instanceof Activation) {
l = ((Activation)selectedItem).getVisibleLifeline();
} else {
if (lastLifeline != null) {
l = lastLifeline;
} else {
l = ((Message)selectedItem).getSource().getVisibleLifeline();
}
}
List<UMLItem> ordered = getVisibleOrderedItems(l);
int index = ordered.indexOf(selectedItem);
index++;
if (index < ordered.size()) {
internalSetSelection(new UMLItem[]{ordered.get(index)});
navigate(ordered.get(index));
}
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
lastLifeline = l;
}
break;
}
if (lastLifeline != null) {
lastLifeline.setHighlight(true);
}
}
/**
* Scrolls x position of the window to show the given activation.
* @param target
*/
private void show(Activation item) {
Rectangle itemBounds = getItemBounds(item);
if (itemBounds != null) {
Rectangle clientBounds = getBounds();
setScrollX((itemBounds.x + itemBounds.width/2) - (clientBounds.width/2));
}
}
private List<UMLItem> getVisibleOrderedItems(Lifeline l) {
TreeSet<UMLItem> items = new TreeSet<UMLItem>(new Comparator<UMLItem>(){
public int compare(UMLItem o1, UMLItem o2) {
if (o1 == o2) return 0;
Rectangle r1 = getItemBounds(o1);
Rectangle r2 = getItemBounds(o2);
if (r1 == null) {
if (r2 == null) {
return 0;
}
return -1;
}
if (r2 == null) {
return 1;
}
int diff = r1.y-r2.y;
if (diff == 0) {
if (o1 instanceof Activation) {
diff=1;
} else {
diff = -1;
}
}
return diff;
}});
List<Activation> activations = Arrays.asList(l.getOrderedActivations());
items.add(l);
items.addAll(activations);
for (Activation a : activations) {
items.addAll(a.getVisibleOrderedMessages());
}
List<UMLItem> list = new ArrayList<UMLItem>(items);
return list;
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent e) {
UMLItem[] selection = getSelection();
if (selection.length > 0) {
if (lastLifeline != null) {
lastLifeline.setHighlight(false);
}
UMLItem newItem = selection[0];
if (newItem instanceof Lifeline) {
lastLifeline = (Lifeline) newItem;
} else if (newItem instanceof Message) {
lastLifeline = ((Message)newItem).getSource().getVisibleLifeline();
} else if (newItem instanceof Activation) {
lastLifeline = ((Activation)newItem).getVisibleLifeline();
} else {
lastLifeline = null;
}
if (lastLifeline != null) {
lastLifeline.setHighlight(true);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
*/
public void keyReleased(KeyEvent e) {}
}
private class SashPainter implements Listener {
boolean armed = false;
private CustomSashForm form;
public SashPainter(CustomSashForm form) {
this.form = form;
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
public void handleEvent(Event event) {
switch (event.type) {
case SWT.MouseEnter:
case SWT.MouseDown:
armed = true;
((Control)event.widget).redraw();
break;
case SWT.MouseUp:
if (armed) {
if (event.widget instanceof Sash) {
Control c = form.findControl((Sash) event.widget);
if (c != null) {
int weight = form.getWeight(c);
if (weight != 0) {
form.collapseControl(c);
} else {
form.extendControl(c);
}
}
}
}
armed = false;
break;
case SWT.DragDetect:
case SWT.MouseExit:
((Control)event.widget).redraw();
armed = false;
break;
case SWT.Move:
case SWT.Resize:
((Control)event.widget).redraw();
break;
case SWT.Paint:
paint(event);
break;
}
}
/**
* @param gc
*/
private void paint(Event e) {
GC gc = e.gc;
if (!(e.widget instanceof Sash)){
return;
}
Sash c = (Sash) e.widget;
Rectangle bounds = c.getBounds();
int ar = 1;
if (bounds.height != 0) {
ar = bounds.width/bounds.height;
}
if (armed) {
gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
} else {
gc.setBackground(getBackground());
}
gc.fillRectangle(e.x, e.y, e.width, e.height);
gc.setForeground(c.getDisplay().getSystemColor(SWT.COLOR_BLACK));
if (ar > 0) {
int centerX = e.width/2;
int top = 0;//getBounds().y;
int bottom = top+e.height-1;
if (((CustomSashForm)contents).getWeights()[0] > 0) {
//draw the arrows going up
gc.setLineWidth(2);
gc.drawLine(centerX-8, bottom, centerX-4,top);
gc.drawLine(centerX-4, top, centerX,bottom);
gc.drawLine(centerX, bottom, centerX+4,top);
gc.drawLine(centerX+4, top, centerX+8,bottom);
} else {
//draw the arrows going down
gc.setLineWidth(2);
gc.drawLine(centerX-8, top, centerX-4,bottom);
gc.drawLine(centerX-4, bottom, centerX,top);
gc.drawLine(centerX, top, centerX+4,bottom);
gc.drawLine(centerX+4, bottom, centerX+8,top);
}
} else {
int centerY = e.height/2;
int left = 0;//getBounds().y;
int right = left+e.width-1;
if (((CustomSashForm)contents).getWeights()[0] > 0) {
//draw the arrows going up
gc.setLineWidth(2);
gc.drawLine(left, centerY-8, right, centerY-4);
gc.drawLine(right, centerY-4, left, centerY);
gc.drawLine(left, centerY, right, centerY+4);
gc.drawLine(right, centerY+4, left, centerY+8);
} else {
//draw the arrows going down
gc.setLineWidth(2);
gc.setLineWidth(2);
gc.drawLine(right, centerY-8, left, centerY-4);
gc.drawLine(left, centerY-4, right, centerY);
gc.drawLine(right, centerY, left, centerY+4);
gc.drawLine(left, centerY+4, right, centerY+8);
}
}
}
};
/**
* Property indicating that the root of this chart has changed.
*/
public static final String ROOT_PROP = "root";
/**
* Property indicating that the timing on this chart has changed. The old value
* of the property change indication will always be <code>Boolean.FALSE</code>
* the new value will always be <code>Boolean.TRUE</code>
*/
public static final String TIMING_PROP = "time";
private Activation rootActivation;
private ListenerList sequenceListeners;
private PropertyChangeListener expandListener;
private MessageBasedSequenceVisuals widgetVisuals;
private int deferRedrawCount;
private UIJobProcessor jobProcessor;
private SequenceChartProgressService progressService;
/**
* The control that is used to draw the sequence diagram
*/
private FigureCanvas sequenceCanvas;
/**
* The control that is used to draw the objects.
*/
private FigureCanvas objectCanvas;
/**
* The control that contains both the sequence canvas and the object canvas.
*/
private CustomSashForm contents;
private ToolEventDispatcher[] dispatchers;
private SequenceChartEventFilter eventFilter;
private FigureCanvas groupingCanvas;
private CustomSashForm sequenceSash;
private SequenceClone clone;
private Composite sequenceContainer;
private class ExpandPropogator implements PropertyChangeListener {
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.widgets.PropertyChangeListener#propertyChanged(java.lang.Object, java.lang.String, java.lang.Object, java.lang.Object)
*/
public void propertyChanged(Object source, String property, Object oldValue, Object newValue) {
if (IWidgetProperties.EXPANDED.equals(property)) {
fireExpandChanged((IExpandableItem)source, (Boolean)newValue);
}
}
}
/**
* Creates a new UMLSequenceChart on the given parent with the given style. Note that UMLSequenceCharts,
* as with all charts, are complex composites. A second Composite is created for the actual viewing of
* the chart. Therefore, it is best to not set a layout on the chart itself. Instead, if you would like
* to have windowed children on the chart, it is best to place them on the control recieved by
* getControl().
*
* @param parent
* @param style
*/
public UMLSequenceChart(Composite parent, int style) {
super(parent, style);
setBackground(parent.getBackground());
//hide scrollbars
if (getHorizontalBar() != null) {
super.getHorizontalBar().setVisible(false);
}
if (getVerticalBar() != null) {
super.getVerticalBar().setVisible(false);
}
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.widgets.UMLChart#createContents(org.eclipse.swt.widgets.Composite, int)
*/
@Override
protected Composite createContents(Composite parent, int style) {
rootActivation = null;
this.deferRedrawCount = 0;
this.sequenceListeners = new ListenerList();
this.expandListener = new ExpandPropogator();
createFigureContents(parent, style);
return contents;
}
/**
* @param parent
* @param style
* TODO: All of this stuff should be in the MessageBasedSequenceVisuals,
* just to support better encapsulation.
*/
public void createFigureContents(Composite parent, int style) {
contents = new CustomSashForm(parent, SWT.FLAT);
((CustomSashForm)contents).SASH_WIDTH = 5;
contents.setBackground(parent.getBackground());
this.widgetVisuals = new MessageBasedSequenceVisuals(this);
this.dispatchers = new ToolEventDispatcher[] {
new ToolEventDispatcher(widgetVisuals, this),
new ToolEventDispatcher(widgetVisuals, this),
new ToolEventDispatcher(widgetVisuals, this)
};
//create the container pane
LightweightSystem system = new SuspendableLightweightSystem();
system.setEventDispatcher(dispatchers[0]);
groupingCanvas = new FigureCanvas(contents, SWT.NONE, system){
/* (non-Javadoc)
* @see org.eclipse.draw2d.FigureCanvas#computeSize(int, int, boolean)
*/
@Override
public Point computeSize(int hint, int hint2, boolean changed) {
Point size = super.computeSize(hint, hint2, changed);
if (!isVisible()) {
size.y = 0;
}
return size;
}
};
groupingCanvas.setBackground(parent.getBackground());
//groupingCanvas.setViewport(new FreeformViewport());
groupingCanvas.setScrollBarVisibility(FigureCanvas.NEVER);
SashPainter painter = new SashPainter(contents);
contents.addSashListener(SWT.Move, groupingCanvas, painter);
contents.addSashListener(SWT.MouseDown, groupingCanvas, painter);
contents.addSashListener(SWT.MouseUp, groupingCanvas, painter);
contents.addSashListener(SWT.MouseEnter, groupingCanvas, painter);
contents.addSashListener(SWT.MouseExit, groupingCanvas, painter);
contents.addSashListener(SWT.DragDetect, groupingCanvas, painter);
contents.addSashListener(SWT.Resize, groupingCanvas, painter);
contents.addSashListener(SWT.Paint, groupingCanvas, painter);
contents.setOrientation(SWT.VERTICAL);
sequenceSash = new CustomSashForm(contents, SWT.NONE);
sequenceSash.SASH_WIDTH = 5;
Composite cloneContainer = new Composite(sequenceSash, (style | SWT.H_SCROLL | SWT.V_SCROLL)^( SWT.H_SCROLL | SWT.V_SCROLL));
GridLayout layout = new GridLayout();
layout.numColumns = 1;
layout.makeColumnsEqualWidth = true;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.marginBottom = 0;
layout.marginHeight = 0;
layout.marginLeft = 0;
layout.marginRight = 0;
layout.marginTop = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
cloneContainer.setLayout(layout);
sequenceContainer = new Composite(sequenceSash, (style | SWT.H_SCROLL | SWT.V_SCROLL)^( SWT.H_SCROLL | SWT.V_SCROLL));
layout = new GridLayout();
layout.numColumns = 1;
layout.makeColumnsEqualWidth = true;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.marginBottom = 0;
layout.marginHeight = 0;
layout.marginLeft = 0;
layout.marginRight = 0;
layout.marginTop = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
sequenceContainer.setLayout(layout);
Composite objectAndProgressContainer = new Composite(sequenceContainer, SWT.NONE);
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
gd.minimumHeight = MessageBasedSequenceVisuals.OBJECT_HEIGHT;
gd.heightHint = gd.minimumHeight;
objectAndProgressContainer.setLayoutData(gd);
objectAndProgressContainer.setLayout(new StackLayout());
setData(MessageBasedSequenceVisuals.VISUAL_KEY, widgetVisuals);
setActiveTool(new SelectionTool());
system = new SuspendableLightweightSystem();
system.setEventDispatcher(dispatchers[1]);
this.objectCanvas = new FigureCanvas(objectAndProgressContainer, system);
//objectCanvas.setViewport(new FreeformViewport());
objectCanvas.setLayout(new FillLayout());
objectCanvas.setScrollBarVisibility(FigureCanvas.NEVER);
system = new SuspendableLightweightSystem();
system.setUpdateManager(new SuspendableDeferredUpdateManager());
system.setEventDispatcher(dispatchers[2]);
this.sequenceCanvas = new FigureCanvas(sequenceContainer, SWT.H_SCROLL | SWT.V_SCROLL, system);
sequenceCanvas.setViewport(new FreeformViewport());
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
sequenceCanvas.setLayoutData(layoutData);
if ((style & SWT.H_SCROLL) != 0) {
sequenceCanvas.setHorizontalScrollBarVisibility(FigureCanvas.AUTOMATIC);
} else {
sequenceCanvas.setHorizontalScrollBarVisibility(FigureCanvas.NEVER);
}
if ((style & SWT.V_SCROLL) != 0) {
sequenceCanvas.setVerticalScrollBarVisibility(FigureCanvas.AUTOMATIC);
} else {
sequenceCanvas.setVerticalScrollBarVisibility(FigureCanvas.NEVER);
}
sequenceCanvas.getViewport().setContentsTracksHeight(true);
sequenceCanvas.getViewport().setContentsTracksWidth(true);
objectCanvas.getViewport().setContentsTracksHeight(true);
objectCanvas.getViewport().setContentsTracksWidth(true);
groupingCanvas.getViewport().setContentsTracksHeight(true);
groupingCanvas.getViewport().setContentsTracksWidth(true);
widgetVisuals.createFigures();
SequenceNavigator navigator = new SequenceNavigator();
addKeyListener(navigator);
addSelectionListener(navigator);
this.progressService = new SequenceChartProgressService(objectAndProgressContainer);
setData("progress", progressService);
this.jobProcessor = new UIJobProcessor(progressService);
setData("jobs", jobProcessor);
((StackLayout)objectAndProgressContainer.getLayout()).topControl = objectCanvas;
this.eventFilter = new SequenceChartEventFilter(this);
eventFilter.hookToDisplay();
//add the clone
clone = new SequenceClone(cloneContainer, this);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
clone.getControl().setLayoutData(data);
clone.getControl().setBackground(getBackground());
painter = new SashPainter(sequenceSash);
sequenceSash.addSashListener(SWT.Move, cloneContainer, painter);
sequenceSash.addSashListener(SWT.MouseDown, cloneContainer, painter);
sequenceSash.addSashListener(SWT.MouseUp, cloneContainer, painter);
sequenceSash.addSashListener(SWT.MouseEnter, cloneContainer, painter);
sequenceSash.addSashListener(SWT.MouseExit, cloneContainer, painter);
sequenceSash.addSashListener(SWT.DragDetect, cloneContainer, painter);
sequenceSash.addSashListener(SWT.Resize, cloneContainer, painter);
sequenceSash.addSashListener(SWT.Paint, cloneContainer, painter);
sequenceSash.setOrientation(SWT.HORIZONTAL);
contents.setWeights(new int[] {0, 1000});
sequenceSash.setWeights(new int[] {0, 1000});
}
/**
* Returns the control that contains all of the headers of the lifelines.
* @return the control that contains all of the headers of the lifelines.
*/
public Control getLifelineControl() {
return objectCanvas;
}
/**
* Returns the control that draws the sequence diagram itself.
* @return the control that draws the sequence diagram itself.
*/
public Control getSequenceControl() {
return sequenceCanvas;
}
/**
* Returns the control that draws the "clone' of the sequence diagram.
* @return the control that draws the "clone' of the sequence diagram.
*/
public Control getCloneControl() {
return clone.getControl();
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Scrollable#getHorizontalBar()
*/
@Override
public ScrollBar getHorizontalBar() {
checkWidget();
//cheat a little. This chart will never have a horizontal scroll bar, but the sequence canvas
//might.
if (sequenceCanvas != null && sequenceCanvas.getHorizontalScrollBarVisibility() != FigureCanvas.NEVER) {
return sequenceCanvas.getHorizontalBar();
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Scrollable#getVerticalBar()
*/
@Override
public ScrollBar getVerticalBar() {
checkWidget();
//cheat a little. This chart will never have a horizontal scroll bar, but the sequence canvas
//might.
if (sequenceCanvas != null && sequenceCanvas.getVerticalScrollBarVisibility() != FigureCanvas.NEVER) {
return sequenceCanvas.getVerticalBar();
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.widgets.UMLChart#createItem(org.eclipse.mylar.zest.custom.sequence.widgets.UMLItem)
*/
@Override
void createItem(UMLItem item) {
super.createItem(item);
if (item instanceof IExpandableItem) {
item.addPropertyChangeListener(expandListener);
}
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.widgets.UMLChart#deleteItem(org.eclipse.mylar.zest.custom.sequence.widgets.UMLItem)
*/
@Override
void deleteItem(UMLItem item) {
if (item instanceof IExpandableItem) {
item.removePropertyChangeListener(expandListener);
}
super.deleteItem(item);
}
/**
* Sets the activation that will be the visual "root" of all activations in this chart.
* Only this activation and its sub activations will be visible in the chart. A chart cannot
* be seen until the root activation is set.
* @param activation
*/
public void setRootActivation(Activation activation) {
if (!activation.getSequenceChart().equals(this)) SWT.error(SWT.ERROR_INVALID_PARENT);
checkWidget();
Activation old = this.rootActivation;
this.rootActivation = activation;
markDirty();
firePropertyChange(ROOT_PROP, old, activation);
fireRootChanged();
}
/**
* Performs a refresh regardless of whether or not the viewer is redrawing
* @param force
*/
public void refresh(boolean force) {
if (isDisposed() || (!force && !isRedrawing())) return;
if (isDirty() && getRootActivation() != null) {
widgetVisuals.scheduleRefresh(jobProcessor);
this.dirty = false;
}
}
/**
* If <code>redraw</code> is set to false, all operations that cause a refresh in the viewer will be deferred until
* <code>redraw</code> is reset to true. Every call to setRedraw(false) must be followed by a subsequent setRedraw(true),
* otherwise redraws will not occur later. Setting <code>redraw</code> to true causes an immediate refresh.
*/
public synchronized void setRedraw(boolean redraw) {
if (!redraw) {
this.deferRedrawCount++;
} else {
if (deferRedrawCount > 0) {
deferRedrawCount--;
}
}
if (isRedrawing()) {
((SuspendableDeferredUpdateManager)sequenceCanvas.getLightweightSystem().getUpdateManager()).resume();
layout();
} else {
((SuspendableDeferredUpdateManager)sequenceCanvas.getLightweightSystem().getUpdateManager()).suspend();
}
}
private synchronized boolean isRedrawing() {
return deferRedrawCount == 0;
}
/**
* @return the rootActivation
*/
public Activation getRootActivation() {
return rootActivation;
}
private SelectionTool defaultTool;
private boolean disposing;
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.widgets.UMLChart#performLayout()
*/
@Override
protected void performLayout() {
performLayout(false);
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.widgets.UMLChart#markDirty()
*/
@Override
protected void markDirty() {
super.markDirty();
refresh();
}
/**
* Performs the layout, forcing it to occur even if not redrawing.
* @param force
*/
protected void performLayout(boolean force) {
if (isDisposed() || (!force && !isRedrawing())) return;
widgetVisuals.scheduleLayout(jobProcessor);
}
@Override
public void layout(boolean changed, boolean all) {
if (isRedrawing()) {
super.layout(changed, all);
}
}
@Override
public void layout(boolean changed) {
if (isRedrawing()) {
super.layout(changed);
}
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.widgets.UMLChart#clearChart()
*/
@Override
public void clearChart() {
this.rootActivation = null;
super.clearChart();
}
/**
* Adds the given SequenceListener to the list of listeners. Does nothing if an identical listener is
* already registered.
* @param listener the listener to register.
*/
public void addSequenceListener(SequenceListener listener) {
sequenceListeners.add(listener);
}
/**
* Removes the given SequenceListener from the list of listeners, if it exists in the list.
* @param listener the listener to remove.
*/
public void removeSequenceListener(SequenceListener listener) {
sequenceListeners.remove(listener);
}
protected void fireExpandChanged(IExpandableItem item, Boolean newValue) {
Event e = new Event();
e.item = (Widget) item;
e.widget = this;
e.doit=true;
e.time = (int)(System.currentTimeMillis() & 0x0000FFFFL);
SequenceEvent se = new SequenceEvent(e);
for (Object o : sequenceListeners.getListeners()) {
SequenceListener l = (SequenceListener) o;
if (newValue) {
l.itemExpanded(se);
} else {
l.itemCollapsed(se);
}
}
}
protected void fireRootChanged() {
Event e = new Event();
e.item = getRootActivation();
e.widget = this;
e.doit=true;
e.time = (int)(System.currentTimeMillis() & 0x0000FFFFL);
e.data = e.item.getData();
SequenceEvent se = new SequenceEvent(e);
for (Object o : sequenceListeners.getListeners()) {
SequenceListener l = (SequenceListener) o;
l.rootChanged(se);
}
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.widgets.UMLChart#setSelection(org.eclipse.mylar.zest.custom.sequence.widgets.UMLItem[])
*/
@Override
public void setSelection(UMLItem[] items) {
super.setSelection(items);
widgetVisuals.setSelection(items);
}
/**
* @param tool
*/
public void setActiveTool(IWidgetTool tool) {
for (ToolEventDispatcher dispatcher : dispatchers) {
dispatcher.setTool(tool);
setCursor(dispatcher.getTool().getDefaultCursor());
}
}
public IWidgetTool getActiveTool() {
return dispatchers[2].getTool();
}
public IWidgetTool getDefaultTool() {
if (defaultTool == null) {
defaultTool = new SelectionTool();
}
return defaultTool;
}
/**
* Returns an item at the given point relative to the reciever, if available.
* @param p
* @return
*/
public Widget getItemAt(Point p) {
return getItemAt(p.x, p.y);
}
/**
* Locates the item in the chart and scrolls to it so that it can be seen.
* If the item is hidden, then the chart is expanded appropriately so that
* the item can be seen.
*
* @param w
*/
public void navigate(UMLItem item) {
if (item instanceof MessageGroup) {
reveal2((MessageGroup)item);
} else if (item instanceof Activation) {
reveal2((Activation)item);
}
Rectangle itemBounds = getRelativeBounds(item);
FigureCanvas containingControl = (FigureCanvas) widgetVisuals.getContainingComposite(item);
if (itemBounds == null || containingControl == null) {
return;
}
if (containingControl == getLifelineGroupControl()) {
setLifelineGroupsVisible(true);
return;
}
if (item instanceof Message) {
//make sure that the source is visible
Rectangle bounds2 = getRelativeBounds(((Message)item).getSource());
itemBounds.x = bounds2.x;
itemBounds.width = bounds2.width;
}
Point displayPoint = toDisplay(itemBounds.x, itemBounds.y);
itemBounds.x = displayPoint.x;
itemBounds.y = displayPoint.y;
if (itemBounds != null) {
Rectangle sequenceBounds = containingControl.getClientArea();
displayPoint = containingControl.getParent().toDisplay(containingControl.getBounds().x, containingControl.getBounds().y);
sequenceBounds.x = displayPoint.x;
sequenceBounds.y = displayPoint.y;
if (getVerticalBar().isVisible()) {
sequenceBounds.width -= getVerticalBar().getSize().x;
}
if (getHorizontalBar().isVisible()) {
sequenceBounds.height -= getHorizontalBar().getSize().y;
}
//scroll y
if (!(containingControl == getLifelineControl())) {
if (itemBounds.y < sequenceBounds.y) {
int diffy = itemBounds.y - sequenceBounds.y;
scrollToY(diffy-10);
} else if (itemBounds.y > (sequenceBounds.y + sequenceBounds.height)) {
int diffy = (itemBounds.y + 10) - (sequenceBounds.y + sequenceBounds.height);
scrollToY(diffy);
}
}
//scroll x
if (itemBounds.x < sequenceBounds.x) {
int diffx = itemBounds.x - sequenceBounds.x;
scrollToX(diffx-10);
} else if ((itemBounds.x+itemBounds.width) > (sequenceBounds.x + sequenceBounds.width)) {
int diffx = (itemBounds.x + itemBounds.width) - (sequenceBounds.x + sequenceBounds.width);
scrollToX(diffx);
}
}
}
/**
* Locates the item in the chart and scrolls to it so that it can be seen. Centers the item in the view.
* If the item is hidden, then the chart is expanded appropriately so that
* the item can be seen.
*
* @param w
*/
public void reveal(final UMLItem item) {
if (item instanceof MessageGroup) {
reveal2((MessageGroup)item);
} else if (item instanceof Activation) {
reveal2((Activation)item);
}
//run in a job in order to allow any layout to finish.
jobProcessor.runInUIThread(new AbstractSimpleProgressRunnable() {
@Override
protected void doRunInUIThread(SimpleProgressMonitor monitor)
throws InvocationTargetException {
monitor.beginTask("Revealing " + item.getText(), 1);
Rectangle itemBounds = getRelativeBounds(item);
FigureCanvas containingControl = (FigureCanvas) widgetVisuals.getContainingComposite(item);
if (itemBounds == null || containingControl == null) {
return;
}
if (containingControl == getLifelineGroupControl()) {
setLifelineGroupsVisible(true);
return;
}
if (item instanceof Message) {
//make sure that the source is visible
Rectangle bounds2 = getRelativeBounds(((Message)item).getSource());
itemBounds.x = bounds2.x;
itemBounds.width = bounds2.width;
}
Point displayPoint = toDisplay(itemBounds.x, itemBounds.y);
itemBounds.x = displayPoint.x;
itemBounds.y = displayPoint.y;
if (itemBounds != null) {
Rectangle sequenceBounds = containingControl.getClientArea();
displayPoint = containingControl.getParent().toDisplay(containingControl.getBounds().x, containingControl.getBounds().y);
sequenceBounds.x = displayPoint.x;
sequenceBounds.y = displayPoint.y;
if (getVerticalBar().isVisible()) {
sequenceBounds.width -= getVerticalBar().getSize().x;
}
if (getHorizontalBar().isVisible()) {
sequenceBounds.height -= getHorizontalBar().getSize().y;
}
//scroll y
if (!(containingControl == getLifelineControl())) {
if (itemBounds.y < sequenceBounds.y) {
int diffy = itemBounds.y - sequenceBounds.y - sequenceBounds.height/2;
scrollToY(diffy-10);
} else if (itemBounds.y > (sequenceBounds.y + sequenceBounds.height)) {
int diffy = (itemBounds.y + 10) - (sequenceBounds.y + sequenceBounds.height) + sequenceBounds.height/2;
scrollToY(diffy);
}
}
//scroll x
if (itemBounds.x < sequenceBounds.x) {
int diffx = itemBounds.x - sequenceBounds.x - sequenceBounds.width/2;
scrollToX(diffx-10);
} else if ((itemBounds.x+itemBounds.width) > (sequenceBounds.x + sequenceBounds.width)) {
int diffx = (itemBounds.x + itemBounds.width) - (sequenceBounds.x + sequenceBounds.width) + sequenceBounds.width/2;
scrollToX(diffx);
}
}
monitor.done();
}
}, getDisplay(), false);
}
private void scrollToX(int amount) {
int currentValue = sequenceCanvas.getViewport().getHorizontalRangeModel().getValue();
int currentY = sequenceCanvas.getViewport().getVerticalRangeModel().getValue();
sequenceCanvas.scrollSmoothTo( currentValue+amount, currentY);
}
private void scrollToY(int amount) {
int currentValue = sequenceCanvas.getViewport().getVerticalRangeModel().getValue();
int currentX = sequenceCanvas.getViewport().getHorizontalRangeModel().getValue();
sequenceCanvas.scrollSmoothTo(currentX, currentValue+amount);
}
private void setScrollX(int location) {
int currentValue = sequenceCanvas.getViewport().getVerticalRangeModel().getValue();
sequenceCanvas.scrollSmoothTo(location, currentValue);
}
private void reveal2(MessageGroup group) {
if (!group.isVisible()) {
Activation a = group.getActivation();
if (a== null || a.isDisposed()) {
return;
}
//walk up, expanding the activations.
setRedraw(false);
while (a != null && !a.isDisposed() && !a.isVisible()) {
if (!a.isExpanded()) {
a.setExpanded(true);
}
if (!(a.getSourceCall() == null || a.getSourceCall().isDisposed())) {
a = a.getSourceCall().getSource();
} else {
a = null;
}
}
if (a != null && !a.isDisposed() && !a.isExpanded()) {
a.setExpanded(true);
}
setRedraw(true);
refresh(true);
}
}
private void reveal2(Activation activation) {
if (!activation.isVisible()) {
setRedraw(false);
Message call = activation.getSourceCall();
while (call != null && !call.isDisposed()) {
Activation source = call.getSource();
if (source == null || source.isDisposed()) {
return;
}
if (!source.isExpanded()) {
source.setExpanded(true);
}
//if (!source.isVisible()) {
call = source.getSourceCall();
//}
}
setRedraw(true);
refresh(true);
}
}
/**
* Returns the bounds in this chart of the given item, relative to origin of the
* scrollable area on the chart, if it is currently displayed. Null otherwise. The
* x and y location of the bounds will therefore always be positive. To get
* the bounds of the item relative to the chart area, use #getRelativeBounds(UMLItem);
* @param item the item to get the bounds of.
* @return the chart-relative bounds of the given item.
*/
public Rectangle getItemBounds(UMLItem item) {
org.eclipse.draw2d.geometry.Rectangle b = widgetVisuals.getLocation(item);
if (b != null) {
return new Rectangle(b.x, b.y, b.width, b.height);
}
return null;
}
/**
* Returns the bounds of the given item relative to the origin of the chart, not
* the scrollable area.
* @param item
* @return
*/
public Rectangle getRelativeBounds(UMLItem item) {
org.eclipse.draw2d.geometry.Rectangle b = widgetVisuals.getRelativeLocation(item);
if (b != null) {
return new Rectangle(b.x, b.y, b.width, b.height);
}
return null;
}
/**
* Returns the rectangular region that displays the main sequence diagram
* (the region in the right-hand sash if both the clone pane and
* the sequence pane are visible).
* @return the rectangular region that displays the main sequence diagram.
*/
public Rectangle getSequenceArea() {
org.eclipse.draw2d.geometry.Rectangle region =
widgetVisuals.getSequencePanelArea();
if (region != null) {
return new Rectangle(region.x, region.y, region.width, region.height);
}
return new Rectangle(0,0,0,0);
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.widgets.UMLChart#setBackground(org.eclipse.swt.graphics.Color)
*/
@Override
public void setBackground(Color color) {
super.setBackground(color);
contents.setBackground(color);
groupingCanvas.setBackground(color);
objectCanvas.setBackground(color);
sequenceCanvas.setBackground(color);
clone.setBackground(color);
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.widgets.UMLChart#isVisible(org.eclipse.zest.custom.sequence.widgets.UMLItem)
*/
@Override
boolean isVisible(UMLItem item) {
return widgetVisuals.isVisible(item);
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.widgets.UMLChart#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
*/
@Override
protected void widgetDisposed(DisposeEvent e) {
this.disposing = true;
jobProcessor.quit();
super.widgetDisposed(e);
eventFilter.unhookFromDisplay();
sequenceListeners.clear();
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Widget#isDisposed()
*/
@Override
public boolean isDisposed() {
return super.isDisposed() || disposing;
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.widgets.UMLChart#refresh()
*/
@Override
public void refresh() {
refresh(false);
}
/**
* Returns the item at the given x and y point relative to the chart coordinates,
* or null if none could be found.
* @param x
* @param y
* @return
*/
public Widget getItemAt(int x, int y) {
//translate the point to the chart contents.
Point p = toDisplay(x, y);
Point p2 = contents.toControl(p);
if (p2.y <= MessageBasedSequenceVisuals.OBJECT_HEIGHT) {
//use the object canvas
Point localPoint = objectCanvas.toControl(p);
IFigure f = objectCanvas.getLightweightSystem().getRootFigure().findFigureAt(localPoint.x, localPoint.y);
if (f != null) {
return widgetVisuals.getWidget(f);
}
} else {
Point localPoint = sequenceCanvas.toControl(p);
IFigure f = sequenceCanvas.getLightweightSystem().getRootFigure().findFigureAt(localPoint.x, localPoint.y);
if (f != null) {
return widgetVisuals.getWidget(f);
}
}
return null;
}
/**
* Returns the lifelines that are currently visible in the chart, in the order that
* they appear.
* @return the visible lifelines in the chart.
*/
public Lifeline[] getVisibleLifelines() {
return widgetVisuals.getVisibleLifelines();
}
/**
* True iff the lifeline group panel is visible.
* @return true iff the lifeline panel is visible.
*/
public boolean isLifelineGroupsVisible() {
return contents.getMaximizedControl() == null;
}
/**
* Sets whether or not the lifeline group area can be viewed. When visible is false, then
* the lifeline group area will never be drawn. If it is true, then the lifeline group area will be
* expandable using a draggable sash.
* @param visible the visible state that the lifeline group area should have.
*/
public void setLifelineGroupsVisible(boolean visible) {
if (visible == isLifelineGroupsVisible()) {
return;
}
if (visible) {
contents.setMaximizedControl(null);
} else {
contents.setMaximizedControl(sequenceSash);
}
}
/**
* Sets whether or not the clone area can be viewed. When visible is false, then
* the clone area will never be drawn. If it is true, then the clone area will be
* expandable using a draggable sash.
* @param visible the visible state that the clone area should have.
*/
public void setCloneVisible(boolean visible) {
if (visible == isCloneVisible()) {
return;
}
if (visible) {
sequenceSash.setMaximizedControl(null);
} else {
sequenceSash.setMaximizedControl(sequenceContainer);
}
}
/**
* True iff the clone panel is visible.
* @return true iff the clone panel is visible.
*/
public boolean isCloneVisible() {
return sequenceSash.getMaximizedControl() == null;
}
/**
* The control that draws the lifeline groups.
* @return
*/
public Control getLifelineGroupControl() {
return groupingCanvas;
}
}