Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 277534 | Differences between
and this patch

Collapse All | Expand All

(-)META-INF/MANIFEST.MF (-5 / +6 lines)
Lines 4-15 Link Here
4
Bundle-SymbolicName: org.eclipse.zest.core;singleton:=true
4
Bundle-SymbolicName: org.eclipse.zest.core;singleton:=true
5
Bundle-Vendor: %Plugin.providerName
5
Bundle-Vendor: %Plugin.providerName
6
Bundle-Localization: plugin
6
Bundle-Localization: plugin
7
Bundle-Version: 1.2.0.qualifier
7
Bundle-Version: 2.0.0.qualifier
8
Require-Bundle: org.eclipse.zest.layouts,
8
Require-Bundle: org.eclipse.ui,
9
 org.eclipse.ui,
9
 org.eclipse.draw2d;visibility:=reexport,
10
 org.eclipse.draw2d;visibility:=reexport
10
 org.eclipse.zest.layout_2_0;bundle-version="1.0.0"
11
Eclipse-LazyStart: false
11
Eclipse-LazyStart: false
12
Export-Package: org.eclipse.zest.core.viewers,
12
Export-Package: org.eclipse.zest.core.viewers,
13
 org.eclipse.zest.core.widgets
13
 org.eclipse.zest.core.widgets,
14
 org.eclipse.zest.core.widgets.custom
14
Import-Package: com.ibm.icu.text;version="[3.8.1,5.0.0)"
15
Import-Package: com.ibm.icu.text;version="[3.8.1,5.0.0)"
15
Bundle-RequiredExecutionEnvironment: J2SE-1.4
16
Bundle-RequiredExecutionEnvironment: J2SE-1.4
(-)src/org/eclipse/zest/core/viewers/AbstractStructuredGraphViewer.java (+801 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria Mateusz Matela
9
 * <mateusz.matela@gmail.com> - Adapt Zest to changes in layout -
10
 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=283179
11
 ******************************************************************************/
12
package org.eclipse.zest.core.viewers;
13
14
import java.util.ArrayList;
15
import java.util.Comparator;
16
import java.util.HashMap;
17
import java.util.Iterator;
18
import java.util.LinkedList;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.TreeSet;
22
23
import org.eclipse.draw2d.IFigure;
24
import org.eclipse.swt.SWT;
25
import org.eclipse.swt.SWTError;
26
import org.eclipse.swt.events.DisposeEvent;
27
import org.eclipse.swt.widgets.Widget;
28
import org.eclipse.zest.core.viewers.internal.IStylingGraphModelFactory;
29
import org.eclipse.zest.core.widgets.Graph;
30
import org.eclipse.zest.core.widgets.GraphConnection;
31
import org.eclipse.zest.core.widgets.GraphContainer;
32
import org.eclipse.zest.core.widgets.GraphItem;
33
import org.eclipse.zest.core.widgets.GraphNode;
34
import org.eclipse.zest.core.widgets.ZestStyles;
35
import org.eclipse.zest.core.widgets.custom.CGraphNode;
36
import org.eclipse.zest.layouts.LayoutAlgorithm;
37
38
/**
39
 * Abstraction of graph viewers to implement functionality used by all of them.
40
 * Not intended to be implemented by clients. Use one of the provided children
41
 * instead.
42
 * 
43
 * @author Del Myers
44
 * @since 2.0
45
 */
46
public abstract class AbstractStructuredGraphViewer extends
47
		AbstractZoomableViewer {
48
	/**
49
	 * Contains top-level styles for the entire graph. Set in the constructor. *
50
	 */
51
	private int graphStyle;
52
53
	/**
54
	 * Contains node-level styles for the graph. Set in setNodeStyle(). Defaults
55
	 * are used in the constructor.
56
	 */
57
	private int nodeStyle;
58
59
	/**
60
	 * Contains arc-level styles for the graph. Set in setConnectionStyle().
61
	 * Defaults are used in the constructor.
62
	 */
63
	private int connectionStyle;
64
65
	private HashMap nodesMap = new HashMap();
66
	private HashMap connectionsMap = new HashMap();
67
68
	/**
69
	 * A simple graph comparator that orders graph elements based on thier type
70
	 * (connection or node), and their unique object identification.
71
	 */
72
	private class SimpleGraphComparator implements Comparator {
73
		TreeSet storedStrings;
74
75
		/**
76
		 * 
77
		 */
78
		public SimpleGraphComparator() {
79
			this.storedStrings = new TreeSet();
80
		}
81
82
		/*
83
		 * (non-Javadoc)
84
		 * 
85
		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
86
		 */
87
		public int compare(Object arg0, Object arg1) {
88
			if (arg0 instanceof GraphNode && arg1 instanceof GraphConnection) {
89
				return 1;
90
			} else if (arg0 instanceof GraphConnection
91
					&& arg1 instanceof GraphNode) {
92
				return -1;
93
			}
94
			if (arg0.equals(arg1)) {
95
				return 0;
96
			}
97
			return getObjectString(arg0).compareTo(getObjectString(arg1));
98
		}
99
100
		private String getObjectString(Object o) {
101
			String s = o.getClass().getName() + "@"
102
					+ Integer.toHexString(o.hashCode());
103
			while (storedStrings.contains(s)) {
104
				s = s + 'X';
105
			}
106
			return s;
107
		}
108
	}
109
110
	protected AbstractStructuredGraphViewer(int graphStyle) {
111
		this.graphStyle = graphStyle;
112
		this.connectionStyle = SWT.NONE;
113
		this.nodeStyle = SWT.NONE;
114
115
	}
116
117
	/**
118
	 * Sets the default style for nodes in this graph. Note: if an input is set
119
	 * on the viewer, a ZestException will be thrown.
120
	 * 
121
	 * @param nodeStyle
122
	 *            the style for the nodes.
123
	 * @see #ZestStyles
124
	 */
125
	public void setNodeStyle(int nodeStyle) {
126
		if (getInput() != null) {
127
			throw new SWTError(SWT.ERROR_UNSPECIFIED);
128
		}
129
		this.nodeStyle = nodeStyle;
130
	}
131
132
	/**
133
	 * Sets the default style for connections in this graph. Note: if an input
134
	 * is set on the viewer, a ZestException will be thrown.
135
	 * 
136
	 * @param connectionStyle
137
	 *            the style for the connections.
138
	 * @see #ZestStyles
139
	 */
140
	public void setConnectionStyle(int connectionStyle) {
141
		if (getInput() != null) {
142
			throw new SWTError(SWT.ERROR_UNSPECIFIED);
143
		}
144
		if (!ZestStyles.validateConnectionStyle(connectionStyle)) {
145
			throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
146
		}
147
		this.connectionStyle = connectionStyle;
148
	}
149
150
	/**
151
	 * Returns the style set for the graph
152
	 * 
153
	 * @return The style set of the graph
154
	 */
155
	public int getGraphStyle() {
156
		return graphStyle;
157
	}
158
159
	/**
160
	 * Returns the style set for the nodes.
161
	 * 
162
	 * @return the style set for the nodes.
163
	 */
164
	public int getNodeStyle() {
165
		return nodeStyle;
166
	}
167
168
	public Graph getGraphControl() {
169
		return (Graph) getControl();
170
	}
171
172
	/**
173
	 * @return the connection style.
174
	 */
175
	public int getConnectionStyle() {
176
		return connectionStyle;
177
	}
178
179
	/**
180
	 * Sets the layout algorithm for this viewer. Subclasses may place
181
	 * restrictions on the algorithms that it accepts.
182
	 * 
183
	 * @param algorithm
184
	 *            the layout algorithm
185
	 * @param run
186
	 *            true if the layout algorithm should be run immediately. This
187
	 *            is a hint.
188
	 */
189
	public abstract void setLayoutAlgorithm(LayoutAlgorithm algorithm,
190
			boolean run);
191
192
	/**
193
	 * Gets the current layout algorithm.
194
	 * 
195
	 * @return the current layout algorithm.
196
	 */
197
	protected abstract LayoutAlgorithm getLayoutAlgorithm();
198
199
	/**
200
	 * Equivalent to setLayoutAlgorithm(algorithm, false).
201
	 * 
202
	 * @param algorithm
203
	 */
204
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm) {
205
		setLayoutAlgorithm(algorithm, false);
206
	}
207
208
	public Object[] getNodeElements() {
209
		return this.nodesMap.keySet().toArray();
210
	}
211
212
	public Object[] getConnectionElements() {
213
		return this.connectionsMap.keySet().toArray();
214
	}
215
216
	/**
217
	 * @noreference This method is not intended to be referenced by clients.
218
	 * @return
219
	 */
220
	public HashMap getNodesMap() {
221
		return this.nodesMap;
222
	}
223
224
	/**
225
	 * 
226
	 * @param element
227
	 * @return
228
	 * @noreference This method is not intended to be referenced by clients.
229
	 */
230
	public GraphNode addGraphModelContainer(Object element) {
231
		GraphNode node = this.getGraphModelNode(element);
232
		if (node == null) {
233
			node = new GraphContainer((Graph) getControl(), SWT.NONE);
234
			this.nodesMap.put(element, node);
235
			node.setData(element);
236
		}
237
		return node;
238
	}
239
240
	/**
241
	 * 
242
	 * @param container
243
	 * @param element
244
	 * @return
245
	 * @noreference This method is not intended to be referenced by clients.
246
	 */
247
	public GraphNode addGraphModelNode(GraphContainer container, Object element) {
248
		GraphNode node = this.getGraphModelNode(element);
249
		if (node == null) {
250
			node = new GraphNode(container, SWT.NONE);
251
			this.nodesMap.put(element, node);
252
			node.setData(element);
253
		}
254
		return node;
255
	}
256
257
	/**
258
	 * 
259
	 * @param element
260
	 * @param figure
261
	 * @return
262
	 * @noreference This method is not intended to be referenced by clients.
263
	 */
264
	public GraphNode addGraphModelNode(Object element, IFigure figure) {
265
		GraphNode node = this.getGraphModelNode(element);
266
		if (node == null) {
267
			if (figure != null) {
268
				node = new CGraphNode((Graph) getControl(), SWT.NONE, figure);
269
				this.nodesMap.put(element, node);
270
				node.setData(element);
271
			} else {
272
				node = new GraphNode((Graph) getControl(), SWT.NONE);
273
				this.nodesMap.put(element, node);
274
				node.setData(element);
275
			}
276
		}
277
		return node;
278
	}
279
280
	/**
281
	 * 
282
	 * @param element
283
	 * @param source
284
	 * @param target
285
	 * @return
286
	 * @noreference This method is not intended to be referenced by clients.
287
	 */
288
	public GraphConnection addGraphModelConnection(Object element,
289
			GraphNode source, GraphNode target) {
290
		GraphConnection connection = this.getGraphModelConnection(element);
291
		if (connection == null) {
292
			connection = new GraphConnection((Graph) getControl(), SWT.NONE,
293
					source, target);
294
			this.connectionsMap.put(element, connection);
295
			connection.setData(element);
296
		}
297
		return connection;
298
299
	}
300
301
	/**
302
	 * 
303
	 * @param obj
304
	 * @return
305
	 * @noreference This method is not intended to be referenced by clients.
306
	 */
307
	public GraphConnection getGraphModelConnection(Object obj) {
308
		return (GraphConnection) this.connectionsMap.get(obj);
309
	}
310
311
	/**
312
	 * 
313
	 * @param obj
314
	 * @return
315
	 * @noreference This method is not intended to be referenced by clients.
316
	 */
317
	public GraphNode getGraphModelNode(Object obj) {
318
		return (GraphNode) this.nodesMap.get(obj);
319
	}
320
321
	/**
322
	 * @param obj
323
	 * @noreference This method is not intended to be referenced by clients.
324
	 */
325
	public void removeGraphModelConnection(Object obj) {
326
		GraphConnection connection = (GraphConnection) connectionsMap.get(obj);
327
		if (connection != null) {
328
			connectionsMap.remove(obj);
329
			if (!connection.isDisposed()) {
330
				connection.dispose();
331
			}
332
		}
333
	}
334
335
	/**
336
	 * @noreference This method is not intended to be referenced by clients.
337
	 */
338
	public void removeGraphModelNode(Object obj) {
339
		GraphNode node = (GraphNode) nodesMap.get(obj);
340
		if (node != null) {
341
			nodesMap.remove(obj);
342
			if (!node.isDisposed()) {
343
				node.dispose();
344
			}
345
		}
346
	}
347
348
	protected void handleDispose(DisposeEvent event) {
349
350
		if (getControl() != null && !getControl().isDisposed()) {
351
			getControl().dispose();
352
		}
353
		super.handleDispose(event);
354
	}
355
356
	/*
357
	 * (non-Javadoc)
358
	 * 
359
	 * @see
360
	 * org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.
361
	 * Object)
362
	 */
363
	protected void internalRefresh(Object element) {
364
		if (getInput() == null) {
365
			return;
366
		}
367
		if (element == getInput()) {
368
			getFactory().refreshGraph(getGraphControl());
369
		} else {
370
			getFactory().refresh(getGraphControl(), element);
371
		}
372
		// After all the items are loaded, we call update to ensure drawing.
373
		// This way the damaged area does not get too big if we start
374
		// adding and removing more nodes
375
		getGraphControl().getLightweightSystem().getUpdateManager()
376
				.performUpdate();
377
	}
378
379
	protected void doUpdateItem(Widget item, Object element, boolean fullMap) {
380
		if (item == getGraphControl()) {
381
			getFactory().update(getNodesArray(getGraphControl()));
382
			getFactory().update(getConnectionsArray(getGraphControl()));
383
		} else if (item instanceof GraphItem) {
384
			getFactory().update((GraphItem) item);
385
		}
386
	}
387
388
	/*
389
	 * (non-Javadoc)
390
	 * 
391
	 * @see
392
	 * org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.
393
	 * Object)
394
	 */
395
	protected Widget doFindInputItem(Object element) {
396
397
		if (element == getInput() && element instanceof Widget) {
398
			return (Widget) element;
399
		}
400
		return null;
401
	}
402
403
	/*
404
	 * (non-Javadoc)
405
	 * 
406
	 * @see
407
	 * org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
408
	 */
409
	protected Widget doFindItem(Object element) {
410
		Widget node = (Widget) nodesMap.get(element);
411
		Widget connection = (Widget) connectionsMap.get(element);
412
		return (node != null) ? node : connection;
413
	}
414
415
	/*
416
	 * (non-Javadoc)
417
	 * 
418
	 * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
419
	 */
420
	protected List getSelectionFromWidget() {
421
		List internalSelection = getWidgetSelection();
422
		LinkedList externalSelection = new LinkedList();
423
		for (Iterator i = internalSelection.iterator(); i.hasNext();) {
424
			// @tag zest.todo : should there be a method on IGraphItem to get
425
			// the external data?
426
			GraphItem item = (GraphItem) i.next();
427
			if (item instanceof GraphNode) {
428
				externalSelection.add(((GraphNode) item).getData());
429
			} else if (item instanceof GraphConnection) {
430
				externalSelection.add(((GraphConnection) item)
431
						.getExternalConnection());
432
			} else if (item instanceof Widget) {
433
				externalSelection.add(((Widget) item).getData());
434
			}
435
		}
436
		return externalSelection;
437
	}
438
439
	protected GraphItem[] /* GraphItem */findItems(List l) {
440
		if (l == null) {
441
			return new GraphItem[0];
442
		}
443
444
		ArrayList list = new ArrayList();
445
		Iterator iterator = l.iterator();
446
447
		while (iterator.hasNext()) {
448
			GraphItem w = (GraphItem) findItem(iterator.next());
449
			list.add(w);
450
		}
451
		return (GraphItem[]) list.toArray(new GraphItem[list.size()]);
452
	}
453
454
	/*
455
	 * (non-Javadoc)
456
	 * 
457
	 * @see
458
	 * org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.
459
	 * util.List, boolean)
460
	 */
461
	protected void setSelectionToWidget(List l, boolean reveal) {
462
		Graph control = (Graph) getControl();
463
		List selection = new LinkedList();
464
		for (Iterator i = l.iterator(); i.hasNext();) {
465
			Object obj = i.next();
466
			GraphNode node = (GraphNode) nodesMap.get(obj);
467
			GraphConnection conn = (GraphConnection) connectionsMap.get(obj);
468
			if (node != null) {
469
				selection.add(node);
470
			}
471
			if (conn != null) {
472
				selection.add(conn);
473
			}
474
		}
475
		control.setSelection((GraphNode[]) selection
476
				.toArray(new GraphNode[selection.size()]));
477
	}
478
479
	/**
480
	 * Gets the internal model elements that are selected.
481
	 * 
482
	 * @return
483
	 */
484
	protected List getWidgetSelection() {
485
		Graph control = (Graph) getControl();
486
		return control.getSelection();
487
	}
488
489
	/*
490
	 * (non-Javadoc)
491
	 * 
492
	 * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
493
	 * java.lang.Object)
494
	 */
495
	protected void inputChanged(Object input, Object oldInput) {
496
		IStylingGraphModelFactory factory = getFactory();
497
		factory.setConnectionStyle(getConnectionStyle());
498
		factory.setNodeStyle(getNodeStyle());
499
500
		// Save the old map so we can set the size and position of any nodes
501
		// that are the same
502
		Map oldNodesMap = nodesMap;
503
		Graph graph = (Graph) getControl();
504
		graph.setSelection(new GraphNode[0]);
505
506
		Iterator iterator = nodesMap.values().iterator();
507
		while (iterator.hasNext()) {
508
			GraphNode node = (GraphNode) iterator.next();
509
			if (!node.isDisposed()) {
510
				node.dispose();
511
			}
512
		}
513
514
		iterator = connectionsMap.values().iterator();
515
		while (iterator.hasNext()) {
516
			GraphConnection connection = (GraphConnection) iterator.next();
517
			if (!connection.isDisposed()) {
518
				connection.dispose();
519
			}
520
		}
521
522
		nodesMap = new HashMap();
523
		connectionsMap = new HashMap();
524
525
		graph = factory.createGraphModel(graph);
526
527
		((Graph) getControl()).setNodeStyle(getNodeStyle());
528
		((Graph) getControl()).setConnectionStyle(getConnectionStyle());
529
530
		// check if any of the pre-existing nodes are still present
531
		// in this case we want them to keep the same location & size
532
		for (Iterator iter = oldNodesMap.keySet().iterator(); iter.hasNext();) {
533
			Object data = iter.next();
534
			GraphNode newNode = (GraphNode) nodesMap.get(data);
535
			if (newNode != null) {
536
				GraphNode oldNode = (GraphNode) oldNodesMap.get(data);
537
				newNode.setLocation(oldNode.getLocation().x, oldNode
538
						.getLocation().y);
539
				if (oldNode.isSizeFixed()) {
540
					newNode.setSize(oldNode.getSize().width,
541
							oldNode.getSize().height);
542
				}
543
			}
544
		}
545
	}
546
547
	/**
548
	 * Returns the factory used to create the model. This must not be called
549
	 * before the content provider is set.
550
	 * 
551
	 * @return
552
	 * @noreference This method is not intended to be referenced by clients.
553
	 * @nooverride This method is not intended to be re-implemented or extended
554
	 *             by clients.
555
	 */
556
	protected abstract IStylingGraphModelFactory getFactory();
557
558
	protected void filterVisuals() {
559
		if (getGraphControl() == null) {
560
			return;
561
		}
562
		Object[] filtered = getFilteredChildren(getInput());
563
		SimpleGraphComparator comparator = new SimpleGraphComparator();
564
		TreeSet filteredElements = new TreeSet(comparator);
565
		TreeSet unfilteredElements = new TreeSet(comparator);
566
		List connections = getGraphControl().getConnections();
567
		List nodes = getGraphControl().getNodes();
568
		if (filtered.length == 0) {
569
			// set everything to invisible.
570
			// @tag zest.bug.156528-Filters.check : should we only filter out
571
			// the nodes?
572
			for (Iterator i = connections.iterator(); i.hasNext();) {
573
				GraphConnection c = (GraphConnection) i.next();
574
				c.setVisible(false);
575
			}
576
			for (Iterator i = nodes.iterator(); i.hasNext();) {
577
				GraphNode n = (GraphNode) i.next();
578
				n.setVisible(false);
579
			}
580
			return;
581
		}
582
		for (Iterator i = connections.iterator(); i.hasNext();) {
583
			GraphConnection c = (GraphConnection) i.next();
584
			if (c.getExternalConnection() != null) {
585
				unfilteredElements.add(c);
586
			}
587
		}
588
		for (Iterator i = nodes.iterator(); i.hasNext();) {
589
			GraphNode n = (GraphNode) i.next();
590
			if (n.getData() != null) {
591
				unfilteredElements.add(n);
592
			}
593
		}
594
		for (int i = 0; i < filtered.length; i++) {
595
			Object modelElement = connectionsMap.get(filtered[i]);
596
			if (modelElement == null) {
597
				modelElement = nodesMap.get(filtered[i]);
598
			}
599
			if (modelElement != null) {
600
				filteredElements.add(modelElement);
601
			}
602
		}
603
		unfilteredElements.removeAll(filteredElements);
604
		// set all the elements that did not pass the filters to invisible, and
605
		// all the elements that passed to visible.
606
		while (unfilteredElements.size() > 0) {
607
			GraphItem i = (GraphItem) unfilteredElements.first();
608
			i.setVisible(false);
609
			unfilteredElements.remove(i);
610
		}
611
		while (filteredElements.size() > 0) {
612
			GraphItem i = (GraphItem) filteredElements.first();
613
			i.setVisible(true);
614
			filteredElements.remove(i);
615
		}
616
	}
617
618
	/*
619
	 * (non-Javadoc)
620
	 * 
621
	 * @see
622
	 * org.eclipse.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object
623
	 * )
624
	 */
625
	protected Object[] getRawChildren(Object parent) {
626
		if (parent == getInput()) {
627
			// get the children from the model.
628
			LinkedList children = new LinkedList();
629
			if (getGraphControl() != null) {
630
				List connections = getGraphControl().getConnections();
631
				List nodes = getGraphControl().getNodes();
632
				for (Iterator i = connections.iterator(); i.hasNext();) {
633
					GraphConnection c = (GraphConnection) i.next();
634
					if (c.getExternalConnection() != null) {
635
						children.add(c.getExternalConnection());
636
					}
637
				}
638
				for (Iterator i = nodes.iterator(); i.hasNext();) {
639
					GraphNode n = (GraphNode) i.next();
640
					if (n.getData() != null) {
641
						children.add(n.getData());
642
					}
643
				}
644
				return children.toArray();
645
			}
646
		}
647
		return super.getRawChildren(parent);
648
	}
649
650
	/**
651
	 * 
652
	 */
653
	public void reveal(Object element) {
654
		Widget[] items = this.findItems(element);
655
		for (int i = 0; i < items.length; i++) {
656
			Widget item = items[i];
657
			if (item instanceof GraphNode) {
658
				GraphNode graphModelNode = (GraphNode) item;
659
				graphModelNode.highlight();
660
			} else if (item instanceof GraphConnection) {
661
				GraphConnection graphModelConnection = (GraphConnection) item;
662
				graphModelConnection.highlight();
663
			}
664
		}
665
	}
666
667
	public void unReveal(Object element) {
668
		Widget[] items = this.findItems(element);
669
		for (int i = 0; i < items.length; i++) {
670
			Widget item = items[i];
671
			if (item instanceof GraphNode) {
672
				GraphNode graphModelNode = (GraphNode) item;
673
				graphModelNode.unhighlight();
674
			} else if (item instanceof GraphConnection) {
675
				GraphConnection graphModelConnection = (GraphConnection) item;
676
				graphModelConnection.unhighlight();
677
			}
678
		}
679
	}
680
681
	/**
682
	 * Applies the viewers layouts.
683
	 * 
684
	 */
685
	public abstract void applyLayout();
686
687
	/**
688
	 * Removes the given connection object from the layout algorithm and the
689
	 * model.
690
	 * 
691
	 * @param connection
692
	 */
693
	public void removeRelationship(Object connection) {
694
		GraphConnection relationship = (GraphConnection) connectionsMap
695
				.get(connection);
696
697
		if (relationship != null) {
698
			// remove the relationship from the model
699
			relationship.dispose();
700
		}
701
	}
702
703
	/**
704
	 * Creates a new node and adds it to the graph. If it already exists nothing
705
	 * happens.
706
	 * 
707
	 * @param newNode
708
	 */
709
	public void addNode(Object element) {
710
		if (nodesMap.get(element) == null) {
711
			// create the new node
712
			getFactory().createNode(getGraphControl(), element);
713
714
		}
715
	}
716
717
	/**
718
	 * Removes the given element from the layout algorithm and the model.
719
	 * 
720
	 * @param element
721
	 *            The node element to remove.
722
	 */
723
	public void removeNode(Object element) {
724
		GraphNode node = (GraphNode) nodesMap.get(element);
725
726
		if (node != null) {
727
			// remove the node and it's connections from the model
728
			node.dispose();
729
		}
730
	}
731
732
	/**
733
	 * Creates a new relationship between the source node and the destination
734
	 * node. If either node doesn't exist then it will be created.
735
	 * 
736
	 * @param connection
737
	 *            The connection data object.
738
	 * @param srcNode
739
	 *            The source node data object.
740
	 * @param destNode
741
	 *            The destination node data object.
742
	 */
743
	public void addRelationship(Object connection, Object srcNode,
744
			Object destNode) {
745
		// create the new relationship
746
		IStylingGraphModelFactory modelFactory = getFactory();
747
		modelFactory.createConnection(getGraphControl(), connection, srcNode,
748
				destNode);
749
750
	}
751
752
	/**
753
	 * Adds a new relationship given the connection. It will use the content
754
	 * provider to determine the source and destination nodes.
755
	 * 
756
	 * @param connection
757
	 *            The connection data object.
758
	 */
759
	public void addRelationship(Object connection) {
760
		IStylingGraphModelFactory modelFactory = getFactory();
761
		if (connectionsMap.get(connection) == null) {
762
			if (modelFactory.getContentProvider() instanceof IGraphContentProvider) {
763
				IGraphContentProvider content = ((IGraphContentProvider) modelFactory
764
						.getContentProvider());
765
				Object source = content.getSource(connection);
766
				Object dest = content.getDestination(connection);
767
				// create the new relationship
768
				modelFactory.createConnection(getGraphControl(), connection,
769
						source, dest);
770
			} else {
771
				throw new UnsupportedOperationException();
772
			}
773
		}
774
	}
775
776
	/**
777
	 * Converts the list of GraphModelConnection objects into an array and
778
	 * returns it.
779
	 * 
780
	 * @return GraphModelConnection[]
781
	 */
782
	protected GraphConnection[] getConnectionsArray(Graph graph) {
783
		GraphConnection[] connsArray = new GraphConnection[graph
784
				.getConnections().size()];
785
		connsArray = (GraphConnection[]) graph.getConnections().toArray(
786
				connsArray);
787
		return connsArray;
788
	}
789
790
	/**
791
	 * Converts the list of GraphModelNode objects into an array an returns it.
792
	 * 
793
	 * @return GraphModelNode[]
794
	 */
795
	protected GraphNode[] getNodesArray(Graph graph) {
796
		GraphNode[] nodesArray = new GraphNode[graph.getNodes().size()];
797
		nodesArray = (GraphNode[]) graph.getNodes().toArray(nodesArray);
798
		return nodesArray;
799
	}
800
801
}
(-)src/org/eclipse/zest/core/viewers/AbstractZoomableViewer.java (-39 / +43 lines)
Lines 1-39 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * All rights reserved. This program and the accompanying materials
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * which accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 * 
8
 * Contributors:
8
 * Contributors: The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
9
 *******************************************************************************/
10
 *******************************************************************************/
10
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
11
12
12
import org.eclipse.draw2d.geometry.Rectangle;
13
import org.eclipse.draw2d.geometry.Rectangle;
13
import org.eclipse.jface.viewers.StructuredViewer;
14
import org.eclipse.jface.viewers.StructuredViewer;
14
15
import org.eclipse.zest.core.viewers.internal.ZoomManager;
15
/**
16
16
 * A simple interface that provides zooming capabilites. Not intended to be
17
/**
17
 * subclassed by clients.
18
 * A simple interface that provides zooming capabilites. Not intended to be subclassed by clients.
18
 * 
19
 * @author Del Myers
19
 * @author Del Myers
20
 *
20
 * 
21
 */
21
 * @noextend This class is not intended to be subclassed by clients.
22
//@tag bug.156286-Zooming.fix
22
 * 
23
public abstract class AbstractZoomableViewer extends StructuredViewer {
23
 */
24
	/**
24
// @tag bug.156286-Zooming.fix
25
	 * Returns a ZoomManager that zooming can be done on. May return null if none
25
public abstract class AbstractZoomableViewer extends StructuredViewer {
26
	 * is available.
26
	/**
27
	 * @return a ZoomManager that zooming can be done on.
27
	 * Returns a ZoomManager that zooming can be done on. May return null if
28
	 */
28
	 * none is available.
29
	protected abstract ZoomManager getZoomManager();
29
	 * 
30
	
30
	 * @return a ZoomManager that zooming can be done on.
31
	public void zoomTo(int x, int y, int width, int height) {
31
	 * @since 2.0
32
		Rectangle r = new Rectangle(x,y,width,height);
32
	 */
33
		if (r.isEmpty()) {
33
	protected abstract ZoomManager getZoomManager();
34
			getZoomManager().setZoomAsText("100%");
34
35
		} else {
35
	public void zoomTo(int x, int y, int width, int height) {
36
			getZoomManager().zoomTo(r);
36
		Rectangle r = new Rectangle(x, y, width, height);
37
		}
37
		if (r.isEmpty()) {
38
	}
38
			getZoomManager().setZoomAsText("100%");
39
}
39
		} else {
40
			getZoomManager().zoomTo(r);
41
		}
42
	}
43
}
(-)src/org/eclipse/zest/core/viewers/EntityConnectionData.java (-59 / +61 lines)
Lines 1-59 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
/**
13
/**
14
 * A simple object that is used as the "external connection" in content providers that don't
14
 * A simple object that is used as the "external connection" in content
15
 * ask the user to create their own external connection. 
15
 * providers that don't ask the user to create their own external connection.
16
 * 
16
 * 
17
 * This is used whenever users don't specify a connection
17
 * This is used whenever users don't specify a connection
18
 * 
18
 * 
19
 * @author Del Myers
19
 * @author Del Myers
20
 */
20
 */
21
public final class EntityConnectionData {
21
public final class EntityConnectionData {
22
	public final Object source;
22
	public final Object source;
23
	public final Object dest;
23
	public final Object dest;
24
24
25
	/**
25
	/**
26
	 * Creates a new entity connection data.  The source and dest
26
	 * Creates a new entity connection data. The source and dest are users
27
	 * are users nodes.
27
	 * nodes.
28
	 */
28
	 */
29
	public EntityConnectionData(Object source, Object dest) {
29
	public EntityConnectionData(Object source, Object dest) {
30
		/*
30
		/*
31
		if (source == null) {
31
		 * if (source == null) { throw new
32
			throw new RuntimeException("Creating relationship with null source object");
32
		 * RuntimeException("Creating relationship with null source object"); }
33
		}
33
		 * if (dest == null) { throw new
34
		if (dest == null) {
34
		 * RuntimeException("Creating relationship with null dest object"); }
35
			throw new RuntimeException("Creating relationship with null dest object");
35
		 */
36
		}
36
		this.source = source;
37
		*/
37
		this.dest = dest;
38
		this.source = source;
38
	}
39
		this.dest = dest;
39
40
	}
40
	/*
41
41
	 * (non-Javadoc)
42
	/* (non-Javadoc)
42
	 * 
43
	 * @see java.lang.Object#equals(java.lang.Object)
43
	 * @see java.lang.Object#equals(java.lang.Object)
44
	 */
44
	 */
45
	public boolean equals(Object obj) {
45
	public boolean equals(Object obj) {
46
		if (!(obj instanceof EntityConnectionData)) {
46
		if (!(obj instanceof EntityConnectionData)) {
47
			return false;
47
			return false;
48
		}
48
		}
49
		EntityConnectionData that = (EntityConnectionData) obj;
49
		EntityConnectionData that = (EntityConnectionData) obj;
50
		return (this.source.equals(that.source) && this.dest.equals(that.dest));
50
		return (this.source.equals(that.source) && this.dest.equals(that.dest));
51
	}
51
	}
52
52
53
	/* (non-Javadoc)
53
	/*
54
	 * @see java.lang.Object#hashCode()
54
	 * (non-Javadoc)
55
	 */
55
	 * 
56
	public int hashCode() {
56
	 * @see java.lang.Object#hashCode()
57
		return this.source.hashCode() + this.dest.hashCode();
57
	 */
58
	}
58
	public int hashCode() {
59
}
59
		return this.source.hashCode() + this.dest.hashCode();
60
	}
61
}
(-)src/org/eclipse/zest/core/viewers/GraphViewer.java (-300 / +380 lines)
Lines 1-300 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials are made
3
 * All rights reserved. This program and the accompanying materials are made
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers;
10
package org.eclipse.zest.core.viewers;
11
11
12
import java.util.ArrayList;
12
import java.util.ArrayList;
13
import java.util.Iterator;
13
import java.util.Iterator;
14
import java.util.List;
14
import java.util.List;
15
15
16
import org.eclipse.jface.viewers.DoubleClickEvent;
16
import org.eclipse.jface.viewers.DoubleClickEvent;
17
import org.eclipse.jface.viewers.IContentProvider;
17
import org.eclipse.jface.viewers.IContentProvider;
18
import org.eclipse.jface.viewers.ISelection;
18
import org.eclipse.jface.viewers.ISelection;
19
import org.eclipse.jface.viewers.ISelectionChangedListener;
19
import org.eclipse.jface.viewers.ISelectionChangedListener;
20
import org.eclipse.jface.viewers.ISelectionProvider;
20
import org.eclipse.jface.viewers.ISelectionProvider;
21
import org.eclipse.jface.viewers.SelectionChangedEvent;
21
import org.eclipse.jface.viewers.SelectionChangedEvent;
22
import org.eclipse.swt.SWT;
22
import org.eclipse.swt.SWT;
23
import org.eclipse.swt.events.MouseEvent;
23
import org.eclipse.swt.events.MouseEvent;
24
import org.eclipse.swt.events.MouseListener;
24
import org.eclipse.swt.events.MouseListener;
25
import org.eclipse.swt.events.SelectionAdapter;
25
import org.eclipse.swt.events.SelectionAdapter;
26
import org.eclipse.swt.events.SelectionEvent;
26
import org.eclipse.swt.events.SelectionEvent;
27
import org.eclipse.swt.widgets.Composite;
27
import org.eclipse.swt.widgets.Composite;
28
import org.eclipse.swt.widgets.Control;
28
import org.eclipse.swt.widgets.Control;
29
import org.eclipse.swt.widgets.Widget;
29
import org.eclipse.swt.widgets.Widget;
30
import org.eclipse.zest.core.viewers.internal.AbstractStructuredGraphViewer;
30
import org.eclipse.zest.core.viewers.internal.GraphModelEntityFactory;
31
import org.eclipse.zest.core.viewers.internal.GraphModelEntityFactory;
31
import org.eclipse.zest.core.viewers.internal.GraphModelEntityRelationshipFactory;
32
import org.eclipse.zest.core.viewers.internal.GraphModelEntityRelationshipFactory;
32
import org.eclipse.zest.core.viewers.internal.GraphModelFactory;
33
import org.eclipse.zest.core.viewers.internal.GraphModelFactory;
33
import org.eclipse.zest.core.viewers.internal.IStylingGraphModelFactory;
34
import org.eclipse.zest.core.viewers.internal.IStylingGraphModelFactory;
34
import org.eclipse.zest.core.widgets.Graph;
35
import org.eclipse.zest.core.viewers.internal.ZoomManager;
35
import org.eclipse.zest.core.widgets.GraphItem;
36
import org.eclipse.zest.core.widgets.Graph;
36
import org.eclipse.zest.core.widgets.ZestStyles;
37
import org.eclipse.zest.core.widgets.GraphItem;
37
import org.eclipse.zest.layouts.LayoutAlgorithm;
38
import org.eclipse.zest.core.widgets.ZestStyles;
38
39
import org.eclipse.zest.layouts.LayoutAlgorithm;
39
/**
40
40
 * This view is used to represent a static graph. Static graphs can be layed
41
/*
41
 * out, but do not continually update their layout locations.
42
 * This view is used to represent a static graph. Static graphs can be layed
42
 * 
43
 * out, but do not continually update their layout locations.
43
 * @author Ian Bull
44
 * 
44
 * 
45
 * @author Ian Bull
45
 * @author Chris Callendar
46
 * 
46
 * 
47
 * @author Chris Callendar
47
 * @noextend This class is not intended to be subclassed by clients.
48
 */
48
 */
49
public class GraphViewer extends AbstractStructuredGraphViewer implements ISelectionProvider {
49
public class GraphViewer extends AbstractStructuredGraphViewer implements
50
50
		ISelectionProvider {
51
	protected Graph graph = null;
51
52
	private IStylingGraphModelFactory modelFactory = null;
52
	protected Graph graph = null;
53
	private List selectionChangedListeners = null;
53
	private IStylingGraphModelFactory modelFactory = null;
54
	ZoomManager zoomManager = null;
54
	private List selectionChangedListeners = null;
55
55
	ZoomManager zoomManager = null;
56
	/**
56
57
	 * Initializes the viewer.
57
	/**
58
	 * 
58
	 * Initializes the viewer.
59
	 * @param composite
59
	 * 
60
	 * @param style
60
	 * @param composite
61
	 *            the style for the viewer and for the layout algorithm
61
	 * @param style
62
	 * @see ZestStyles#LAYOUT_GRID
62
	 *            the style for the viewer and for the layout algorithm
63
	 * @see ZestStyles#LAYOUT_TREE
63
	 * @see ZestStyles#LAYOUT_GRID
64
	 * @see ZestStyles#LAYOUT_RADIAL
64
	 * @see ZestStyles#LAYOUT_TREE
65
	 * @see ZestStyles#LAYOUT_SPRING
65
	 * @see ZestStyles#LAYOUT_RADIAL
66
	 * @see ZestStyles#NO_OVERLAPPING_NODES
66
	 * @see ZestStyles#LAYOUT_SPRING
67
	 * @see ZestStyles#NODES_HIGHLIGHT_ADJACENT
67
	 * @see ZestStyles#NO_OVERLAPPING_NODES
68
	 * @see SWT#V_SCROLL
68
	 * @see ZestStyles#NODES_HIGHLIGHT_ADJACENT
69
	 * @see SWT#H_SCROLL
69
	 * @see SWT#V_SCROLL
70
	 */
70
	 * @see SWT#H_SCROLL
71
	public GraphViewer(Composite composite, int style) {
71
	 */
72
		super(style);
72
	public GraphViewer(Composite composite, int style) {
73
		this.graph = new Graph(composite, style);
73
		super(style);
74
		hookControl(this.graph);
74
		this.graph = new Graph(composite, style);
75
	}
75
		hookControl(this.graph);
76
76
	}
77
	public void setControl(Graph graphModel) {
77
78
		this.graph = graphModel;
78
	public void setControl(Graph graphModel) {
79
		hookControl(this.graph);
79
		this.graph = graphModel;
80
	}
80
		hookControl(this.graph);
81
81
	}
82
	protected void hookControl(Control control) {
82
83
		super.hookControl(control);
83
	protected void hookControl(Control control) {
84
84
		super.hookControl(control);
85
		selectionChangedListeners = new ArrayList();
85
86
		getGraphControl().addSelectionListener(new SelectionAdapter() {
86
		selectionChangedListeners = new ArrayList();
87
87
		getGraphControl().addSelectionListener(new SelectionAdapter() {
88
			public void widgetSelected(SelectionEvent e) {
88
89
				Iterator iterator = selectionChangedListeners.iterator();
89
			public void widgetSelected(SelectionEvent e) {
90
90
				Iterator iterator = selectionChangedListeners.iterator();
91
				ISelection structuredSelection = getSelection();
91
92
				SelectionChangedEvent event = new SelectionChangedEvent(GraphViewer.this, structuredSelection);
92
				ISelection structuredSelection = getSelection();
93
93
				SelectionChangedEvent event = new SelectionChangedEvent(
94
				while (iterator.hasNext()) {
94
						GraphViewer.this, structuredSelection);
95
					ISelectionChangedListener listener = (ISelectionChangedListener) iterator.next();
95
96
					listener.selectionChanged(event);
96
				while (iterator.hasNext()) {
97
				}
97
					ISelectionChangedListener listener = (ISelectionChangedListener) iterator
98
98
							.next();
99
			}
99
					listener.selectionChanged(event);
100
100
				}
101
		});
101
102
102
			}
103
		control.addMouseListener(new MouseListener() {
103
104
104
		});
105
			public void mouseDoubleClick(MouseEvent e) {
105
106
				DoubleClickEvent doubleClickEvent = new DoubleClickEvent(GraphViewer.this, getSelection());
106
		control.addMouseListener(new MouseListener() {
107
				fireDoubleClick(doubleClickEvent);
107
108
			}
108
			public void mouseDoubleClick(MouseEvent e) {
109
109
				DoubleClickEvent doubleClickEvent = new DoubleClickEvent(
110
			public void mouseDown(MouseEvent e) {
110
						GraphViewer.this, getSelection());
111
111
				fireDoubleClick(doubleClickEvent);
112
			}
112
			}
113
113
114
			public void mouseUp(MouseEvent e) {
114
			public void mouseDown(MouseEvent e) {
115
115
116
			}
116
			}
117
117
118
		});
118
			public void mouseUp(MouseEvent e) {
119
	}
119
120
120
			}
121
	/**
121
122
	 * Gets the styles for this structuredViewer
122
		});
123
	 * 
123
	}
124
	 * @return
124
125
	 */
125
	protected void inputChanged(Object input, Object oldInput) {
126
	public int getStyle() {
126
		graph.setDynamicLayout(false);
127
		return this.graph.getStyle();
127
		super.inputChanged(input, oldInput);
128
	}
128
		graph.setDynamicLayout(true);
129
129
		graph.applyLayout();
130
	/*
130
	}
131
	 * (non-Javadoc)
131
132
	 * 
132
	/**
133
	 * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#getGraphControl()
133
	 * Gets the styles for this structuredViewer
134
	 */
134
	 * 
135
	public Graph getGraphControl() {
135
	 * @return
136
		return super.getGraphControl();
136
	 */
137
	};
137
	public int getStyle() {
138
138
		return this.graph.getStyle();
139
	/**
139
	}
140
	 * Sets the layout algorithm to use for this viewer.
140
141
	 * 
141
	/*
142
	 * @param algorithm
142
	 * (non-Javadoc)
143
	 *            the algorithm to layout the nodes
143
	 * 
144
	 * @param runLayout
144
	 * @seeorg.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#
145
	 *            if the layout should be run
145
	 * getGraphControl()
146
	 */
146
	 */
147
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean runLayout) {
147
	public Graph getGraphControl() {
148
		graph.setLayoutAlgorithm(algorithm, runLayout);
148
		return super.getGraphControl();
149
	}
149
	};
150
150
151
	/*
151
	/**
152
	 * (non-Javadoc)
152
	 * Sets the layout algorithm to use for this viewer.
153
	 * 
153
	 * 
154
	 * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#setLayoutAlgorithm(org.eclipse.zest.layouts.LayoutAlgorithm)
154
	 * @param algorithm
155
	 */
155
	 *            the algorithm to layout the nodes
156
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm) {
156
	 * @param runLayout
157
		super.setLayoutAlgorithm(algorithm);
157
	 *            if the layout should be run
158
	}
158
	 */
159
159
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean runLayout) {
160
	/*
160
		graph.setLayoutAlgorithm(algorithm, runLayout);
161
	 * (non-Javadoc)
161
	}
162
	 * 
162
163
	 * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#setNodeStyle(int)
163
	/*
164
	 */
164
	 * (non-Javadoc)
165
	public void setNodeStyle(int nodeStyle) {
165
	 * 
166
		super.setNodeStyle(nodeStyle);
166
	 * @seeorg.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#
167
		this.graph.setNodeStyle(nodeStyle);
167
	 * setLayoutAlgorithm(org.eclipse.zest.layouts.LayoutAlgorithm)
168
	}
168
	 */
169
169
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm) {
170
	public void setContentProvider(IContentProvider contentProvider) {
170
		super.setLayoutAlgorithm(algorithm);
171
		if (contentProvider instanceof IGraphContentProvider) {
171
	}
172
			super.setContentProvider(contentProvider);
172
173
		} else if (contentProvider instanceof IGraphEntityContentProvider) {
173
	/*
174
			super.setContentProvider(contentProvider);
174
	 * (non-Javadoc)
175
		} else if (contentProvider instanceof IGraphEntityRelationshipContentProvider) {
175
	 * 
176
			super.setContentProvider(contentProvider);
176
	 * @see
177
		} else {
177
	 * org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#setNodeStyle
178
			throw new IllegalArgumentException("Invalid content provider, only IGraphContentProvider, IGraphEntityContentProvider, or IGraphEntityRelationshipContentProvider are supported.");
178
	 * (int)
179
		}
179
	 */
180
	}
180
	public void setNodeStyle(int nodeStyle) {
181
181
		super.setNodeStyle(nodeStyle);
182
	/**
182
		this.graph.setNodeStyle(nodeStyle);
183
	 * Finds the graph widget item for a given user model item.
183
	}
184
	 * 
184
185
	 * Note: This method returns an internal interface (GraphItem). You should
185
	public void setContentProvider(IContentProvider contentProvider) {
186
	 * be able to cast this to either a IGraphModelNode or IGraphModelConnection
186
		if (contentProvider instanceof IGraphContentProvider) {
187
	 * (which are also internal). These are internal because this API is not
187
			super.setContentProvider(contentProvider);
188
	 * stable. If use this method (to access internal nodes and edges), your
188
		} else if (contentProvider instanceof IGraphEntityContentProvider) {
189
	 * code may not compile between versions.
189
			super.setContentProvider(contentProvider);
190
	 * 
190
		} else if (contentProvider instanceof IGraphEntityRelationshipContentProvider) {
191
	 * @param The
191
			super.setContentProvider(contentProvider);
192
	 *            user model node.
192
		} else {
193
	 * @return An IGraphItem. This should be either a IGraphModelNode or
193
			throw new IllegalArgumentException(
194
	 *         IGraphModelConnection
194
					"Invalid content provider, only IGraphContentProvider, IGraphEntityContentProvider, or IGraphEntityRelationshipContentProvider are supported.");
195
	 */
195
		}
196
	public GraphItem findGraphItem(Object element) {
196
	}
197
		Widget[] result = findItems(element);
197
198
		return (result.length == 0 || !(result[0] instanceof GraphItem)) ? null : (GraphItem) result[0];
198
	/**
199
	}
199
	 * Finds the graph widget item for a given user model item.
200
200
	 * 
201
	/**
201
	 * Note: This method returns an internal interface (GraphItem). You should
202
	 * Applys the current layout to the viewer
202
	 * be able to cast this to either a IGraphModelNode or IGraphModelConnection
203
	 */
203
	 * (which are also internal). These are internal because this API is not
204
	public void applyLayout() {
204
	 * stable. If use this method (to access internal nodes and edges), your
205
		graph.applyLayout();
205
	 * code may not compile between versions.
206
	}
206
	 * 
207
207
	 * @param The
208
	protected void setSelectionToWidget(List l, boolean reveal) {
208
	 *            user model node.
209
		GraphItem[] listOfItems = findItems(l);
209
	 * @return An IGraphItem. This should be either a IGraphModelNode or
210
		graph.setSelection(listOfItems);
210
	 *         IGraphModelConnection
211
	}
211
	 */
212
212
	public GraphItem findGraphItem(Object element) {
213
	public Control getControl() {
213
		Widget[] result = findItems(element);
214
		return graph;
214
		return (result.length == 0 || !(result[0] instanceof GraphItem)) ? null
215
	}
215
				: (GraphItem) result[0];
216
216
	}
217
	public Object[] getNodeElements() {
217
218
		return super.getNodeElements();
218
	/**
219
	}
219
	 * Applys the current layout to the viewer
220
220
	 */
221
	public Object[] getConnectionElements() {
221
	public void applyLayout() {
222
		return super.getConnectionElements();
222
		graph.applyLayout();
223
	}
223
	}
224
224
225
	/*
225
	protected void setSelectionToWidget(List l, boolean reveal) {
226
	 * (non-Javadoc)
226
		GraphItem[] listOfItems = findItems(l);
227
	 * 
227
		graph.setSelection(listOfItems);
228
	 * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#reveal(java.lang.Object)
228
	}
229
	 */
229
230
	public void reveal(Object element) {
230
	public Control getControl() {
231
		super.reveal(element);
231
		return graph;
232
	}
232
	}
233
233
234
	/*
234
	public Object[] getNodeElements() {
235
	 * (non-Javadoc)
235
		return super.getNodeElements();
236
	 * 
236
	}
237
	 * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#setConnectionStyle(int)
237
238
	 */
238
	public Object[] getConnectionElements() {
239
	public void setConnectionStyle(int connectionStyle) {
239
		return super.getConnectionElements();
240
		super.setConnectionStyle(connectionStyle);
240
	}
241
	}
241
242
242
	/*
243
	/*
243
	 * (non-Javadoc)
244
	 * (non-Javadoc)
244
	 * 
245
	 * 
245
	 * @see
246
	 * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#unReveal(java.lang.Object)
246
	 * org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#reveal
247
	 */
247
	 * (java.lang.Object)
248
	public void unReveal(Object element) {
248
	 */
249
		super.unReveal(element);
249
	public void reveal(Object element) {
250
	}
250
		super.reveal(element);
251
251
	}
252
	public void addSelectionChangedListener(ISelectionChangedListener listener) {
252
253
		if (!selectionChangedListeners.contains(listener)) {
253
	/*
254
			selectionChangedListeners.add(listener);
254
	 * (non-Javadoc)
255
		}
255
	 * 
256
	}
256
	 * @seeorg.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#
257
257
	 * setConnectionStyle(int)
258
	public void removeSelectionChangedListener(ISelectionChangedListener listener) {
258
	 */
259
		if (selectionChangedListeners.contains(listener)) {
259
	public void setConnectionStyle(int connectionStyle) {
260
			selectionChangedListeners.remove(listener);
260
		super.setConnectionStyle(connectionStyle);
261
		}
261
	}
262
	}
262
263
263
	/*
264
	// @tag zest.bug.156286-Zooming.fix.experimental : expose the zoom manager
264
	 * (non-Javadoc)
265
	// for new actions.
265
	 * 
266
	protected ZoomManager getZoomManager() {
266
	 * @see
267
		if (zoomManager == null) {
267
	 * org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#unReveal
268
			zoomManager = new ZoomManager(getGraphControl().getRootLayer(), getGraphControl().getViewport());
268
	 * (java.lang.Object)
269
		}
269
	 */
270
		return zoomManager;
270
	public void unReveal(Object element) {
271
	}
271
		super.unReveal(element);
272
272
	}
273
	/*
273
274
	 * (non-Javadoc)
274
	public void addSelectionChangedListener(ISelectionChangedListener listener) {
275
	 * 
275
		if (!selectionChangedListeners.contains(listener)) {
276
	 * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#getFactory()
276
			selectionChangedListeners.add(listener);
277
	 */
277
		}
278
	protected IStylingGraphModelFactory getFactory() {
278
	}
279
		if (modelFactory == null) {
279
280
			if (getContentProvider() instanceof IGraphContentProvider) {
280
	public void removeSelectionChangedListener(
281
				modelFactory = new GraphModelFactory(this);
281
			ISelectionChangedListener listener) {
282
			} else if (getContentProvider() instanceof IGraphEntityContentProvider) {
282
		if (selectionChangedListeners.contains(listener)) {
283
				modelFactory = new GraphModelEntityFactory(this);
283
			selectionChangedListeners.remove(listener);
284
			} else if (getContentProvider() instanceof IGraphEntityRelationshipContentProvider) {
284
		}
285
				modelFactory = new GraphModelEntityRelationshipFactory(this);
285
	}
286
			}
286
287
		}
287
	/**
288
		return modelFactory;
288
	 * {@inheritDoc}
289
	}
289
	 * 
290
290
	 * NOTE: If a layout algorithm is set in the receiver, layout is performed
291
	/*
291
	 * after the refresh.
292
	 * (non-Javadoc)
292
	 */
293
	 * 
293
	public void refresh(Object element) {
294
	 * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#getLayoutAlgorithm()
294
		boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled();
295
	 */
295
		graph.setDynamicLayout(false);
296
	protected LayoutAlgorithm getLayoutAlgorithm() {
296
		super.refresh(element);
297
		return graph.getLayoutAlgorithm();
297
		graph.setDynamicLayout(dynamicLayoutEnabled);
298
	}
298
	}
299
299
300
}
300
	/**
301
	 * {@inheritDoc}
302
	 * 
303
	 * NOTE: If a layout algorithm is set in the receiver, layout is performed
304
	 * after the refresh.
305
	 */
306
	public void refresh(Object element, boolean updateLabels) {
307
		boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled();
308
		graph.setDynamicLayout(false);
309
		super.refresh(element, updateLabels);
310
		graph.setDynamicLayout(dynamicLayoutEnabled);
311
	}
312
313
	/**
314
	 * {@inheritDoc}
315
	 * 
316
	 * NOTE: If a layout algorithm is set in the receiver, layout is performed
317
	 * after the update.
318
	 */
319
	public void update(Object element, String[] properties) {
320
		boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled();
321
		graph.setDynamicLayout(false);
322
		super.update(element, properties);
323
		graph.setDynamicLayout(dynamicLayoutEnabled);
324
	}
325
326
	/**
327
	 * {@inheritDoc}
328
	 * 
329
	 * NOTE: If a layout algorithm is set in the receiver, layout is performed
330
	 * after the update.
331
	 */
332
	public void update(Object[] elements, String[] properties) {
333
		boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled();
334
		graph.setDynamicLayout(false);
335
		super.update(elements, properties);
336
		graph.setDynamicLayout(dynamicLayoutEnabled);
337
	}
338
339
	// @tag zest.bug.156286-Zooming.fix.experimental : expose the zoom manager
340
	// for new actions.
341
	protected ZoomManager getZoomManager() {
342
		if (zoomManager == null) {
343
			zoomManager = new ZoomManager(getGraphControl().getRootLayer(),
344
					getGraphControl().getViewport());
345
		}
346
		return zoomManager;
347
	}
348
349
	/**
350
	 * (non-Javadoc)
351
	 * 
352
	 * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#getFactory()
353
	 * @noreference This method is not intended to be referenced by clients.
354
	 * @nooverride This method is not intended to be re-implemented or extended
355
	 *             by clients.
356
	 */
357
	protected IStylingGraphModelFactory getFactory() {
358
		if (modelFactory == null) {
359
			if (getContentProvider() instanceof IGraphContentProvider) {
360
				modelFactory = new GraphModelFactory(this);
361
			} else if (getContentProvider() instanceof IGraphEntityContentProvider) {
362
				modelFactory = new GraphModelEntityFactory(this);
363
			} else if (getContentProvider() instanceof IGraphEntityRelationshipContentProvider) {
364
				modelFactory = new GraphModelEntityRelationshipFactory(this);
365
			}
366
		}
367
		return modelFactory;
368
	}
369
370
	/*
371
	 * (non-Javadoc)
372
	 * 
373
	 * @seeorg.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#
374
	 * getLayoutAlgorithm()
375
	 */
376
	protected LayoutAlgorithm getLayoutAlgorithm() {
377
		return graph.getLayoutAlgorithm();
378
	}
379
380
}
(-)src/org/eclipse/zest/core/viewers/IConnectionStyleBezierExtension.java (-67 / +76 lines)
Lines 1-67 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
/**
13
/**
14
 * An extension to the IConnectinStyleProvider that allows styling specific to
14
 * An extension to the IConnectinStyleProvider that allows styling specific to
15
 * bezier curves.
15
 * bezier curves.
16
 * 
16
 * 
17
 * Bezier curves are defined by a set of four points: two point in the layout
17
 * Bezier curves are defined by a set of four points: two point in the layout
18
 * (start and end), and two related control points (also start and end). The
18
 * (start and end), and two related control points (also start and end). The
19
 * control points are defined relative to their corresponding layout point.
19
 * control points are defined relative to their corresponding layout point. This
20
 * This definition includes an angle between the layout point and the line
20
 * definition includes an angle between the layout point and the line between
21
 * between the two layout points, as well as a ratio distance from the corresponding
21
 * the two layout points, as well as a ratio distance from the corresponding
22
 * layout point. The ratio distance is defined as a fraction between 0 and 1
22
 * layout point. The ratio distance is defined as a fraction between 0 and 1 of
23
 * of the distance between the two layout points. Using this definition
23
 * the distance between the two layout points. Using this definition allows
24
 * allows bezier curves to have a consistant look regardless of the actual
24
 * bezier curves to have a consistant look regardless of the actual positions of
25
 * positions of the nodes in the layouts. 
25
 * the nodes in the layouts.
26
 * @author Del Myers
26
 * 
27
 *
27
 * @author Del Myers
28
 */
28
 * 
29
//@tag bug(152530-Bezier(fix)) : users can style bezier curves.
29
 */
30
public interface IConnectionStyleBezierExtension {
30
// @tag bug(152530-Bezier(fix)) : users can style bezier curves.
31
31
public interface IConnectionStyleBezierExtension {
32
	/**
32
33
	 * Gets the angle between the start point, and the line between the start 
33
	/**
34
	 * and end, which will define the position of the start control point.
34
	 * Gets the angle between the start point, and the line between the start
35
	 * If the start angle, and the end angle are the same sign, the two control
35
	 * and end, which will define the position of the start control point. If
36
	 * points are guaranteed to be on the same side of the line.
36
	 * the start angle, and the end angle are the same sign, the two control
37
	 * @param rel the relationship to base on.
37
	 * points are guaranteed to be on the same side of the line.
38
	 * @return the start angle or <code>Double.NaN</code> for defaults.
38
	 * 
39
	 */
39
	 * @param rel
40
	double getStartAngle(Object rel);
40
	 *            the relationship to base on.
41
	
41
	 * @return the start angle or <code>Double.NaN</code> for defaults.
42
	/**
42
	 */
43
	 * Gets the angle between the end point, and the line between the start 
43
	double getStartAngle(Object rel);
44
	 * and end, which will define the position of the end control point.
44
45
	 * If the start angle, and the end angle are the same sign, the two control
45
	/**
46
	 * points are guaranteed to be on the same side of the line.
46
	 * Gets the angle between the end point, and the line between the start and
47
	 * @param rel the relationship to base on.
47
	 * end, which will define the position of the end control point. If the
48
	 * @return the end angle or <code>Double.NaN</code> for defaults.
48
	 * start angle, and the end angle are the same sign, the two control points
49
	 */
49
	 * are guaranteed to be on the same side of the line.
50
	double getEndAngle(Object rel);
50
	 * 
51
	
51
	 * @param rel
52
	/**
52
	 *            the relationship to base on.
53
	 * Gets the distance between the start point and the start control point,
53
	 * @return the end angle or <code>Double.NaN</code> for defaults.
54
	 * as a fraction of the distance between the start point and end point.
54
	 */
55
	 * @param rel the relationship to base on.
55
	double getEndAngle(Object rel);
56
	 * @return the start distance or <code>Double.NaN</code> for defaults.
56
57
	 */
57
	/**
58
	double getStartDistance(Object rel);
58
	 * Gets the distance between the start point and the start control point, as
59
	
59
	 * a fraction of the distance between the start point and end point.
60
	/**
60
	 * 
61
	 * Gets the distance between the end point and the end control point,
61
	 * @param rel
62
	 * as a fraction of the distance between the start point and end point.
62
	 *            the relationship to base on.
63
	 * @param rel the relationship to base on.
63
	 * @return the start distance or <code>Double.NaN</code> for defaults.
64
	 * @return the end distance or <code>Double.NaN</code> for defaults.
64
	 */
65
	 */
65
	double getStartDistance(Object rel);
66
	double getEndDistance(Object rel);
66
67
}
67
	/**
68
	 * Gets the distance between the end point and the end control point, as a
69
	 * fraction of the distance between the start point and end point.
70
	 * 
71
	 * @param rel
72
	 *            the relationship to base on.
73
	 * @return the end distance or <code>Double.NaN</code> for defaults.
74
	 */
75
	double getEndDistance(Object rel);
76
}
(-)src/org/eclipse/zest/core/viewers/IConnectionStyleProvider.java (-70 / +79 lines)
Lines 1-70 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
import org.eclipse.draw2d.IFigure;
13
import org.eclipse.draw2d.IFigure;
14
import org.eclipse.swt.graphics.Color;
14
import org.eclipse.swt.graphics.Color;
15
import org.eclipse.ui.services.IDisposable;
15
import org.eclipse.ui.services.IDisposable;
16
16
17
/**
17
/**
18
 * An extension to label providers, to supply styles for connections based upon 
18
 * An extension to label providers, to supply styles for connections based upon
19
 * relationships, rather than on connected nodes.
19
 * relationships, rather than on connected nodes.
20
 * @author Del Myers
20
 * 
21
 * @see #IGraphContentProvider
21
 * @author Del Myers
22
 * @see #IEntityStyleProvider
22
 * @see #IGraphContentProvider
23
 *
23
 * @see #IEntityStyleProvider
24
 */
24
 * 
25
//@tag bug(151327-Styles) : created to solve this bug
25
 */
26
public interface IConnectionStyleProvider extends IDisposable {
26
// @tag bug(151327-Styles) : created to solve this bug
27
	/**
27
public interface IConnectionStyleProvider extends IDisposable {
28
	 * Returns the style flags for this connection. Valid flags are those
28
	/**
29
	 * that begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check
29
	 * Returns the style flags for this connection. Valid flags are those that
30
	 * ZestStyles for legal combinations.
30
	 * begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check
31
	 * @param rel the relationship represented by this connection.
31
	 * ZestStyles for legal combinations.
32
	 * @return the style flags for this connection.
32
	 * 
33
	 * @see org.eclipse.zest.core.widgets.ZestStyles
33
	 * @param rel
34
	 */
34
	 *            the relationship represented by this connection.
35
	public int getConnectionStyle(Object rel);
35
	 * @return the style flags for this connection.
36
	
36
	 * @see org.eclipse.zest.core.widgets.ZestStyles
37
	/**
37
	 */
38
	 * Returns the color for the connection. Null for default.  Any resources created by this class must be disposed by
38
	public int getConnectionStyle(Object rel);
39
	 * this class.
39
40
	 * @param rel the relationship represented by this connection.
40
	/**
41
	 * @return the color.
41
	 * Returns the color for the connection. Null for default. Any resources
42
	 * @see #dispose()
42
	 * created by this class must be disposed by this class.
43
	 */
43
	 * 
44
	public Color getColor(Object rel);
44
	 * @param rel
45
	
45
	 *            the relationship represented by this connection.
46
	/**
46
	 * @return the color.
47
	 * Returns the highlighted color for this connection. Null for default.  Any resources created by this class must be disposed by
47
	 * @see #dispose()
48
	 * this class.
48
	 */
49
	 * @param rel the relationship represented by this connection.
49
	public Color getColor(Object rel);
50
	 * @return the highlighted color. Null for default.
50
51
	 * @see #dispose()
51
	/**
52
	 */
52
	 * Returns the highlighted color for this connection. Null for default. Any
53
	public Color getHighlightColor(Object rel);
53
	 * resources created by this class must be disposed by this class.
54
	
54
	 * 
55
	/**
55
	 * @param rel
56
	 * Returns the line width of the connection. -1 for default.
56
	 *            the relationship represented by this connection.
57
	 * @param rel the relationship represented by this connection.
57
	 * @return the highlighted color. Null for default.
58
	 * @return the line width for the connection. -1 for default.
58
	 * @see #dispose()
59
	 */
59
	 */
60
	public int getLineWidth(Object rel);
60
	public Color getHighlightColor(Object rel);
61
61
62
	/**
62
	/**
63
	 * Returns the tooltop for this node. If null is returned Zest will simply
63
	 * Returns the line width of the connection. -1 for default.
64
	 * use the default tooltip.
64
	 * 
65
	 * 
65
	 * @param rel
66
	 * @param entity
66
	 *            the relationship represented by this connection.
67
	 * @return
67
	 * @return the line width for the connection. -1 for default.
68
	 */
68
	 */
69
	public IFigure getTooltip(Object entity);
69
	public int getLineWidth(Object rel);
70
}
70
71
	/**
72
	 * Returns the tooltop for this node. If null is returned Zest will simply
73
	 * use the default tooltip.
74
	 * 
75
	 * @param entity
76
	 * @return
77
	 */
78
	public IFigure getTooltip(Object entity);
79
}
(-)src/org/eclipse/zest/core/viewers/IEntityConnectionStyleBezierExtension.java (-84 / +84 lines)
Lines 1-84 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
/**
13
/**
14
 * An extension to the IEntityConnectinStyleProvider that allows styling
14
 * An extension to the IEntityConnectinStyleProvider that allows styling
15
 * specific to bezier curves.
15
 * specific to bezier curves.
16
 * 
16
 * 
17
 * Bezier curves are defined by a set of four points: two point in the layout
17
 * Bezier curves are defined by a set of four points: two point in the layout
18
 * (start and end), and two related control points (also start and end). The
18
 * (start and end), and two related control points (also start and end). The
19
 * control points are defined relative to their corresponding layout point. This
19
 * control points are defined relative to their corresponding layout point. This
20
 * definition includes an angle between the layout point and the line between
20
 * definition includes an angle between the layout point and the line between
21
 * the two layout points, as well as a ratio distance from the corresponding
21
 * the two layout points, as well as a ratio distance from the corresponding
22
 * layout point. The ratio distance is defined as a fraction between 0 and 1 of
22
 * layout point. The ratio distance is defined as a fraction between 0 and 1 of
23
 * the distance between the two layout points. Using this definition allows
23
 * the distance between the two layout points. Using this definition allows
24
 * bezier curves to have a consistant look regardless of the actual positions of
24
 * bezier curves to have a consistant look regardless of the actual positions of
25
 * the nodes in the layouts.
25
 * the nodes in the layouts.
26
 * 
26
 * 
27
 * @author Del Myers
27
 * @author Del Myers
28
 * 
28
 * 
29
 */
29
 */
30
// @tag zest(bug(152530-Bezier(fix))) : users can style bezier curves.
30
// @tag zest(bug(152530-Bezier(fix))) : users can style bezier curves.
31
interface IEntityConnectionStyleBezierExtension {
31
interface IEntityConnectionStyleBezierExtension {
32
32
33
	/**
33
	/**
34
	 * Gets the angle between the start point, and the line between the start
34
	 * Gets the angle between the start point, and the line between the start
35
	 * and end, which will define the position of the start control point. If
35
	 * and end, which will define the position of the start control point. If
36
	 * the start angle, and the end angle are the same sign, the two control
36
	 * the start angle, and the end angle are the same sign, the two control
37
	 * points are guaranteed to be on the same side of the line.
37
	 * points are guaranteed to be on the same side of the line.
38
	 * 
38
	 * 
39
	 * @param source
39
	 * @param source
40
	 *            the source node to base on.
40
	 *            the source node to base on.
41
	 * @param dest
41
	 * @param dest
42
	 *            the destination node to base on.
42
	 *            the destination node to base on.
43
	 * @return the start angle or <code>Double.NaN</code> for defaults.
43
	 * @return the start angle or <code>Double.NaN</code> for defaults.
44
	 */
44
	 */
45
	double getStartAngle(Object source, Object dest);
45
	double getStartAngle(Object source, Object dest);
46
46
47
	/**
47
	/**
48
	 * Gets the angle between the end point, and the line between the start and
48
	 * Gets the angle between the end point, and the line between the start and
49
	 * end, which will define the position of the end control point. If the
49
	 * end, which will define the position of the end control point. If the
50
	 * start angle, and the end angle are the same sign, the two control points
50
	 * start angle, and the end angle are the same sign, the two control points
51
	 * are guaranteed to be on the same side of the line.
51
	 * are guaranteed to be on the same side of the line.
52
	 * 
52
	 * 
53
	 * @param source
53
	 * @param source
54
	 *            the source node to base on.
54
	 *            the source node to base on.
55
	 * @param dest
55
	 * @param dest
56
	 *            the destination node to base on.
56
	 *            the destination node to base on.
57
	 * @return the end angle or <code>Double.NaN</code> for defaults.
57
	 * @return the end angle or <code>Double.NaN</code> for defaults.
58
	 */
58
	 */
59
	double getEndAngle(Object source, Object dest);
59
	double getEndAngle(Object source, Object dest);
60
60
61
	/**
61
	/**
62
	 * Gets the distance between the start point and the start control point, as
62
	 * Gets the distance between the start point and the start control point, as
63
	 * a fraction of the distance between the start point and end point.
63
	 * a fraction of the distance between the start point and end point.
64
	 * 
64
	 * 
65
	 * @param source
65
	 * @param source
66
	 *            the source node to base on.
66
	 *            the source node to base on.
67
	 * @param dest
67
	 * @param dest
68
	 *            the destination node to base on.
68
	 *            the destination node to base on.
69
	 * @return the start distance or <code>Double.NaN</code> for defaults.
69
	 * @return the start distance or <code>Double.NaN</code> for defaults.
70
	 */
70
	 */
71
	double getStartDistance(Object source, Object dest);
71
	double getStartDistance(Object source, Object dest);
72
72
73
	/**
73
	/**
74
	 * Gets the distance between the end point and the end control point, as a
74
	 * Gets the distance between the end point and the end control point, as a
75
	 * fraction of the distance between the start point and end point.
75
	 * fraction of the distance between the start point and end point.
76
	 * 
76
	 * 
77
	 * @param source
77
	 * @param source
78
	 *            the source node to base on.
78
	 *            the source node to base on.
79
	 * @param dest
79
	 * @param dest
80
	 *            the destination node to base on.
80
	 *            the destination node to base on.
81
	 * @return the end distance or <code>Double.NaN</code> for defaults.
81
	 * @return the end distance or <code>Double.NaN</code> for defaults.
82
	 */
82
	 */
83
	double getEndDistance(Object source, Object dest);
83
	double getEndDistance(Object source, Object dest);
84
}
84
}
(-)src/org/eclipse/zest/core/viewers/IEntityConnectionStyleProvider.java (-73 / +86 lines)
Lines 1-73 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
import org.eclipse.draw2d.IFigure;
13
import org.eclipse.draw2d.IFigure;
14
import org.eclipse.swt.graphics.Color;
14
import org.eclipse.swt.graphics.Color;
15
import org.eclipse.ui.services.IDisposable;
15
import org.eclipse.ui.services.IDisposable;
16
16
17
/**
17
/**
18
 * An extension for label providers which allows users to set styles for connections
18
 * An extension for label providers which allows users to set styles for
19
 * that are based on entity end points.
19
 * connections that are based on entity end points.
20
 * @author Del Myers
20
 * 
21
 *
21
 * @author Del Myers
22
 */
22
 * 
23
//@tag bug(151327-Styles) : fix
23
 */
24
public interface IEntityConnectionStyleProvider extends IDisposable {
24
// @tag bug(151327-Styles) : fix
25
25
public interface IEntityConnectionStyleProvider extends IDisposable {
26
	/**
26
27
	 * Returns the style flags for this connection. Valid flags are those
27
	/**
28
	 * that begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check
28
	 * Returns the style flags for this connection. Valid flags are those that
29
	 * ZestStyles for legal combinations.
29
	 * begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check
30
	 * @param src the source entity.
30
	 * ZestStyles for legal combinations.
31
	 * @param dest the destination entity.
31
	 * 
32
	 * @return the style flags for this connection.
32
	 * @param src
33
	 * @see org.eclipse.zest.core.widgets.ZestStyles
33
	 *            the source entity.
34
	 */
34
	 * @param dest
35
	public int getConnectionStyle(Object src, Object dest);
35
	 *            the destination entity.
36
	
36
	 * @return the style flags for this connection.
37
	/**
37
	 * @see org.eclipse.zest.core.widgets.ZestStyles
38
	 * Returns the color for the connection. Null for default.
38
	 */
39
	 * @param src the source entity.  Any resources created by this class must be disposed by
39
	public int getConnectionStyle(Object src, Object dest);
40
	 * this class.
40
41
	 * @param dest the destination entity.
41
	/**
42
	 * @return the color.
42
	 * Returns the color for the connection. Null for default.
43
	 * @see #dispose()
43
	 * 
44
	 */
44
	 * @param src
45
	public Color getColor(Object src, Object dest);
45
	 *            the source entity. Any resources created by this class must be
46
	
46
	 *            disposed by this class.
47
	/**
47
	 * @param dest
48
	 * Returns the highlighted color for this connection. Null for default.
48
	 *            the destination entity.
49
	 * @param src the source entity.  Any resources created by this class must be disposed by
49
	 * @return the color.
50
	 * this class.
50
	 * @see #dispose()
51
	 * @param dest the destination entity.
51
	 */
52
	 * @return the highlighted color. Null for default.
52
	public Color getColor(Object src, Object dest);
53
	 * @see #dispose()
53
54
	 */
54
	/**
55
	public Color getHighlightColor(Object src, Object dest);
55
	 * Returns the highlighted color for this connection. Null for default.
56
	
56
	 * 
57
	/**
57
	 * @param src
58
	 * Returns the line width of the connection. -1 for default.
58
	 *            the source entity. Any resources created by this class must be
59
	 * @param src the source entity.
59
	 *            disposed by this class.
60
	 * @param dest the destination entity.
60
	 * @param dest
61
	 * @return the line width for the connection. -1 for default.
61
	 *            the destination entity.
62
	 */
62
	 * @return the highlighted color. Null for default.
63
	public int getLineWidth(Object src, Object dest);
63
	 * @see #dispose()
64
		
64
	 */
65
	/**
65
	public Color getHighlightColor(Object src, Object dest);
66
	 * Returns the tooltop for this node. If null is returned Zest will simply
66
67
	 * use the default tooltip.
67
	/**
68
	 * 
68
	 * Returns the line width of the connection. -1 for default.
69
	 * @param entity
69
	 * 
70
	 * @return
70
	 * @param src
71
	 */
71
	 *            the source entity.
72
	public IFigure getTooltip(Object entity);
72
	 * @param dest
73
}
73
	 *            the destination entity.
74
	 * @return the line width for the connection. -1 for default.
75
	 */
76
	public int getLineWidth(Object src, Object dest);
77
78
	/**
79
	 * Returns the tooltop for this node. If null is returned Zest will simply
80
	 * use the default tooltip.
81
	 * 
82
	 * @param entity
83
	 * @return
84
	 */
85
	public IFigure getTooltip(Object entity);
86
}
(-)src/org/eclipse/zest/core/viewers/IEntityStyleProvider.java (-131 / +131 lines)
Lines 1-131 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers;
10
package org.eclipse.zest.core.viewers;
11
11
12
import org.eclipse.draw2d.IFigure;
12
import org.eclipse.draw2d.IFigure;
13
import org.eclipse.swt.graphics.Color;
13
import org.eclipse.swt.graphics.Color;
14
import org.eclipse.ui.services.IDisposable;
14
import org.eclipse.ui.services.IDisposable;
15
15
16
/**
16
/**
17
 * An extension to Label providers for graphs. Gets specific details about the
17
 * An extension to Label providers for graphs. Gets specific details about the
18
 * style of an entity before it is created. This style provider offers:
18
 * style of an entity before it is created. This style provider offers:
19
 * 
19
 * 
20
 * -Background and forground colours -Hilighted and unhighlighted colours
20
 * -Background and forground colours -Hilighted and unhighlighted colours
21
 * (colours defined by selections). -Border color. -Highlighted and
21
 * (colours defined by selections). -Border color. -Highlighted and
22
 * unhighlighted colours for borders. -Border width -Font for text inside the
22
 * unhighlighted colours for borders. -Border width -Font for text inside the
23
 * entity.
23
 * entity.
24
 * 
24
 * 
25
 * Any method may return null if the Zest defaults are preferred.
25
 * Any method may return null if the Zest defaults are preferred.
26
 * 
26
 * 
27
 * NOTE: It is up to the implementors of this interface to dispose of any Colors
27
 * NOTE: It is up to the implementors of this interface to dispose of any Colors
28
 * or Fonts that are created by this class. The dispose() method will be called
28
 * or Fonts that are created by this class. The dispose() method will be called
29
 * at the end of the entity's life-cycle so that this class may dispose of its
29
 * at the end of the entity's life-cycle so that this class may dispose of its
30
 * resources.
30
 * resources.
31
 * 
31
 * 
32
 * @author Del Myers
32
 * @author Del Myers
33
 * @see org.eclipse.jface.viewers.IColorProvider
33
 * @see org.eclipse.jface.viewers.IColorProvider
34
 * @tag bug(151327-Styles) : created to solve this bug
34
 * @tag bug(151327-Styles) : created to solve this bug
35
 */
35
 */
36
public interface IEntityStyleProvider extends IDisposable {
36
public interface IEntityStyleProvider extends IDisposable {
37
37
38
	/**
38
	/**
39
	 * Returns the forground colour of this entity. May return null for
39
	 * Returns the forground colour of this entity. May return null for
40
	 * defaults. Any resources created by this class must be disposed by this
40
	 * defaults. Any resources created by this class must be disposed by this
41
	 * class.
41
	 * class.
42
	 * 
42
	 * 
43
	 * @param entity
43
	 * @param entity
44
	 *            the entity to be styled.
44
	 *            the entity to be styled.
45
	 * @return the forground colour of this entity.
45
	 * @return the forground colour of this entity.
46
	 * @see #dispose()
46
	 * @see #dispose()
47
	 */
47
	 */
48
	public Color getNodeHighlightColor(Object entity);
48
	public Color getNodeHighlightColor(Object entity);
49
49
50
	/**
50
	/**
51
	 * Returns the background colour for this entity. May return null for
51
	 * Returns the background colour for this entity. May return null for
52
	 * defaults. Any resources created by this class must be disposed by this
52
	 * defaults. Any resources created by this class must be disposed by this
53
	 * class.
53
	 * class.
54
	 * 
54
	 * 
55
	 * @param entity
55
	 * @param entity
56
	 *            the entity to be styled.
56
	 *            the entity to be styled.
57
	 * @return the background colour for this entity.
57
	 * @return the background colour for this entity.
58
	 * @see #dispose()
58
	 * @see #dispose()
59
	 */
59
	 */
60
	public Color getBorderColor(Object entity);
60
	public Color getBorderColor(Object entity);
61
61
62
	/**
62
	/**
63
	 * Returns the border highlight colour for this entity. May return null for
63
	 * Returns the border highlight colour for this entity. May return null for
64
	 * defaults. Any resources created by this class must be disposed by this
64
	 * defaults. Any resources created by this class must be disposed by this
65
	 * class.
65
	 * class.
66
	 * 
66
	 * 
67
	 * @param entity
67
	 * @param entity
68
	 *            the entity to be styled.
68
	 *            the entity to be styled.
69
	 * @return the border highlight colour for this entity.
69
	 * @return the border highlight colour for this entity.
70
	 * @see #dispose()
70
	 * @see #dispose()
71
	 */
71
	 */
72
	public Color getBorderHighlightColor(Object entity);
72
	public Color getBorderHighlightColor(Object entity);
73
73
74
	/**
74
	/**
75
	 * Returns the border width for this entity. May return -1 for defaults.
75
	 * Returns the border width for this entity. May return -1 for defaults.
76
	 * 
76
	 * 
77
	 * @param entity
77
	 * @param entity
78
	 *            the entity to be styled.
78
	 *            the entity to be styled.
79
	 * @return the border width, or -1 for defaults.
79
	 * @return the border width, or -1 for defaults.
80
	 */
80
	 */
81
	public int getBorderWidth(Object entity);
81
	public int getBorderWidth(Object entity);
82
82
83
	/**
83
	/**
84
	 * Returns true iff the adjacent entities should be highlighted when this
84
	 * Returns true iff the adjacent entities should be highlighted when this
85
	 * node is selected. Zest's default action is true.
85
	 * node is selected. Zest's default action is true.
86
	 * 
86
	 * 
87
	 * @return true iff the adjacent entities should be highlighted when this
87
	 * @return true iff the adjacent entities should be highlighted when this
88
	 *         node is selected.
88
	 *         node is selected.
89
	 */
89
	 */
90
	// @tag ADJACENT : Removed highlight adjacent
90
	// @tag ADJACENT : Removed highlight adjacent
91
	//public boolean highlightAdjacentEntities(Object entity);
91
	// public boolean highlightAdjacentEntities(Object entity);
92
	/**
92
	/**
93
	 * Returns the color that adjacent entities will be drawn when this entity
93
	 * Returns the color that adjacent entities will be drawn when this entity
94
	 * is selected. Will be ignored if HighlightAdjacentEntities() returns
94
	 * is selected. Will be ignored if HighlightAdjacentEntities() returns
95
	 * false. May return null for defaults. Any resources created by this class
95
	 * false. May return null for defaults. Any resources created by this class
96
	 * must be disposed by this class.
96
	 * must be disposed by this class.
97
	 * 
97
	 * 
98
	 * @param entity
98
	 * @param entity
99
	 *            the entity to be styled.
99
	 *            the entity to be styled.
100
	 * @return the color for adjacent entities.
100
	 * @return the color for adjacent entities.
101
	 * @see #highlightAdjacentEntities(Object entity)
101
	 * @see #highlightAdjacentEntities(Object entity)
102
	 * @see #dispose()
102
	 * @see #dispose()
103
	 */
103
	 */
104
	// @tag ADJACENT : Removed highlight adjacent
104
	// @tag ADJACENT : Removed highlight adjacent
105
	//public Color getAdjacentEntityHighlightColor(Object entity);
105
	// public Color getAdjacentEntityHighlightColor(Object entity);
106
	/**
106
	/**
107
	 * Returns the colour that this node should be coloured. This will be
107
	 * Returns the colour that this node should be coloured. This will be
108
	 * ignored if getNodeColour returns null. Any resources created by this
108
	 * ignored if getNodeColour returns null. Any resources created by this
109
	 * class must be diposed by this class.
109
	 * class must be diposed by this class.
110
	 * 
110
	 * 
111
	 * @param entity
111
	 * @param entity
112
	 *            The entity to be styled
112
	 *            The entity to be styled
113
	 * @return The colour for the node
113
	 * @return The colour for the node
114
	 * @see #dispose()
114
	 * @see #dispose()
115
	 */
115
	 */
116
	public Color getBackgroundColour(Object entity);
116
	public Color getBackgroundColour(Object entity);
117
117
118
	public Color getForegroundColour(Object entity);
118
	public Color getForegroundColour(Object entity);
119
119
120
	/**
120
	/**
121
	 * Returns the tooltop for this node. If null is returned Zest will simply
121
	 * Returns the tooltop for this node. If null is returned Zest will simply
122
	 * use the default tooltip.
122
	 * use the default tooltip.
123
	 * 
123
	 * 
124
	 * @param entity
124
	 * @param entity
125
	 * @return
125
	 * @return
126
	 */
126
	 */
127
	public IFigure getTooltip(Object entity);
127
	public IFigure getTooltip(Object entity);
128
128
129
	public boolean fisheyeNode(Object entity);
129
	public boolean fisheyeNode(Object entity);
130
130
131
}
131
}
(-)src/org/eclipse/zest/core/viewers/IFigureProvider.java (-16 / +25 lines)
Lines 1-16 Link Here
1
package org.eclipse.zest.core.viewers;
1
/******************************************************************************* 
2
2
 * Copyright (c) 2009-2010 EclipseSource and others. All rights reserved. This
3
import org.eclipse.draw2d.IFigure;
3
 * program and the accompanying materials are made available under the terms of
4
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
/**
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * Allows a user to create a figure for an element in 
6
 *
7
 * graph model.  To use this interface, it should 
7
 * Contributors:
8
 * be implemented and passed to {@link GraphViewer#setLabelProvider()}
8
 *   EclipseSource - initial API and implementation
9
 */
9
 ******************************************************************************/
10
public interface IFigureProvider {
10
package org.eclipse.zest.core.viewers;
11
11
12
	/**
12
import org.eclipse.draw2d.IFigure;
13
	 * Creates a custom figure for a graph model element
13
14
	 */
14
/**
15
	public IFigure getFigure(Object element);
15
 * Allows a user to create a figure for an element in graph model. To use this
16
}
16
 * interface, it should be implemented and passed to
17
 * {@link GraphViewer#setLabelProvider()}
18
 */
19
public interface IFigureProvider {
20
21
	/**
22
	 * Creates a custom figure for a graph model element
23
	 */
24
	public IFigure getFigure(Object element);
25
}
(-)src/org/eclipse/zest/core/viewers/IGraphContentProvider.java (-51 / +58 lines)
Lines 1-51 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
import org.eclipse.jface.viewers.IStructuredContentProvider;
13
import org.eclipse.jface.viewers.IStructuredContentProvider;
14
14
15
/**
15
/**
16
 * A graph content provider. 
16
 * A graph content provider.
17
 * 
17
 * 
18
 * @author Ian Bull
18
 * @author Ian Bull
19
 */
19
 */
20
public interface IGraphContentProvider extends IStructuredContentProvider {
20
public interface IGraphContentProvider extends IStructuredContentProvider {
21
21
22
	/**
22
	/**
23
	 * Gets the source Object for the given relationship. Note, at least one of the source
23
	 * Gets the source Object for the given relationship. Note, at least one of
24
	 * or destination must not be null. If both are null, then nothing can be displayed in
24
	 * the source or destination must not be null. If both are null, then
25
	 * the graph (a relationship cannot exist without nodes to be connected to). However,
25
	 * nothing can be displayed in the graph (a relationship cannot exist
26
	 * if one of getSource() or getDestination() returns null, then the resulting graph will
26
	 * without nodes to be connected to). However, if one of getSource() or
27
	 * contain an unconnected node for the non-null object returned from the other method.
27
	 * getDestination() returns null, then the resulting graph will contain an
28
	 * @param rel the relationship.
28
	 * unconnected node for the non-null object returned from the other method.
29
	 * @return the source, or null for an unconnected destination.
29
	 * 
30
	 */
30
	 * @param rel
31
	public Object getSource(Object rel);
31
	 *            the relationship.
32
32
	 * @return the source, or null for an unconnected destination.
33
	/**
33
	 */
34
	 * Gets the target Object for the given relationship. Note, at least one of the source
34
	public Object getSource(Object rel);
35
	 * or destination must not be null. If both are null, then nothing can be displayed in
35
36
	 * the graph (a relationship cannot exist without nodes to be connected to). However,
36
	/**
37
	 * if one of getSource() or getDestination() returns null, then the resulting graph will
37
	 * Gets the target Object for the given relationship. Note, at least one of
38
	 * contain an unconnected node for the non-null object returned from the other method.
38
	 * the source or destination must not be null. If both are null, then
39
	 * @param rel the relationship.
39
	 * nothing can be displayed in the graph (a relationship cannot exist
40
	 * @return the destination, or null for an unconnected source.
40
	 * without nodes to be connected to). However, if one of getSource() or
41
	 */
41
	 * getDestination() returns null, then the resulting graph will contain an
42
	public Object getDestination(Object rel);
42
	 * unconnected node for the non-null object returned from the other method.
43
43
	 * 
44
	/**
44
	 * @param rel
45
	 * Returns all the relationships in the graph for the given input.
45
	 *            the relationship.
46
	 * @input the input model object.
46
	 * @return the destination, or null for an unconnected source.
47
	 * @return all the relationships in the graph for the given input.
47
	 */
48
	 */
48
	public Object getDestination(Object rel);
49
	public Object[] getElements(Object input);
49
50
50
	/**
51
}
51
	 * Returns all the relationships in the graph for the given input.
52
	 * 
53
	 * @input the input model object.
54
	 * @return all the relationships in the graph for the given input.
55
	 */
56
	public Object[] getElements(Object input);
57
58
}
(-)src/org/eclipse/zest/core/viewers/IGraphEntityContentProvider.java (-31 / +32 lines)
Lines 1-31 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
import org.eclipse.jface.viewers.IStructuredContentProvider;
13
import org.eclipse.jface.viewers.IStructuredContentProvider;
14
14
15
/**
15
/**
16
 * 
16
 * 
17
 * @author Ian Bull
17
 * @author Ian Bull
18
 *
18
 * 
19
 */
19
 */
20
public interface IGraphEntityContentProvider extends IStructuredContentProvider {
20
public interface IGraphEntityContentProvider extends IStructuredContentProvider {
21
21
22
	public Object[] getElements(Object inputElement);
22
	public Object[] getElements(Object inputElement);
23
23
24
	/**
24
	/**
25
	 * Gets the elements this object is connected to
25
	 * Gets the elements this object is connected to
26
	 * @param entity
26
	 * 
27
	 * @return
27
	 * @param entity
28
	 */
28
	 * @return
29
	public Object[] getConnectedTo(Object entity);
29
	 */
30
30
	public Object[] getConnectedTo(Object entity);
31
}
31
32
}
(-)src/org/eclipse/zest/core/viewers/IGraphEntityRelationshipContentProvider.java (-32 / +39 lines)
Lines 1-32 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
import org.eclipse.jface.viewers.IStructuredContentProvider;
13
import org.eclipse.jface.viewers.IStructuredContentProvider;
14
14
15
/**
15
/**
16
 * A content provider that is node-relationship centric. Call-backs return model
16
 * A content provider that is node-relationship centric. Call-backs return model
17
 * nodes to the user, and ask for relationships. Both nodes and relationships are
17
 * nodes to the user, and ask for relationships. Both nodes and relationships
18
 * represented by the user's model.
18
 * are represented by the user's model.
19
 * @author Del Myers
19
 * 
20
 *
20
 * @author Del Myers
21
 */
21
 * 
22
//@tag bug.154580-Content.fix : new content provider that returns relationships for the given source and destination.
22
 */
23
public interface IGraphEntityRelationshipContentProvider extends IStructuredContentProvider {
23
// @tag bug.154580-Content.fix : new content provider that returns relationships
24
	/**
24
// for the given source and destination.
25
	 * Gets the relationships between the given source and destination nodes.
25
public interface IGraphEntityRelationshipContentProvider extends
26
	 * @param source the source node.
26
		IStructuredContentProvider {
27
	 * @param dest the destination node.
27
	/**
28
	 * @return objects represtenting the different relationships between the nodes.
28
	 * Gets the relationships between the given source and destination nodes.
29
	 */
29
	 * 
30
	public Object[] getRelationships(Object source, Object dest);
30
	 * @param source
31
31
	 *            the source node.
32
}
32
	 * @param dest
33
	 *            the destination node.
34
	 * @return objects represtenting the different relationships between the
35
	 *         nodes.
36
	 */
37
	public Object[] getRelationships(Object source, Object dest);
38
39
}
(-)src/org/eclipse/zest/core/viewers/INestedContentProvider.java (-41 / +43 lines)
Lines 1-41 Link Here
1
/*******************************************************************************
1
/******************************************************************************* 
2
 * Copyright (c) 2000, 2008 IBM Corporation and others. All rights reserved.
2
 * Copyright (c) 2009-2010 EclipseSource and others. All rights reserved. This
3
 * This program and the accompanying materials are made available under the
3
 * program and the accompanying materials are made available under the terms of
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
6
 *
7
 * Contributors: IBM Corporation - initial API and implementation Chisel Group,
7
 * Contributors:
8
 * University of Victoria
8
 *   EclipseSource - initial API and implementation
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers;
10
package org.eclipse.zest.core.viewers;
11
11
12
/*
12
/**
13
 * A content provider for nested graphs. Any entity based content provider
13
 * A content provider for nested graphs. Any entity based content provider
14
 * (IGraphEntityContentProvider or IGraphEntityRelationshipContentProvider) can
14
 * (IGraphEntityContentProvider or IGraphEntityRelationshipContentProvider) can
15
 * also implement this interface. Any node that "hasChildren" will be rendered
15
 * also implement this interface. Any node that "hasChildren" will be rendered
16
 * as a container.
16
 * as a container.
17
 * 
17
 * 
18
 * Note: Containers cannot contain other containers.
18
 * Note: Containers cannot contain other containers.
19
 * 
19
 * 
20
 * @author irbull
20
 * @author irbull
21
 */
21
 */
22
public interface INestedContentProvider {
22
public interface INestedContentProvider {
23
23
24
	/**
24
	/**
25
	 * Does the current node have children?  If so, it will be rendered as a
25
	 * Does the current node have children? If so, it will be rendered as a
26
	 * container.
26
	 * container.
27
	 * 
27
	 * 
28
	 * @param element The current node
28
	 * @param element
29
	 * @return True if it has children, false otherwise
29
	 *            The current node
30
	 */
30
	 * @return True if it has children, false otherwise
31
	public boolean hasChildren(Object element);
31
	 */
32
32
	public boolean hasChildren(Object element);
33
	/**
33
34
	 * Gets the children of this node.  This method will not be called
34
	/**
35
	 * if hasChildren returns false.
35
	 * Gets the children of this node. This method will not be called if
36
	 * 
36
	 * hasChildren returns false.
37
	 * @param element The current node
37
	 * 
38
	 * @return The list of children for this node.  
38
	 * @param element
39
	 */
39
	 *            The current node
40
	public Object[] getChildren(Object element);
40
	 * @return The list of children for this node.
41
}
41
	 */
42
	public Object[] getChildren(Object element);
43
}
(-)src/org/eclipse/zest/core/viewers/ISelfStyleProvider.java (-32 / +32 lines)
Lines 1-32 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2009 EclipseSource and others. All rights reserved. This
2
 * Copyright (c) 2009-2010 EclipseSource and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
6
 * 
7
 * Contributors: EclipseSource - initial API and implementation
7
 * Contributors: EclipseSource - initial API and implementation
8
 ******************************************************************************/
8
 ******************************************************************************/
9
package org.eclipse.zest.core.viewers;
9
package org.eclipse.zest.core.viewers;
10
10
11
import org.eclipse.zest.core.widgets.GraphConnection;
11
import org.eclipse.zest.core.widgets.GraphConnection;
12
import org.eclipse.zest.core.widgets.GraphNode;
12
import org.eclipse.zest.core.widgets.GraphNode;
13
13
14
/**
14
/**
15
 * Provides a mechanism to style nodes and edges when they are created. 
15
 * Provides a mechanism to style nodes and edges when they are created.
16
 * 
16
 * 
17
 * After each node or edge is created, the self styling method will be called with both 
17
 * After each node or edge is created, the self styling method will be called
18
 * the element and the widget. 
18
 * with both the element and the widget.
19
 */
19
 */
20
public interface ISelfStyleProvider {
20
public interface ISelfStyleProvider {
21
21
22
	/**
22
	/**
23
	 * Styles a connection
23
	 * Styles a connection
24
	 */
24
	 */
25
	public void selfStyleConnection(Object element, GraphConnection connection);
25
	public void selfStyleConnection(Object element, GraphConnection connection);
26
26
27
	/**
27
	/**
28
	 * Styles a node
28
	 * Styles a node
29
	 */
29
	 */
30
	public void selfStyleNode(Object element, GraphNode node);
30
	public void selfStyleNode(Object element, GraphNode node);
31
31
32
}
32
}
(-)src/org/eclipse/zest/core/viewers/IZoomableWorkbenchPart.java (-27 / +28 lines)
Lines 1-27 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers;
11
package org.eclipse.zest.core.viewers;
12
12
13
13
/**
14
/**
14
 * An interface that can be added to IWorkbenchParts based on ZEST views so that
15
 * An interface that can be added to IWorkbenchParts based on ZEST views so that zooming
15
 * zooming is supported.
16
 * is supported.
16
 * 
17
 * @author Del Myers
17
 * @author Del Myers
18
 *
18
 * 
19
 */
19
 */
20
//@tag bug.156286-Zooming.fix : experimental
20
// @tag bug.156286-Zooming.fix : experimental
21
public interface IZoomableWorkbenchPart {
21
public interface IZoomableWorkbenchPart {
22
	/**
22
	/**
23
	 * Returns the viewer that is zoomable.
23
	 * Returns the viewer that is zoomable.
24
	 * @return the viewer that is zoomable.
24
	 * 
25
	 */
25
	 * @return the viewer that is zoomable.
26
	AbstractZoomableViewer getZoomableViewer();
26
	 */
27
}
27
	AbstractZoomableViewer getZoomableViewer();
28
}
(-)src/org/eclipse/zest/core/viewers/ZoomContributionViewItem.java (-241 / +247 lines)
Lines 1-241 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers;
10
package org.eclipse.zest.core.viewers;
11
11
12
import org.eclipse.jface.action.ContributionItem;
12
import org.eclipse.jface.action.ContributionItem;
13
import org.eclipse.zest.core.viewers.internal.ZoomListener;
13
import org.eclipse.swt.SWT;
14
import org.eclipse.zest.core.viewers.internal.ZoomManager;
14
import org.eclipse.swt.events.MenuAdapter;
15
import org.eclipse.swt.SWT;
15
import org.eclipse.swt.events.MenuEvent;
16
import org.eclipse.swt.events.MenuAdapter;
16
import org.eclipse.swt.events.SelectionAdapter;
17
import org.eclipse.swt.events.MenuEvent;
17
import org.eclipse.swt.events.SelectionEvent;
18
import org.eclipse.swt.events.SelectionAdapter;
18
import org.eclipse.swt.widgets.Combo;
19
import org.eclipse.swt.events.SelectionEvent;
19
import org.eclipse.swt.widgets.Composite;
20
import org.eclipse.swt.widgets.Combo;
20
import org.eclipse.swt.widgets.CoolBar;
21
import org.eclipse.swt.widgets.Composite;
21
import org.eclipse.swt.widgets.CoolItem;
22
import org.eclipse.swt.widgets.CoolBar;
22
import org.eclipse.swt.widgets.Menu;
23
import org.eclipse.swt.widgets.CoolItem;
23
import org.eclipse.swt.widgets.MenuItem;
24
import org.eclipse.swt.widgets.Menu;
24
import org.eclipse.swt.widgets.ToolBar;
25
import org.eclipse.swt.widgets.MenuItem;
25
import org.eclipse.swt.widgets.ToolItem;
26
import org.eclipse.swt.widgets.ToolBar;
26
27
import org.eclipse.swt.widgets.ToolItem;
27
/**
28
28
 * A contribution item that adds a combo to a toolbar or coolbar, or a list of
29
/**
29
 * zooms to a menu. Can only be used for one toolbar, coolbar, or menu.
30
 * A contribution item that adds a combo to a toolbar or coolbar, or a list of
30
 * 
31
 * zooms to a menu. Can only be used for one toolbar, coolbar, or menu.
31
 * In order to use this item, let your workbench part implement
32
 * 
32
 * IZoomableWorkbenchPart. If the workbench part then supplies a viewer that is
33
 * In order to use this item, let your workbench part implement
33
 * zoomable, the combo or menu created by this item will be enabled.
34
 * IZoomableWorkbenchPart. If the workbench part then supplies a viewer that is
34
 * 
35
 * zoomable, the combo or menu created by this item will be enabled.
35
 * @author Del Myers
36
 * 
36
 * 
37
 * @author Del Myers
37
 */
38
 * 
38
// @tag zest.bug.156286-Zooming.fix : create a contribution item that can set
39
 */
39
// zooming on Zest views.
40
//@tag zest.bug.156286-Zooming.fix : create a contribution item that can set zooming on Zest views.
40
public class ZoomContributionViewItem extends ContributionItem implements
41
public class ZoomContributionViewItem extends ContributionItem implements ZoomListener {
41
		ZoomListener {
42
	/**
42
	/**
43
	 * Zooms to fit the width.
43
	 * Zooms to fit the width.
44
	 */
44
	 */
45
	public static final String FIT_WIDTH = ZoomManager.FIT_WIDTH;
45
	public static final String FIT_WIDTH = ZoomManager.FIT_WIDTH;
46
	/**
46
	/**
47
	 * Zooms to fit the height.
47
	 * Zooms to fit the height.
48
	 */
48
	 */
49
	public static final String FIT_HEIGHT = ZoomManager.FIT_HEIGHT;
49
	public static final String FIT_HEIGHT = ZoomManager.FIT_HEIGHT;
50
	/**
50
	/**
51
	 * Zooms to fit entirely within the viewport.
51
	 * Zooms to fit entirely within the viewport.
52
	 */
52
	 */
53
	public static final String FIT_ALL = ZoomManager.FIT_ALL;
53
	public static final String FIT_ALL = ZoomManager.FIT_ALL;
54
54
55
	private String[] zoomLevels;
55
	private String[] zoomLevels;
56
	private ZoomManager zoomManager;
56
	private ZoomManager zoomManager;
57
	private Combo combo;
57
	private Combo combo;
58
	private Menu fMenu;
58
	private Menu fMenu;
59
	private MenuAdapter menuAdapter = new MenuAdapter() {
59
	private MenuAdapter menuAdapter = new MenuAdapter() {
60
		public void menuShown(MenuEvent e) {
60
		public void menuShown(MenuEvent e) {
61
			refresh(true);
61
			refresh(true);
62
		}
62
		}
63
	};
63
	};
64
64
65
	/**
65
	/**
66
	 * Creates a new contribution item that will work on the given part
66
	 * Creates a new contribution item that will work on the given part
67
	 * service.initialZooms will be used to populate the combo or the menu.
67
	 * service.initialZooms will be used to populate the combo or the menu.
68
	 * Valid values for initialZooms are percentage numbers (e.g., "100%"), or
68
	 * Valid values for initialZooms are percentage numbers (e.g., "100%"), or
69
	 * FIT_WIDTH, FIT_HEIGHT, FIT_ALL.
69
	 * FIT_WIDTH, FIT_HEIGHT, FIT_ALL.
70
	 * 
70
	 * 
71
	 * @param partService
71
	 * @param partService
72
	 *            service used to see whether the view is zoomable.
72
	 *            service used to see whether the view is zoomable.
73
	 */
73
	 */
74
	public ZoomContributionViewItem(IZoomableWorkbenchPart part) {
74
	public ZoomContributionViewItem(IZoomableWorkbenchPart part) {
75
		zoomManager = part.getZoomableViewer().getZoomManager();
75
		zoomManager = part.getZoomableViewer().getZoomManager();
76
	}
76
	}
77
77
78
	/*
78
	/*
79
	 * (non-Javadoc)
79
	 * (non-Javadoc)
80
	 * 
80
	 * 
81
	 * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.Menu,
81
	 * @see
82
	 *      int)
82
	 * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets
83
	 */
83
	 * .Menu, int)
84
	public void fill(Menu menu, int index) {
84
	 */
85
		if (this.fMenu == null || this.fMenu != menu) {
85
	public void fill(Menu menu, int index) {
86
			if (this.fMenu != null) {
86
		if (this.fMenu == null || this.fMenu != menu) {
87
				this.fMenu.removeMenuListener(menuAdapter);
87
			if (this.fMenu != null) {
88
				this.fMenu = null;
88
				this.fMenu.removeMenuListener(menuAdapter);
89
			}
89
				this.fMenu = null;
90
			this.fMenu = menu;
90
			}
91
			menu.addMenuListener(menuAdapter);
91
			this.fMenu = menu;
92
		}
92
			menu.addMenuListener(menuAdapter);
93
	}
93
		}
94
94
	}
95
	/*
95
96
	 * (non-Javadoc)
96
	/*
97
	 * 
97
	 * (non-Javadoc)
98
	 * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.CoolBar,
98
	 * 
99
	 *      int)
99
	 * @see
100
	 */
100
	 * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets
101
	public void fill(CoolBar parent, int index) {
101
	 * .CoolBar, int)
102
		CoolItem item = new CoolItem(parent, SWT.DROP_DOWN);
102
	 */
103
		Combo combo = createCombo(parent);
103
	public void fill(CoolBar parent, int index) {
104
		item.setControl(combo);
104
		CoolItem item = new CoolItem(parent, SWT.DROP_DOWN);
105
	}
105
		Combo combo = createCombo(parent);
106
106
		item.setControl(combo);
107
	/*
107
	}
108
	 * (non-Javadoc)
108
109
	 * 
109
	/*
110
	 * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.ToolBar,
110
	 * (non-Javadoc)
111
	 *      int)
111
	 * 
112
	 */
112
	 * @see
113
	public void fill(ToolBar parent, int index) {
113
	 * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets
114
		ToolItem item = new ToolItem(parent, SWT.DROP_DOWN);
114
	 * .ToolBar, int)
115
		Combo combo = createCombo(parent);
115
	 */
116
		item.setControl(combo);
116
	public void fill(ToolBar parent, int index) {
117
	}
117
		ToolItem item = new ToolItem(parent, SWT.DROP_DOWN);
118
118
		Combo combo = createCombo(parent);
119
	private Combo createCombo(Composite parent) {
119
		item.setControl(combo);
120
		this.combo = new Combo(parent, SWT.DROP_DOWN);
120
	}
121
		this.combo.setItems(zoomLevels);
121
122
		this.combo.addSelectionListener(new SelectionAdapter() {
122
	private Combo createCombo(Composite parent) {
123
			/*
123
		this.combo = new Combo(parent, SWT.DROP_DOWN);
124
			 * (non-Javadoc)
124
		this.combo.setItems(zoomLevels);
125
			 * 
125
		this.combo.addSelectionListener(new SelectionAdapter() {
126
			 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
126
			/*
127
			 */
127
			 * (non-Javadoc)
128
			public void widgetSelected(SelectionEvent e) {
128
			 * 
129
				int selection = combo.getSelectionIndex();
129
			 * @see
130
				if (selection > 0) {
130
			 * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
131
					doZoom(combo.getItem(selection));
131
			 * .swt.events.SelectionEvent)
132
				} else {
132
			 */
133
					doZoom(combo.getItem(0));
133
			public void widgetSelected(SelectionEvent e) {
134
				}
134
				int selection = combo.getSelectionIndex();
135
			}
135
				if (selection > 0) {
136
		});
136
					doZoom(combo.getItem(selection));
137
		return this.combo;
137
				} else {
138
	}
138
					doZoom(combo.getItem(0));
139
139
				}
140
	private void doZoom(String zoom) {
140
			}
141
		if (zoomManager != null) {
141
		});
142
			zoomManager.setZoomAsText(zoom);
142
		return this.combo;
143
		}
143
	}
144
	}
144
145
145
	private void doZoom(String zoom) {
146
	private void refresh(boolean rebuild) {
146
		if (zoomManager != null) {
147
		//
147
			zoomManager.setZoomAsText(zoom);
148
		if (combo != null && !combo.isDisposed()) {
148
		}
149
			refreshCombo(rebuild);
149
	}
150
		} else if (fMenu != null && !fMenu.isDisposed()) {
150
151
			refreshMenu(rebuild);
151
	private void refresh(boolean rebuild) {
152
		}
152
		//
153
	}
153
		if (combo != null && !combo.isDisposed()) {
154
154
			refreshCombo(rebuild);
155
	/**
155
		} else if (fMenu != null && !fMenu.isDisposed()) {
156
	 * @param rebuild
156
			refreshMenu(rebuild);
157
	 */
157
		}
158
	private void refreshMenu(boolean rebuild) {
158
	}
159
		fMenu.setEnabled(false);
159
160
		if (zoomManager == null) {
160
	/**
161
			return;
161
	 * @param rebuild
162
		}
162
	 */
163
		if (rebuild) {
163
	private void refreshMenu(boolean rebuild) {
164
			zoomLevels = zoomManager.getZoomLevelsAsText();
164
		fMenu.setEnabled(false);
165
			MenuItem[] oldItems = fMenu.getItems();
165
		if (zoomManager == null) {
166
			for (int i = 0; i < oldItems.length; i++) {
166
			return;
167
				if (oldItems[i].getData() == this) {
167
		}
168
					oldItems[i].dispose();
168
		if (rebuild) {
169
				}
169
			zoomLevels = zoomManager.getZoomLevelsAsText();
170
			}
170
			MenuItem[] oldItems = fMenu.getItems();
171
			for (int i = 0; i < zoomLevels.length; i++) {
171
			for (int i = 0; i < oldItems.length; i++) {
172
				MenuItem item = new MenuItem(fMenu, SWT.RADIO);
172
				if (oldItems[i].getData() == this) {
173
				item.setText(zoomLevels[i]);
173
					oldItems[i].dispose();
174
				item.setData(this);
174
				}
175
				item.addSelectionListener(new SelectionAdapter() {
175
			}
176
					public void widgetSelected(SelectionEvent e) {
176
			for (int i = 0; i < zoomLevels.length; i++) {
177
						MenuItem source = (MenuItem) e.getSource();
177
				MenuItem item = new MenuItem(fMenu, SWT.RADIO);
178
						doZoom(source.getText());
178
				item.setText(zoomLevels[i]);
179
					}
179
				item.setData(this);
180
				});
180
				item.addSelectionListener(new SelectionAdapter() {
181
			}
181
					public void widgetSelected(SelectionEvent e) {
182
		}
182
						MenuItem source = (MenuItem) e.getSource();
183
		String zoom = zoomManager.getZoomAsText();
183
						doZoom(source.getText());
184
		MenuItem[] items = fMenu.getItems();
184
					}
185
		for (int i = 0; i < items.length; i++) {
185
				});
186
			MenuItem item = items[i];
186
			}
187
			if (item.getData() == this) {
187
		}
188
				item.setSelection(false);
188
		String zoom = zoomManager.getZoomAsText();
189
				if (zoom.equalsIgnoreCase(item.getText())) {
189
		MenuItem[] items = fMenu.getItems();
190
					item.setSelection(true);
190
		for (int i = 0; i < items.length; i++) {
191
				}
191
			MenuItem item = items[i];
192
			}
192
			if (item.getData() == this) {
193
		}
193
				item.setSelection(false);
194
		fMenu.setEnabled(true);
194
				if (zoom.equalsIgnoreCase(item.getText())) {
195
	}
195
					item.setSelection(true);
196
196
				}
197
	/**
197
			}
198
	 * @param rebuild
198
		}
199
	 */
199
		fMenu.setEnabled(true);
200
	private void refreshCombo(boolean rebuild) {
200
	}
201
		combo.setEnabled(false);
201
202
		if (zoomManager == null) {
202
	/**
203
			return;
203
	 * @param rebuild
204
		}
204
	 */
205
		if (rebuild) {
205
	private void refreshCombo(boolean rebuild) {
206
			combo.setItems(zoomManager.getZoomLevelsAsText());
206
		combo.setEnabled(false);
207
		}
207
		if (zoomManager == null) {
208
		String zoom = zoomManager.getZoomAsText();
208
			return;
209
		int index = combo.indexOf(zoom);
209
		}
210
		if (index > 0) {
210
		if (rebuild) {
211
			combo.select(index);
211
			combo.setItems(zoomManager.getZoomLevelsAsText());
212
		}
212
		}
213
		combo.setEnabled(true);
213
		String zoom = zoomManager.getZoomAsText();
214
	}
214
		int index = combo.indexOf(zoom);
215
215
		if (index > 0) {
216
	/*
216
			combo.select(index);
217
	 * (non-Javadoc)
217
		}
218
	 * 
218
		combo.setEnabled(true);
219
	 * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double)
219
	}
220
	 */
220
221
	public void zoomChanged(double z) {
221
	/*
222
		refresh(false);
222
	 * (non-Javadoc)
223
	}
223
	 * 
224
224
	 * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double)
225
	/*
225
	 */
226
	 * (non-Javadoc)
226
	public void zoomChanged(double z) {
227
	 * 
227
		refresh(false);
228
	 * @see org.eclipse.jface.action.ContributionItem#dispose()
228
	}
229
	 */
229
230
230
	/*
231
	public void dispose() {
231
	 * (non-Javadoc)
232
		if (combo != null) {
232
	 * 
233
			combo = null;
233
	 * @see org.eclipse.jface.action.ContributionItem#dispose()
234
		}
234
	 */
235
		if (fMenu != null) {
235
236
			fMenu = null;
236
	public void dispose() {
237
		}
237
		if (combo != null) {
238
		//		@tag zest.bug.159667-ZoomDispose : make sure that we no longer listen to the part service.
238
			combo = null;
239
		super.dispose();
239
		}
240
	}
240
		if (fMenu != null) {
241
}
241
			fMenu = null;
242
		}
243
		// @tag zest.bug.159667-ZoomDispose : make sure that we no longer listen
244
		// to the part service.
245
		super.dispose();
246
	}
247
}
(-)src/org/eclipse/zest/core/viewers/ZoomListener.java (+27 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2010 IBM Corporation and others. All rights reserved.
3
 * This program and the accompanying materials are made available under the
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: IBM Corporation - initial API and implementation
8
 *******************************************************************************/
9
package org.eclipse.zest.core.viewers;
10
11
/**
12
 * Listens to zoom level changes.
13
 * 
14
 * @author Eric Bordeau
15
 * @since 2.0
16
 */
17
public interface ZoomListener {
18
19
	/**
20
	 * Called whenever the ZoomManager's zoom level changes.
21
	 * 
22
	 * @param zoom
23
	 *            the new zoom level.
24
	 */
25
	void zoomChanged(double zoom);
26
27
}
(-)src/org/eclipse/zest/core/viewers/ZoomManager.java (+611 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2010 IBM Corporation and others. All rights reserved.
3
 * This program and the accompanying materials are made available under the
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: IBM Corporation - initial API and implementation
8
 ******************************************************************************/
9
package org.eclipse.zest.core.viewers;
10
11
import java.util.ArrayList;
12
import java.util.Collections;
13
import java.util.Iterator;
14
import java.util.List;
15
16
import org.eclipse.draw2d.FreeformFigure;
17
import org.eclipse.draw2d.IFigure;
18
import org.eclipse.draw2d.ScalableFigure;
19
import org.eclipse.draw2d.ScalableFreeformLayeredPane;
20
import org.eclipse.draw2d.Viewport;
21
import org.eclipse.draw2d.geometry.Dimension;
22
import org.eclipse.draw2d.geometry.Point;
23
import org.eclipse.draw2d.geometry.Rectangle;
24
import org.eclipse.swt.widgets.Display;
25
import org.eclipse.zest.core.viewers.internal.SharedMessages;
26
27
/**
28
 * Manage the primary zoom function in a graphical viewer. This class is used by
29
 * the zoom contribution items, including:
30
 * <UL>
31
 * <LI>{@link org.eclipse.gef.ui.actions.ZoomInAction}
32
 * <LI>{@link org.eclipse.gef.ui.actions.ZoomOutAction}
33
 * <LI>and {@link org.eclipse.gef.ui.actions.ZoomComboContributionItem}
34
 * </UL>
35
 * <P>
36
 * A ZoomManager controls how zoom in and zoom out are performed. It also
37
 * determines the list of choices the user sees in the drop-down Combo on the
38
 * toolbar. The zoom manager controls a <code>ScalableFigure</code>, which
39
 * performs the actual zoom, and also a <code>Viewport</code>. The viewport is
40
 * needed so that the scrolled location is preserved as the zoom level changes.
41
 * <p>
42
 * <b>NOTE:</b> For the settings of {@link #FIT_ALL Page}, {@link #FIT_WIDTH
43
 * Width} and {@link #FIT_HEIGHT Height} to work properly, the given
44
 * <code>Viewport</code> should have its scrollbars always visible or never
45
 * visible. Otherwise, these settings may cause undesired effects.
46
 * 
47
 * @author Dan Lee
48
 * @author Eric Bordeau
49
 * @since 2.0
50
 */
51
public class ZoomManager {
52
53
	/** Style bit meaning don't animate any zooms */
54
	public static final int ANIMATE_NEVER = 0;
55
	/** Style bit meaning animate during {@link #zoomIn()} and {@link #zoomOut()} */
56
	public static final int ANIMATE_ZOOM_IN_OUT = 1;
57
58
	private List listeners = new ArrayList();
59
60
	private double multiplier = 1.0;
61
	private ScalableFigure pane;
62
	private Viewport viewport;
63
	private double zoom = 1.0;
64
	// private int zoomAnimationStyle = ANIMATE_NEVER;
65
	private String currentZoomContant = null;
66
	private double[] zoomLevels = { .5, .75, 1.0, 1.5, 2.0, 2.5, 3, 4 };
67
68
	/**
69
	 * String constant for the "Height" zoom level. At this zoom level, the zoom
70
	 * manager will adopt a zoom setting such that the entire height of the
71
	 * diagram will be visible on the screen.
72
	 */
73
	public static final String FIT_HEIGHT = SharedMessages.FitHeightAction_Label;
74
	/**
75
	 * String constant for the "Width" zoom level. At this zoom level, the zoom
76
	 * manager will adopt a zoom setting such that the entire width of the
77
	 * diagram will be visible on the screen.
78
	 */
79
	public static final String FIT_WIDTH = SharedMessages.FitWidthAction_Label;
80
	/**
81
	 * String constant for the "Page" zoom level. At this zoom level, the zoom
82
	 * manager will adopt a zoom setting such that the entire diagram will be
83
	 * visible on the screen.
84
	 */
85
	public static final String FIT_ALL = SharedMessages.FitAllAction_Label;
86
	private List zoomLevelContributions = Collections.EMPTY_LIST;
87
88
	//DecimalFormat format = new DecimalFormat("####%"); //$NON-NLS-1$
89
90
	/**
91
	 * Creates a new ZoomManager.
92
	 * 
93
	 * @param pane
94
	 *            The ScalableFigure associated with this ZoomManager
95
	 * @param viewport
96
	 *            The Viewport assoicated with this ZoomManager
97
	 */
98
	public ZoomManager(ScalableFigure pane, Viewport viewport) {
99
		this.pane = pane;
100
		this.viewport = viewport;
101
		zoomLevelContributions = new ArrayList();
102
		zoomLevelContributions.add(FIT_ALL);
103
	}
104
105
	/**
106
	 * @deprecated Use {@link #ZoomManager(ScalableFigure, Viewport)} instead.
107
	 *             Creates a new ZoomManager
108
	 * @param pane
109
	 *            The ScalableFreeformLayeredPane associated with this
110
	 *            ZoomManager
111
	 * @param viewport
112
	 *            The Viewport assoicated with this viewport
113
	 */
114
	public ZoomManager(ScalableFreeformLayeredPane pane, Viewport viewport) {
115
		this.pane = pane;
116
		this.viewport = viewport;
117
	}
118
119
	/**
120
	 * Adds the given ZoomListener to this ZoomManager's list of listeners.
121
	 * 
122
	 * @param listener
123
	 *            the ZoomListener to be added
124
	 */
125
	public void addZoomListener(ZoomListener listener) {
126
		listeners.add(listener);
127
	}
128
129
	/**
130
	 * returns <code>true</code> if the zoommanager can perform
131
	 * <code>zoomIn()</code>.
132
	 * 
133
	 * @return boolean true if zoomIn can be called
134
	 */
135
	public boolean canZoomIn() {
136
		return getZoom() < getMaxZoom();
137
	}
138
139
	/**
140
	 * returns <code>true</code> if the zoommanager can perform
141
	 * <code>zoomOut()</code>.
142
	 * 
143
	 * @return boolean true if zoomOut can be called
144
	 */
145
	public boolean canZoomOut() {
146
		return getZoom() > getMinZoom();
147
	}
148
149
	/**
150
	 * Notifies listeners that the zoom level has changed.
151
	 */
152
	protected void fireZoomChanged() {
153
		Iterator iter = listeners.iterator();
154
		while (iter.hasNext()) {
155
			((ZoomListener) iter.next()).zoomChanged(zoom);
156
		}
157
	}
158
159
	private double getFitXZoomLevel(int which) {
160
		IFigure fig = getScalableFigure();
161
162
		Dimension available = getViewport().getClientArea().getSize();
163
		Dimension desired;
164
		if (fig instanceof FreeformFigure) {
165
			desired = ((FreeformFigure) fig).getFreeformExtent().getCopy()
166
					.union(0, 0).getSize();
167
		} else {
168
			desired = fig.getPreferredSize().getCopy();
169
		}
170
171
		desired.width -= fig.getInsets().getWidth();
172
		desired.height -= fig.getInsets().getHeight();
173
174
		while (fig != getViewport()) {
175
			available.width -= fig.getInsets().getWidth();
176
			available.height -= fig.getInsets().getHeight();
177
			fig = fig.getParent();
178
		}
179
180
		double scaleX = Math.min(available.width * zoom / desired.width,
181
				getMaxZoom());
182
		double scaleY = Math.min(available.height * zoom / desired.height,
183
				getMaxZoom());
184
		if (which == 0) {
185
			return scaleX;
186
		}
187
		if (which == 1) {
188
			return scaleY;
189
		}
190
		return Math.min(scaleX, scaleY);
191
	}
192
193
	/**
194
	 * Calculates and returns the zoom percent required so that the entire
195
	 * height of the {@link #getScalableFigure() scalable figure} is visible on
196
	 * the screen. This is the zoom level associated with {@link #FIT_HEIGHT}.
197
	 * 
198
	 * @return zoom setting required to fit the scalable figure vertically on
199
	 *         the screen
200
	 */
201
	protected double getFitHeightZoomLevel() {
202
		return getFitXZoomLevel(1);
203
	}
204
205
	/**
206
	 * Calculates and returns the zoom percentage required to fit the entire
207
	 * {@link #getScalableFigure() scalable figure} on the screen. This is the
208
	 * zoom setting associated with {@link #FIT_ALL}. It is the minimum of
209
	 * {@link #getFitHeightZoomLevel()} and {@link #getFitWidthZoomLevel()}.
210
	 * 
211
	 * @return zoom setting required to fit the entire scalable figure on the
212
	 *         screen
213
	 */
214
	protected double getFitPageZoomLevel() {
215
		return getFitXZoomLevel(2);
216
	}
217
218
	/**
219
	 * Calculates and returns the zoom percentage required so that the entire
220
	 * width of the {@link #getScalableFigure() scalable figure} is visible on
221
	 * the screen. This is the zoom setting associated with {@link #FIT_WIDTH}.
222
	 * 
223
	 * @return zoom setting required to fit the scalable figure horizontally on
224
	 *         the screen
225
	 */
226
	protected double getFitWidthZoomLevel() {
227
		return getFitXZoomLevel(0);
228
	}
229
230
	/**
231
	 * Returns the maxZoom.
232
	 * 
233
	 * @return double
234
	 */
235
	public double getMaxZoom() {
236
		return getZoomLevels()[getZoomLevels().length - 1];
237
	}
238
239
	/**
240
	 * Returns the minZoom.
241
	 * 
242
	 * @return double
243
	 */
244
	public double getMinZoom() {
245
		return getZoomLevels()[0];
246
	}
247
248
	/**
249
	 * Returns the mutltiplier. This value is used to use zoom levels internally
250
	 * that are proportionally different than those displayed to the user. e.g.
251
	 * with a multiplier value of 2.0, the zoom level 1.0 will be displayed as
252
	 * "200%".
253
	 * 
254
	 * @return double The multiplier
255
	 */
256
	public double getUIMultiplier() {
257
		return multiplier;
258
	}
259
260
	/**
261
	 * Returns the zoom level that is one level higher than the current level.
262
	 * If zoom level is at maximum, returns the maximum.
263
	 * 
264
	 * @return double The next zoom level
265
	 */
266
	public double getNextZoomLevel() {
267
		for (int i = 0; i < zoomLevels.length; i++) {
268
			if (zoomLevels[i] > zoom) {
269
				return zoomLevels[i];
270
			}
271
		}
272
		return getMaxZoom();
273
	}
274
275
	/**
276
	 * Returns the zoom level that is one level higher than the current level.
277
	 * If zoom level is at maximum, returns the maximum.
278
	 * 
279
	 * @return double The previous zoom level
280
	 */
281
	public double getPreviousZoomLevel() {
282
		for (int i = 1; i < zoomLevels.length; i++) {
283
			if (zoomLevels[i] >= zoom) {
284
				return zoomLevels[i - 1];
285
			}
286
		}
287
		return getMinZoom();
288
	}
289
290
	/**
291
	 * Returns the figure which performs the actual zooming.
292
	 * 
293
	 * @return the scalable figure
294
	 */
295
	public ScalableFigure getScalableFigure() {
296
		return pane;
297
	}
298
299
	/**
300
	 * Returns the viewport.
301
	 * 
302
	 * @return Viewport
303
	 */
304
	public Viewport getViewport() {
305
		return viewport;
306
	}
307
308
	/**
309
	 * Returns the current zoom level.
310
	 * 
311
	 * @return double the zoom level
312
	 */
313
	public double getZoom() {
314
		return zoom;
315
	}
316
317
	private String format(double d) {
318
		return "" + ((int) (d * 100)) + "%";
319
	}
320
321
	/**
322
	 * Returns the current zoom level as a percentage formatted String
323
	 * 
324
	 * @return String The current zoom level as a String
325
	 */
326
	public String getZoomAsText() {
327
		if (currentZoomContant != null) {
328
			return currentZoomContant;
329
		}
330
331
		// String newItem = format.format(zoom * multiplier);
332
		String newItem = format(zoom * multiplier);
333
		return newItem;
334
	}
335
336
	/**
337
	 * Returns the list of strings that should be appended to the list of
338
	 * numerical zoom levels. These could be things such as Fit Width, Fit Page,
339
	 * etc. May return <code>null</code>.
340
	 * 
341
	 * @return the list of contributed zoom levels
342
	 */
343
	public List getZoomLevelContributions() {
344
		return zoomLevelContributions;
345
	}
346
347
	/**
348
	 * Returns the zoomLevels.
349
	 * 
350
	 * @return double[]
351
	 */
352
	public double[] getZoomLevels() {
353
		return zoomLevels;
354
	}
355
356
	/**
357
	 * Returns the list of zoom levels as Strings in percent notation, plus any
358
	 * additional zoom levels that were contributed using
359
	 * {@link #setZoomLevelContributions(List)}.
360
	 * 
361
	 * @return List The list of zoom levels
362
	 */
363
	public String[] getZoomLevelsAsText() {
364
		String[] zoomLevelStrings = new String[zoomLevels.length
365
				+ zoomLevelContributions.size()];
366
367
		if (zoomLevelContributions != null) {
368
			for (int i = 0; i < zoomLevelContributions.size(); i++) {
369
				zoomLevelStrings[i] = (String) zoomLevelContributions.get(i);
370
			}
371
		}
372
		for (int i = 0; i < zoomLevels.length; i++) {
373
			// zoomLevelStrings[i + zoomLevelContributions.size()] =
374
			// format.format(zoomLevels[i] * multiplier);
375
			zoomLevelStrings[i + zoomLevelContributions.size()] = format(zoomLevels[i]
376
					* multiplier);
377
		}
378
379
		return zoomLevelStrings;
380
	}
381
382
	/**
383
	 * Sets the zoom level to the given value. Min-max range check is not done.
384
	 * 
385
	 * @param zoom
386
	 *            the new zoom level
387
	 */
388
	protected void primSetZoom(double zoom) {
389
		Point p1 = getViewport().getClientArea().getCenter();
390
		Point p2 = p1.getCopy();
391
		Point p = getViewport().getViewLocation();
392
		double prevZoom = this.zoom;
393
		this.zoom = zoom;
394
		pane.setScale(zoom);
395
		fireZoomChanged();
396
		getViewport().validate();
397
398
		p2.scale(zoom / prevZoom);
399
		Dimension dif = p2.getDifference(p1);
400
		p.x += dif.width;
401
		p.y += dif.height;
402
		setViewLocation(p);
403
	}
404
405
	/**
406
	 * Removes the given ZoomListener from this ZoomManager's list of listeners.
407
	 * 
408
	 * @param listener
409
	 *            the ZoomListener to be removed
410
	 */
411
	public void removeZoomListener(ZoomListener listener) {
412
		listeners.remove(listener);
413
	}
414
415
	/**
416
	 * Sets the UI multiplier. The UI multiplier is applied to all zoom settings
417
	 * when they are presented to the user ({@link #getZoomAsText()}).
418
	 * Similarly, the multiplier is inversely applied when the user specifies a
419
	 * zoom level ({@link #setZoomAsText(String)}).
420
	 * <P>
421
	 * When the UI multiplier is <code>1.0</code>, the User will see the exact
422
	 * zoom level that is being applied. If the value is <code>2.0</code>, then
423
	 * a scale of <code>0.5</code> will be labeled "100%" to the User.
424
	 * 
425
	 * @param multiplier
426
	 *            The mutltiplier to set
427
	 */
428
	public void setUIMultiplier(double multiplier) {
429
		this.multiplier = multiplier;
430
	}
431
432
	/**
433
	 * Sets the Viewport's view associated with this ZoomManager to the passed
434
	 * Point
435
	 * 
436
	 * @param p
437
	 *            The new location for the Viewport's view.
438
	 */
439
	public void setViewLocation(Point p) {
440
		viewport.setViewLocation(p.x, p.y);
441
442
	}
443
444
	/**
445
	 * Sets the zoom level to the given value. If the zoom is out of the min-max
446
	 * range, it will be ignored.
447
	 * 
448
	 * @param zoom
449
	 *            the new zoom level
450
	 */
451
	public void setZoom(double zoom) {
452
		currentZoomContant = null;
453
		zoom = Math.min(getMaxZoom(), zoom);
454
		zoom = Math.max(getMinZoom(), zoom);
455
		if (this.zoom != zoom) {
456
			primSetZoom(zoom);
457
		}
458
	}
459
460
	/**
461
	 * Sets which zoom methods get animated.
462
	 * 
463
	 * @param style
464
	 *            the style bits determining the zoom methods to be animated.
465
	 */
466
	public void setZoomAnimationStyle(int style) {
467
		// zoomAnimationStyle = style;
468
	}
469
470
	/**
471
	 * Sets zoom to the passed string. The string must be composed of numeric
472
	 * characters only with the exception of a decimal point and a '%' as the
473
	 * last character. If the zoom level contribution list has been set, this
474
	 * method should be overridden to provide the appropriate zoom
475
	 * implementation for the new zoom levels.
476
	 * 
477
	 * @param zoomString
478
	 *            The new zoom level
479
	 */
480
	public void setZoomAsText(String zoomString) {
481
		currentZoomContant = null;
482
		if (zoomString.equalsIgnoreCase(FIT_HEIGHT)) {
483
			currentZoomContant = FIT_HEIGHT;
484
			primSetZoom(getFitHeightZoomLevel());
485
			viewport.getUpdateManager().performUpdate();
486
			viewport.setViewLocation(viewport.getHorizontalRangeModel()
487
					.getValue(), viewport.getVerticalRangeModel().getMinimum());
488
		} else if (zoomString.equalsIgnoreCase(FIT_ALL)) {
489
			currentZoomContant = FIT_ALL;
490
			primSetZoom(getFitPageZoomLevel());
491
			viewport.getUpdateManager().performUpdate();
492
			viewport.setViewLocation(viewport.getHorizontalRangeModel()
493
					.getMinimum(), viewport.getVerticalRangeModel()
494
					.getMinimum());
495
		} else if (zoomString.equalsIgnoreCase(FIT_WIDTH)) {
496
			currentZoomContant = FIT_WIDTH;
497
			primSetZoom(getFitWidthZoomLevel());
498
			viewport.getUpdateManager().performUpdate();
499
			viewport.setViewLocation(viewport.getHorizontalRangeModel()
500
					.getMinimum(), viewport.getVerticalRangeModel().getValue());
501
		} else {
502
			try {
503
				// Trim off the '%'
504
				if (zoomString.charAt(zoomString.length() - 1) == '%') {
505
					zoomString = zoomString.substring(0,
506
							zoomString.length() - 1);
507
				}
508
				double newZoom = Double.parseDouble(zoomString) / 100;
509
				setZoom(newZoom / multiplier);
510
			} catch (Exception e) {
511
				Display.getCurrent().beep();
512
			}
513
		}
514
	}
515
516
	/**
517
	 * Sets the list of zoom level contributions (as strings). If you contribute
518
	 * something <b>other than</b> {@link #FIT_HEIGHT}, {@link #FIT_WIDTH} and
519
	 * {@link #FIT_ALL} you must subclass this class and override this method to
520
	 * implement your contributed zoom function.
521
	 * 
522
	 * @param contributions
523
	 *            the list of contributed zoom levels
524
	 */
525
	public void setZoomLevelContributions(List contributions) {
526
		zoomLevelContributions = contributions;
527
	}
528
529
	/**
530
	 * Sets the zoomLevels.
531
	 * 
532
	 * @param zoomLevels
533
	 *            The zoomLevels to set
534
	 */
535
	public void setZoomLevels(double[] zoomLevels) {
536
		this.zoomLevels = zoomLevels;
537
	}
538
539
	/**
540
	 * Sets the zoom level to be one level higher
541
	 */
542
	public void zoomIn() {
543
		setZoom(getNextZoomLevel());
544
	}
545
546
	/**
547
	 * Currently does nothing.
548
	 * 
549
	 * @param rect
550
	 *            a rectangle
551
	 */
552
	public void zoomTo(Rectangle rect) {
553
	}
554
555
	// private void performAnimatedZoom(Rectangle rect, boolean zoomIn, int
556
	// iterationCount) {
557
	// double finalRatio;
558
	// double zoomIncrement;
559
	//
560
	// if (zoomIn) {
561
	// finalRatio = zoom / getNextZoomLevel();
562
	// zoomIncrement = (getNextZoomLevel() - zoom) / iterationCount;
563
	// } else {
564
	// finalRatio = zoom / getPreviousZoomLevel();
565
	// zoomIncrement = (getPreviousZoomLevel() - zoom) / iterationCount;
566
	// }
567
	//
568
	// getScalableFigure().translateToRelative(rect);
569
	// Point originalViewLocation = getViewport().getViewLocation();
570
	// Point finalViewLocation = calculateViewLocation(rect, finalRatio);
571
	//
572
	// double xIncrement =
573
	// (double) (finalViewLocation.x - originalViewLocation.x) / iterationCount;
574
	// double yIncrement =
575
	// (double) (finalViewLocation.y - originalViewLocation.y) / iterationCount;
576
	//
577
	// double originalZoom = zoom;
578
	// Point currentViewLocation = new Point();
579
	// for (int i = 1; i < iterationCount; i++) {
580
	// currentViewLocation.x = (int)(originalViewLocation.x + (xIncrement * i));
581
	// currentViewLocation.y = (int)(originalViewLocation.y + (yIncrement * i));
582
	// setZoom(originalZoom + zoomIncrement * i);
583
	// getViewport().validate();
584
	// setViewLocation(currentViewLocation);
585
	// getViewport().getUpdateManager().performUpdate();
586
	// }
587
	//
588
	// if (zoomIn)
589
	// setZoom(getNextZoomLevel());
590
	// else
591
	// setZoom(getPreviousZoomLevel());
592
	//
593
	// getViewport().validate();
594
	// setViewLocation(finalViewLocation);
595
	// }
596
	//
597
	// private Point calculateViewLocation(Rectangle zoomRect, double ratio) {
598
	// Point viewLocation = new Point();
599
	// viewLocation.x = (int)(zoomRect.x / ratio);
600
	// viewLocation.y = (int)(zoomRect.y / ratio);
601
	// return viewLocation;
602
	// }
603
604
	/**
605
	 * Sets the zoom level to be one level lower
606
	 */
607
	public void zoomOut() {
608
		setZoom(getPreviousZoomLevel());
609
	}
610
611
}
(-)src/org/eclipse/zest/core/viewers/internal/AbstractStylingModelFactory.java (-414 / +430 lines)
Lines 1-414 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, 2009, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005, 2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors:
9
 ******************************************************************************/
9
 *     The Chisel Group, University of Victoria
10
package org.eclipse.zest.core.viewers.internal;
10
 *     Mateusz Matela <mateusz.matela@gmail.com> - Adapt Zest to changes in layout - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283179
11
11
 ******************************************************************************/
12
import java.util.ArrayList;
12
package org.eclipse.zest.core.viewers.internal;
13
import java.util.HashMap;
13
14
import java.util.Iterator;
14
import java.util.HashMap;
15
import java.util.LinkedList;
15
import java.util.Iterator;
16
import java.util.List;
16
import java.util.LinkedList;
17
import java.util.Map;
17
import java.util.List;
18
18
import java.util.Map;
19
import org.eclipse.draw2d.IFigure;
19
20
import org.eclipse.jface.viewers.IBaseLabelProvider;
20
import org.eclipse.draw2d.IFigure;
21
import org.eclipse.jface.viewers.IStructuredContentProvider;
21
import org.eclipse.jface.viewers.IBaseLabelProvider;
22
import org.eclipse.jface.viewers.StructuredViewer;
22
import org.eclipse.jface.viewers.IStructuredContentProvider;
23
import org.eclipse.jface.viewers.ViewerFilter;
23
import org.eclipse.jface.viewers.StructuredViewer;
24
import org.eclipse.swt.SWT;
24
import org.eclipse.jface.viewers.ViewerFilter;
25
import org.eclipse.zest.core.viewers.IFigureProvider;
25
import org.eclipse.swt.SWT;
26
import org.eclipse.zest.core.viewers.INestedContentProvider;
26
import org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer;
27
import org.eclipse.zest.core.widgets.Graph;
27
import org.eclipse.zest.core.viewers.IFigureProvider;
28
import org.eclipse.zest.core.widgets.GraphConnection;
28
import org.eclipse.zest.core.viewers.INestedContentProvider;
29
import org.eclipse.zest.core.widgets.GraphItem;
29
import org.eclipse.zest.core.widgets.Graph;
30
import org.eclipse.zest.core.widgets.GraphNode;
30
import org.eclipse.zest.core.widgets.GraphConnection;
31
import org.eclipse.zest.core.widgets.IContainer;
31
import org.eclipse.zest.core.widgets.GraphContainer;
32
32
import org.eclipse.zest.core.widgets.GraphItem;
33
/*
33
import org.eclipse.zest.core.widgets.GraphNode;
34
 * Base class that can be used for model factories. Offers facilities to style
34
35
 * the items that have been created by the factory.
35
/**
36
 * 
36
 * Base class that can be used for model factories. Offers facilities to style
37
 * @author Del Myers
37
 * the items that have been created by the factory.
38
 */
38
 * 
39
// @tag zest.bug.160367-Refreshing.fix : update the factory to use the
39
 * @author Del Myers
40
// IStylingGraphModelFactory
40
 */
41
public abstract class AbstractStylingModelFactory implements IStylingGraphModelFactory {
41
// @tag zest.bug.160367-Refreshing.fix : update the factory to use the
42
	private AbstractStructuredGraphViewer viewer;
42
// IStylingGraphModelFactory
43
	private int connectionStyle;
43
public abstract class AbstractStylingModelFactory implements
44
	private int nodeStyle;
44
		IStylingGraphModelFactory {
45
	private List /* ConstraintAdapater */constraintAdapters = new ArrayList();
45
	private AbstractStructuredGraphViewer viewer;
46
46
	private int connectionStyle;
47
	/**
47
	private int nodeStyle;
48
	 * 
48
49
	 */
49
	/**
50
	public AbstractStylingModelFactory(AbstractStructuredGraphViewer viewer) {
50
	 * 
51
		this.viewer = viewer;
51
	 */
52
		this.connectionStyle = SWT.NONE;
52
	public AbstractStylingModelFactory(AbstractStructuredGraphViewer viewer) {
53
		this.nodeStyle = SWT.NONE;
53
		this.viewer = viewer;
54
		if (viewer instanceof AbstractStructuredGraphViewer) {
54
		this.connectionStyle = SWT.NONE;
55
			this.constraintAdapters = (viewer).getConstraintAdapters();
55
		this.nodeStyle = SWT.NONE;
56
		}
56
	}
57
	}
57
58
58
	public void styleConnection(GraphConnection conn) {
59
	public void styleConnection(GraphConnection conn) {
59
		// recount the source and target connections on the node.
60
		// recount the source and target connections on the node.
60
		// this isn't a great way to do it, because it results in
61
		// this isn't a great way to do it, because it results in
61
		// an n^2 algorithm. But, if anyone can figure out a better way
62
		// an n^2 algorithm. But, if anyone can figure out a better way
62
		// go ahead and try it.
63
		// go ahead and try it.
63
		GraphNode source = conn.getSource();
64
		GraphNode source = conn.getSource();
64
		GraphNode dest = conn.getDestination();
65
		GraphNode dest = conn.getDestination();
65
		LinkedList rightList = getConnectionList(source, dest);
66
		LinkedList rightList = getConnectionList(source, dest);
66
67
67
		LinkedList leftList = null;
68
		LinkedList leftList = null;
68
69
69
		if (dest != source) {
70
		if (dest != source) {
70
			leftList = getConnectionList(dest, source);
71
			leftList = getConnectionList(dest, source);
71
		}
72
		}
72
73
73
		// adjust the arcs going from source to destination
74
		// adjust the arcs going from source to destination
74
		adjustCurves(rightList);
75
		adjustCurves(rightList);
75
		// adjust the arcs going from destination to source
76
		// adjust the arcs going from destination to source
76
		if (leftList != null) {
77
		if (leftList != null) {
77
			adjustCurves(leftList);
78
			adjustCurves(leftList);
78
		}
79
		}
79
	}
80
	}
80
81
81
	/**
82
	/**
82
	 * Takes a list of IGraphModelConnections and adjusts the curve depths and
83
	 * Takes a list of IGraphModelConnections and adjusts the curve depths and
83
	 * the bezier curves based on the number of curves in the list.
84
	 * the bezier curves based on the number of curves in the list.
84
	 * 
85
	 * 
85
	 * @param rightList
86
	 * @param rightList
86
	 */
87
	 */
87
	protected void adjustCurves(List connections) {
88
	protected void adjustCurves(List connections) {
88
		// @tag TODO curves : add back this code to adjust the curves
89
		// @tag TODO curves : add back this code to adjust the curves
89
		// int scale = 3;
90
		//		int scale = 3;
90
		// for (int i = 0; i < connections.size(); i++) {
91
		//		for (int i = 0; i < connections.size(); i++) {
91
		// GraphConnection conn = (GraphConnection) connections.get(i);
92
		//			GraphConnection conn = (GraphConnection) connections.get(i);
92
		// if (conn.getSource() == conn.getDestination()) {
93
		//			if (conn.getSource() == conn.getDestination()) {
93
		// scale = 5;
94
		//				scale = 5;
94
		// }
95
		//			}
95
		// // even if the connection isn't curved in the style, the edit part
96
		//			// even if the connection isn't curved in the style, the edit part
96
		// // may decide that it should be curved if source and dest are equal.
97
		//			// may decide that it should be curved if source and dest are equal.
97
		// // @tag drawing(arcs) : check here if arcs are too close when being
98
		//			// @tag drawing(arcs) : check here if arcs are too close when being
98
		// // drawn. Adjust the constant.
99
		//			// drawn. Adjust the constant.
99
		// int lineWidth = conn.getLineWidth();
100
		//			int lineWidth = conn.getLineWidth();
100
		// conn.setCurveDepth((i + 1) * (scale + lineWidth));
101
		//			conn.setCurveDepth((i + 1) * (scale + lineWidth));
101
		//
102
		//
102
		// // @tag zest(bug(152530-Bezier(fix))) : set the angles, etc based on
103
		//			// @tag zest(bug(152530-Bezier(fix))) : set the angles, etc based on
103
		// // the count.
104
		//			// the count.
104
		// // limit the angle to 90 degrees.
105
		//			// limit the angle to 90 degrees.
105
		// conn.setStartAngle(90.0 - 85.0 / Math.pow(i, 1.0 / 9.0));
106
		//			conn.setStartAngle(90.0 - 85.0 / Math.pow(i, 1.0 / 9.0));
106
		// conn.setEndAngle(85.0 / Math.pow(i, 1.0 / 9.0) - 90.0);
107
		//			conn.setEndAngle(85.0 / Math.pow(i, 1.0 / 9.0) - 90.0);
107
		// // limit the length to 1
108
		//			// limit the length to 1
108
		// conn.setStartLength(.75 - .25 / (Math.sqrt(i)));
109
		//			conn.setStartLength(.75 - .25 / (Math.sqrt(i)));
109
		// conn.setEndLength(.75 - .25 / (Math.sqrt(i)));
110
		//			conn.setEndLength(.75 - .25 / (Math.sqrt(i)));
110
		// }
111
		//		}
111
	}
112
	}
112
113
113
	/**
114
	/**
114
	 * @param source
115
	 * @param source
115
	 * @param dest
116
	 * @param dest
116
	 * @return
117
	 * @return
117
	 */
118
	 */
118
	private LinkedList getConnectionList(GraphNode source, GraphNode dest) {
119
	private LinkedList getConnectionList(GraphNode source, GraphNode dest) {
119
		LinkedList list = new LinkedList();
120
		LinkedList list = new LinkedList();
120
		Iterator i = source.getSourceConnections().iterator();
121
		Iterator i = source.getSourceConnections().iterator();
121
		while (i.hasNext()) {
122
		while (i.hasNext()) {
122
			GraphConnection c = (GraphConnection) i.next();
123
			GraphConnection c = (GraphConnection) i.next();
123
			if (c.getDestination() == dest) {
124
			if (c.getDestination() == dest) {
124
				list.add(c);
125
				list.add(c);
125
			}
126
			}
126
		}
127
		}
127
		return list;
128
		return list;
128
	}
129
	}
129
130
130
	public void styleItem(GraphItem item) {
131
	public void styleItem(GraphItem item) {
131
		GraphItemStyler.styleItem(item, getLabelProvider());
132
		GraphItemStyler.styleItem(item, getLabelProvider());
132
		if (item instanceof GraphConnection) {
133
		if (item instanceof GraphConnection) {
133
			styleConnection((GraphConnection) item);
134
			styleConnection((GraphConnection) item);
134
		}
135
		}
135
	}
136
	}
136
137
137
	public StructuredViewer getViewer() {
138
	public StructuredViewer getViewer() {
138
		return viewer;
139
		return viewer;
139
	}
140
	}
140
141
141
	/*
142
	/*
142
	 * (non-Javadoc)
143
	 * (non-Javadoc)
143
	 * 
144
	 * 
144
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
145
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#getLabelProvider()
145
	 * getLabelProvider()
146
	 */
146
	 */
147
	public IBaseLabelProvider getLabelProvider() {
147
	public IBaseLabelProvider getLabelProvider() {
148
		return viewer.getLabelProvider();
148
		return viewer.getLabelProvider();
149
	}
149
	}
150
150
151
	/*
151
	/*
152
	 * (non-Javadoc)
152
	 * (non-Javadoc)
153
	 * 
153
	 * 
154
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#getContentProvider()
154
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
155
	 */
155
	 * getContentProvider()
156
	public IStructuredContentProvider getContentProvider() {
156
	 */
157
		return (IStructuredContentProvider) viewer.getContentProvider();
157
	public IStructuredContentProvider getContentProvider() {
158
	}
158
		return (IStructuredContentProvider) viewer.getContentProvider();
159
159
	}
160
	/*
160
161
	 * (non-Javadoc)
161
	/*
162
	 * 
162
	 * (non-Javadoc)
163
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#createConnection(org.eclipse.zest.core.internal.graphmodel.GraphModel,
163
	 * 
164
	 *      java.lang.Object, java.lang.Object, java.lang.Object)
164
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
165
	 */
165
	 * createConnection(org.eclipse.zest.core.internal.graphmodel.GraphModel,
166
	public GraphConnection createConnection(Graph graph, Object element, Object source, Object dest) {
166
	 * java.lang.Object, java.lang.Object, java.lang.Object)
167
		if (source == null || dest == null) {
167
	 */
168
			return null;
168
	public GraphConnection createConnection(Graph graph, Object element,
169
		}
169
			Object source, Object dest) {
170
		GraphConnection oldConnection = viewer.getGraphModelConnection(element);
170
		if (source == null || dest == null) {
171
		GraphNode sn = viewer.getGraphModelNode(source);
171
			return null;
172
		GraphNode dn = viewer.getGraphModelNode(dest);
172
		}
173
		if (oldConnection != null) {
173
		GraphConnection oldConnection = viewer.getGraphModelConnection(element);
174
			if (sn != oldConnection.getSource() || dn != oldConnection.getDestination()) {
174
		GraphNode sn = viewer.getGraphModelNode(source);
175
				viewer.removeGraphModelConnection(oldConnection);
175
		GraphNode dn = viewer.getGraphModelNode(dest);
176
			} else {
176
		if (oldConnection != null) {
177
				styleItem(oldConnection);
177
			if (sn != oldConnection.getSource()
178
				return oldConnection;
178
					|| dn != oldConnection.getDestination()) {
179
			}
179
				viewer.removeGraphModelConnection(oldConnection);
180
		}
180
			} else {
181
		if (sn == null) {
181
				styleItem(oldConnection);
182
			sn = createNode(graph, source);
182
				return oldConnection;
183
		}
183
			}
184
		if (dn == null) {
184
		}
185
			dn = createNode(graph, dest);
185
		if (sn == null) {
186
		}
186
			sn = createNode(graph, source);
187
		GraphConnection c = viewer.addGraphModelConnection(element, sn, dn);
187
		}
188
		styleItem(c);
188
		if (dn == null) {
189
		return c;
189
			dn = createNode(graph, dest);
190
	}
190
		}
191
191
		GraphConnection c = viewer.addGraphModelConnection(element, sn, dn);
192
	/*
192
		styleItem(c);
193
	 * (non-Javadoc)
193
		return c;
194
	 * 
194
	}
195
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#createNode(org.eclipse.zest.core.internal.graphmodel.GraphModel,
195
196
	 *      java.lang.Object)
196
	/*
197
	 */
197
	 * (non-Javadoc)
198
	public GraphNode createNode(Graph graph, Object element, IFigure figure) {
198
	 * 
199
		GraphNode node = null;
199
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
200
		if (getContentProvider() instanceof INestedContentProvider) {
200
	 * createNode(org.eclipse.zest.core.internal.graphmodel.GraphModel,
201
			boolean isContainer = ((INestedContentProvider) getContentProvider()).hasChildren(element);
201
	 * java.lang.Object)
202
			if (isContainer) {
202
	 */
203
				node = viewer.addGraphModelContainer(element);
203
	public GraphNode createNode(Graph graph, Object element, IFigure figure) {
204
				styleItem(node);
204
		GraphNode node = null;
205
				Object[] childNodes = ((INestedContentProvider) getContentProvider()).getChildren(element);
205
		if (getContentProvider() instanceof INestedContentProvider) {
206
				childNodes = filter(getViewer().getInput(), childNodes);
206
			boolean isContainer = ((INestedContentProvider) getContentProvider())
207
				if (childNodes == null) {
207
					.hasChildren(element);
208
					return node;
208
			if (isContainer) {
209
				}
209
				node = viewer.addGraphModelContainer(element);
210
				for (int i = 0; i < childNodes.length; i++) {
210
				styleItem(node);
211
					GraphNode childNode = viewer.addGraphModelNode((IContainer) node, childNodes[i]);
211
				Object[] childNodes = ((INestedContentProvider) getContentProvider())
212
					styleItem(childNode);
212
						.getChildren(element);
213
				}
213
				childNodes = filter(getViewer().getInput(), childNodes);
214
				((IContainer) node).applyLayout();
214
				if (childNodes == null) {
215
				return node;
215
					return node;
216
			}
216
				}
217
		}
217
				for (int i = 0; i < childNodes.length; i++) {
218
		node = viewer.addGraphModelNode(element, figure);
218
					GraphNode childNode = viewer.addGraphModelNode(
219
		styleItem(node);
219
							(GraphContainer) node, childNodes[i]);
220
		return node;
220
					styleItem(childNode);
221
	}
221
				}
222
222
				((GraphContainer) node).applyLayout();
223
	public GraphNode createNode(Graph graph, Object element) {
223
				return node;
224
		IFigure nodeFigure = null;
224
			}
225
		if (getLabelProvider() instanceof IFigureProvider) {
225
		}
226
			nodeFigure = ((IFigureProvider) getLabelProvider()).getFigure(element);
226
		node = viewer.addGraphModelNode(element, figure);
227
		}
227
		styleItem(node);
228
		return this.createNode(graph, element, nodeFigure);
228
		return node;
229
	}
229
	}
230
230
231
	public void setConnectionStyle(int style) {
231
	public GraphNode createNode(Graph graph, Object element) {
232
		this.connectionStyle = style;
232
		IFigure nodeFigure = null;
233
	}
233
		if (getLabelProvider() instanceof IFigureProvider) {
234
234
			nodeFigure = ((IFigureProvider) getLabelProvider())
235
	/**
235
					.getFigure(element);
236
	 * @return the connectionStyle
236
		}
237
	 */
237
		return this.createNode(graph, element, nodeFigure);
238
	public int getConnectionStyle() {
238
	}
239
		return connectionStyle;
239
240
	}
240
	public void setConnectionStyle(int style) {
241
241
		this.connectionStyle = style;
242
	public void setNodeStyle(int style) {
242
	}
243
		this.nodeStyle = style;
243
244
	}
244
	/**
245
245
	 * @return the connectionStyle
246
	public List /* ConstraintAdapter */getConstraintAdapters() {
246
	 */
247
		return this.constraintAdapters;
247
	public int getConnectionStyle() {
248
	}
248
		return connectionStyle;
249
249
	}
250
	/**
250
251
	 * @return the nodeStyle
251
	public void setNodeStyle(int style) {
252
	 */
252
		this.nodeStyle = style;
253
	public int getNodeStyle() {
253
	}
254
		return nodeStyle;
254
255
	}
255
	/**
256
256
	 * @return the nodeStyle
257
	/**
257
	 */
258
	 * Default implementation simply restyles the item, regardless of the
258
	public int getNodeStyle() {
259
	 * properties.
259
		return nodeStyle;
260
	 */
260
	}
261
	public void update(GraphItem item) {
261
262
		styleItem(item);
262
	/**
263
	}
263
	 * Default implementation simply restyles the item, regardless of the
264
264
	 * properties.
265
	/**
265
	 */
266
	 * Default implementation simply restyles the items, regardless of the
266
	public void update(GraphItem item) {
267
	 * properties.
267
		styleItem(item);
268
	 */
268
	}
269
	public void update(GraphItem[] items) {
269
270
		for (int i = 0; i < items.length; i++) {
270
	/**
271
			styleItem(items[i]);
271
	 * Default implementation simply restyles the items, regardless of the
272
		}
272
	 * properties.
273
	}
273
	 */
274
274
	public void update(GraphItem[] items) {
275
	/*
275
		for (int i = 0; i < items.length; i++) {
276
	 * (non-Javadoc)
276
			styleItem(items[i]);
277
	 * 
277
		}
278
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refreshGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
278
	}
279
	 */
279
280
	public void refreshGraph(Graph graph) {
280
	/*
281
		// with this kind of graph, it is just as easy and cost-effective to
281
	 * (non-Javadoc)
282
		// rebuild the whole thing.
282
	 * 
283
283
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
284
		Map oldMap = viewer.getNodesMap();
284
	 * refreshGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
285
		HashMap nodesMap = new HashMap();
285
	 */
286
		// have to copy the Map data accross so that it doesn't get overwritten
286
	public void refreshGraph(Graph graph) {
287
		for (Iterator keys = oldMap.keySet().iterator(); keys.hasNext();) {
287
		// with this kind of graph, it is just as easy and cost-effective to
288
			Object key = keys.next();
288
		// rebuild the whole thing.
289
			nodesMap.put(key, oldMap.get(key));
289
290
		}
290
		Map oldMap = viewer.getNodesMap();
291
		clearGraph(graph);
291
		HashMap nodesMap = new HashMap();
292
		doBuildGraph(graph);
292
		// have to copy the Map data accross so that it doesn't get overwritten
293
		// update the positions on the new nodes to match the old ones.
293
		for (Iterator keys = oldMap.keySet().iterator(); keys.hasNext();) {
294
		GraphNode[] nodes = getNodesArray(graph);
294
			Object key = keys.next();
295
		// save a little time, go with the smallest list as the primary list
295
			nodesMap.put(key, oldMap.get(key));
296
		if (nodes.length < nodesMap.keySet().size()) {
296
		}
297
			for (int i = 0; i < nodes.length; i++) {
297
		clearGraph(graph);
298
				GraphNode oldNode = (GraphNode) nodesMap.get(nodes[i].getData());
298
		doBuildGraph(graph);
299
				if (oldNode != null) {
299
		// update the positions on the new nodes to match the old ones.
300
					nodes[i].setLocation(oldNode.getLocation().x, oldNode.getLocation().y);
300
		GraphNode[] nodes = getNodesArray(graph);
301
					if (oldNode.isSizeFixed()) {
301
		// save a little time, go with the smallest list as the primary list
302
						nodes[i].setSize(oldNode.getSize().width, oldNode.getSize().height);
302
		if (nodes.length < nodesMap.keySet().size()) {
303
					}
303
			for (int i = 0; i < nodes.length; i++) {
304
				}
304
				GraphNode oldNode = (GraphNode) nodesMap
305
			}
305
						.get(nodes[i].getData());
306
		} else {
306
				if (oldNode != null) {
307
			for (Iterator i = nodesMap.keySet().iterator(); i.hasNext();) {
307
					nodes[i].setLocation(oldNode.getLocation().x, oldNode
308
				Object key = i.next();
308
							.getLocation().y);
309
				GraphNode node = viewer.getGraphModelNode(key);
309
					if (oldNode.isSizeFixed()) {
310
				if (node != null) {
310
						nodes[i].setSize(oldNode.getSize().width, oldNode
311
					GraphNode oldNode = (GraphNode) nodesMap.get(key);
311
								.getSize().height);
312
					node.setLocation(oldNode.getLocation().x, oldNode.getLocation().y);
312
					}
313
					if (oldNode.isSizeFixed()) {
313
				}
314
						node.setSize(oldNode.getSize().width, oldNode.getSize().height);
314
			}
315
					}
315
		} else {
316
				}
316
			for (Iterator i = nodesMap.keySet().iterator(); i.hasNext();) {
317
			}
317
				Object key = i.next();
318
		}
318
				GraphNode node = viewer.getGraphModelNode(key);
319
	}
319
				if (node != null) {
320
320
					GraphNode oldNode = (GraphNode) nodesMap.get(key);
321
	/**
321
					node.setLocation(oldNode.getLocation().x, oldNode
322
	 * Convenience method for clearing all the elements in the graph.
322
							.getLocation().y);
323
	 * 
323
					if (oldNode.isSizeFixed()) {
324
	 * @param graph
324
						node.setSize(oldNode.getSize().width,
325
	 */
325
								oldNode.getSize().height);
326
	public void clearGraph(Graph graph) {
326
					}
327
		graph.setSelection(null);
327
				}
328
		Object[] nodeElements = viewer.getNodeElements();
328
			}
329
		for (int i = 0; i < nodeElements.length; i++) {
329
		}
330
			viewer.removeGraphModelNode(nodeElements[i]);
330
	}
331
		}
331
332
		Object[] connectionElements = viewer.getConnectionElements();
332
	/**
333
		for (int i = 0; i < connectionElements.length; i++) {
333
	 * Convenience method for clearing all the elements in the graph.
334
			viewer.removeGraphModelConnection(connectionElements[i]);
334
	 * 
335
		}
335
	 * @param graph
336
	}
336
	 */
337
337
	public void clearGraph(Graph graph) {
338
	/**
338
		graph.setSelection(null);
339
	 * Builds the graph model from the viewer's content provider. There is no
339
		Object[] nodeElements = viewer.getNodeElements();
340
	 * guarantee that the model will be cleared before this method is called.
340
		for (int i = 0; i < nodeElements.length; i++) {
341
	 * 
341
			viewer.removeGraphModelNode(nodeElements[i]);
342
	 * @param graph
342
		}
343
	 */
343
		Object[] connectionElements = viewer.getConnectionElements();
344
	protected void doBuildGraph(Graph model) {
344
		for (int i = 0; i < connectionElements.length; i++) {
345
		clearGraph(model);
345
			viewer.removeGraphModelConnection(connectionElements[i]);
346
		model.setConnectionStyle(getConnectionStyle());
346
		}
347
		model.setNodeStyle(getNodeStyle());
347
	}
348
		model.setConstraintAdapters(getConstraintAdapters());
348
349
	}
349
	/**
350
350
	 * Builds the graph model from the viewer's content provider. There is no
351
	/**
351
	 * guarantee that the model will be cleared before this method is called.
352
	 * Determines if this element should be filtered or not.
352
	 * 
353
	 * 
353
	 * @param graph
354
	 * @param parent
354
	 */
355
	 * @param element
355
	protected void doBuildGraph(Graph model) {
356
	 * @return
356
		clearGraph(model);
357
	 */
357
		model.setConnectionStyle(getConnectionStyle());
358
	protected boolean filterElement(Object parent, Object element) {
358
		model.setNodeStyle(getNodeStyle());
359
		ViewerFilter[] filters = getViewer().getFilters();
359
	}
360
		for (int i = 0; i < filters.length; i++) {
360
361
			boolean selected = filters[i].select(viewer, parent, element);
361
	/**
362
			if (!selected) {
362
	 * Determines if this element should be filtered or not.
363
				return true;
363
	 * 
364
			}
364
	 * @param parent
365
		}
365
	 * @param element
366
		return false;
366
	 * @return
367
	}
367
	 */
368
368
	protected boolean filterElement(Object parent, Object element) {
369
	/*
369
		ViewerFilter[] filters = getViewer().getFilters();
370
	 * (non-Javadoc)
370
		for (int i = 0; i < filters.length; i++) {
371
	 * 
371
			boolean selected = filters[i].select(viewer, parent, element);
372
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#isFiltered(java.lang.Object)
372
			if (!selected) {
373
	 */
373
				return true;
374
	protected Object[] filter(Object parent, Object[] elements) {
374
			}
375
		Object[] result = elements;
375
		}
376
		ViewerFilter[] filters = getViewer().getFilters();
376
		return false;
377
		for (int i = 0; i < filters.length; i++) {
377
	}
378
			result = filters[i].filter(viewer, parent, result);
378
379
		}
379
	/*
380
		return result;
380
	 * (non-Javadoc)
381
	}
381
	 * 
382
382
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
383
	/*
383
	 * isFiltered(java.lang.Object)
384
	 * (non-Javadoc)
384
	 */
385
	 * 
385
	protected Object[] filter(Object parent, Object[] elements) {
386
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel,
386
		Object[] result = elements;
387
	 *      java.lang.Object)
387
		ViewerFilter[] filters = getViewer().getFilters();
388
	 */
388
		for (int i = 0; i < filters.length; i++) {
389
	public void refresh(Graph graph, Object element) {
389
			result = filters[i].filter(viewer, parent, result);
390
		refresh(graph, element, false);
390
		}
391
	}
391
		return result;
392
392
	}
393
	/**
393
394
	 * Converts the list of GraphNode objects into an array and return it.
394
	/*
395
	 * 
395
	 * (non-Javadoc)
396
	 * @return GraphModelNode[]
396
	 * 
397
	 */
397
	 * @see
398
	protected GraphNode[] getNodesArray(Graph graph) {
398
	 * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
399
		GraphNode[] nodesArray = new GraphNode[graph.getNodes().size()];
399
	 * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
400
		nodesArray = (GraphNode[]) graph.getNodes().toArray(nodesArray);
400
	 */
401
		return nodesArray;
401
	public void refresh(Graph graph, Object element) {
402
	}
402
		refresh(graph, element, false);
403
403
	}
404
	/**
404
405
	 * Converts the list of GraphConnections objects into an array and return it.
405
	/**
406
	 * @param graph
406
	 * Converts the list of GraphNode objects into an array and return it.
407
	 * @return
407
	 * 
408
	 */
408
	 * @return GraphModelNode[]
409
	protected GraphConnection[] getConnectionArray(Graph graph) {
409
	 */
410
		GraphConnection[] connectionArray = new GraphConnection[graph.getConnections().size()];
410
	protected GraphNode[] getNodesArray(Graph graph) {
411
		connectionArray = (GraphConnection[]) graph.getConnections().toArray(connectionArray);
411
		GraphNode[] nodesArray = new GraphNode[graph.getNodes().size()];
412
		return connectionArray;
412
		nodesArray = (GraphNode[]) graph.getNodes().toArray(nodesArray);
413
	}
413
		return nodesArray;
414
}
414
	}
415
416
	/**
417
	 * Converts the list of GraphConnections objects into an array and return
418
	 * it.
419
	 * 
420
	 * @param graph
421
	 * @return
422
	 */
423
	protected GraphConnection[] getConnectionArray(Graph graph) {
424
		GraphConnection[] connectionArray = new GraphConnection[graph
425
				.getConnections().size()];
426
		connectionArray = (GraphConnection[]) graph.getConnections().toArray(
427
				connectionArray);
428
		return connectionArray;
429
	}
430
}
(-)src/org/eclipse/zest/core/viewers/internal/GraphItemStyler.java (-269 / +279 lines)
Lines 1-269 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers.internal;
10
package org.eclipse.zest.core.viewers.internal;
11
11
12
import org.eclipse.draw2d.IFigure;
12
import org.eclipse.draw2d.IFigure;
13
import org.eclipse.jface.viewers.IBaseLabelProvider;
13
import org.eclipse.jface.viewers.IBaseLabelProvider;
14
import org.eclipse.jface.viewers.IColorProvider;
14
import org.eclipse.jface.viewers.IColorProvider;
15
import org.eclipse.jface.viewers.IFontProvider;
15
import org.eclipse.jface.viewers.IFontProvider;
16
import org.eclipse.jface.viewers.ILabelProvider;
16
import org.eclipse.jface.viewers.ILabelProvider;
17
import org.eclipse.swt.SWT;
17
import org.eclipse.swt.SWT;
18
import org.eclipse.swt.SWTError;
18
import org.eclipse.swt.SWTError;
19
import org.eclipse.swt.graphics.Color;
19
import org.eclipse.swt.graphics.Color;
20
import org.eclipse.zest.core.viewers.IConnectionStyleProvider;
20
import org.eclipse.zest.core.viewers.IConnectionStyleProvider;
21
import org.eclipse.zest.core.viewers.IEntityConnectionStyleProvider;
21
import org.eclipse.zest.core.viewers.IEntityConnectionStyleProvider;
22
import org.eclipse.zest.core.viewers.IEntityStyleProvider;
22
import org.eclipse.zest.core.viewers.IEntityStyleProvider;
23
import org.eclipse.zest.core.viewers.ISelfStyleProvider;
23
import org.eclipse.zest.core.viewers.ISelfStyleProvider;
24
import org.eclipse.zest.core.widgets.GraphConnection;
24
import org.eclipse.zest.core.widgets.GraphConnection;
25
import org.eclipse.zest.core.widgets.GraphItem;
25
import org.eclipse.zest.core.widgets.GraphItem;
26
import org.eclipse.zest.core.widgets.GraphNode;
26
import org.eclipse.zest.core.widgets.GraphNode;
27
import org.eclipse.zest.core.widgets.ZestStyles;
27
import org.eclipse.zest.core.widgets.ZestStyles;
28
28
29
/*
29
/*
30
 * Helper class used to style graph elements based on graph element stylers.
30
 * Helper class used to style graph elements based on graph element stylers.
31
 * 
31
 * 
32
 * @author Del Myers
32
 * @author Del Myers
33
 */
33
 */
34
// @tag bug(151327-Styles) : created to help resolve this bug
34
// @tag bug(151327-Styles) : created to help resolve this bug
35
public class GraphItemStyler {
35
public class GraphItemStyler {
36
	public static void styleItem(GraphItem item, final IBaseLabelProvider labelProvider) {
36
	public static void styleItem(GraphItem item,
37
37
			final IBaseLabelProvider labelProvider) {
38
		if (item instanceof GraphNode) {
38
39
			GraphNode node = (GraphNode) item;
39
		if (item instanceof GraphNode) {
40
			// set defaults.
40
			GraphNode node = (GraphNode) item;
41
			if (node.getGraphModel().getNodeStyle() != ZestStyles.NONE) {
41
			// set defaults.
42
				node.setNodeStyle(node.getGraphModel().getNodeStyle());
42
			if (node.getGraphModel().getNodeStyle() != ZestStyles.NONE) {
43
			} else {
43
				node.setNodeStyle(node.getGraphModel().getNodeStyle());
44
				node.setNodeStyle(SWT.NONE);
44
			} else {
45
			}
45
				node.setNodeStyle(SWT.NONE);
46
			Object entity = node.getData();
46
			}
47
			if (labelProvider instanceof IEntityStyleProvider) {
47
			Object entity = node.getData();
48
				styleNode(node, (IEntityStyleProvider) labelProvider);
48
			if (labelProvider instanceof IEntityStyleProvider) {
49
			}
49
				styleNode(node, (IEntityStyleProvider) labelProvider);
50
			if (labelProvider instanceof IColorProvider) {
50
			}
51
				IColorProvider colorProvider = (IColorProvider) labelProvider;
51
			if (labelProvider instanceof IColorProvider) {
52
				node.setForegroundColor(colorProvider.getForeground(entity));
52
				IColorProvider colorProvider = (IColorProvider) labelProvider;
53
				node.setBackgroundColor(colorProvider.getBackground(entity));
53
				node.setForegroundColor(colorProvider.getForeground(entity));
54
			}
54
				node.setBackgroundColor(colorProvider.getBackground(entity));
55
			if (labelProvider instanceof IFontProvider) {
55
			}
56
				IFontProvider fontProvider = (IFontProvider) labelProvider;
56
			if (labelProvider instanceof IFontProvider) {
57
				node.setFont(fontProvider.getFont(entity));
57
				IFontProvider fontProvider = (IFontProvider) labelProvider;
58
			}
58
				node.setFont(fontProvider.getFont(entity));
59
			if (labelProvider instanceof ILabelProvider) {
59
			}
60
				String text = ((ILabelProvider) labelProvider).getText(node.getData());
60
			if (labelProvider instanceof ILabelProvider) {
61
				node.setText((text != null) ? text : "");
61
				String text = ((ILabelProvider) labelProvider).getText(node
62
				node.setImage(((ILabelProvider) labelProvider).getImage(node.getData()));
62
						.getData());
63
			}
63
				node.setText((text != null) ? text : "");
64
			if (labelProvider instanceof ISelfStyleProvider) {
64
				node.setImage(((ILabelProvider) labelProvider).getImage(node
65
				((ISelfStyleProvider) labelProvider).selfStyleNode(entity, node);
65
						.getData()));
66
			}
66
			}
67
		} else if (item instanceof GraphConnection) {
67
			if (labelProvider instanceof ISelfStyleProvider) {
68
			GraphConnection conn = (GraphConnection) item;
68
				((ISelfStyleProvider) labelProvider)
69
69
						.selfStyleNode(entity, node);
70
			// set defaults
70
			}
71
			if (conn.getGraphModel().getConnectionStyle() != ZestStyles.NONE) {
71
		} else if (item instanceof GraphConnection) {
72
				int s = conn.getGraphModel().getConnectionStyle();
72
			GraphConnection conn = (GraphConnection) item;
73
				conn.setConnectionStyle(s);
73
74
			} else {
74
			// set defaults
75
				conn.setConnectionStyle(SWT.NONE);
75
			if (conn.getGraphModel().getConnectionStyle() != ZestStyles.NONE) {
76
			}
76
				int s = conn.getGraphModel().getConnectionStyle();
77
			if (labelProvider instanceof ILabelProvider) {
77
				conn.setConnectionStyle(s);
78
				String text = ((ILabelProvider) labelProvider).getText(conn.getExternalConnection());
78
			} else {
79
				conn.setText((text != null) ? text : "");
79
				conn.setConnectionStyle(SWT.NONE);
80
				conn.setImage(((ILabelProvider) labelProvider).getImage(conn.getExternalConnection()));
80
			}
81
			}
81
			if (labelProvider instanceof ILabelProvider) {
82
			if (labelProvider instanceof IEntityConnectionStyleProvider) {
82
				String text = ((ILabelProvider) labelProvider).getText(conn
83
				styleEntityConnection(conn, (IEntityConnectionStyleProvider) labelProvider);
83
						.getExternalConnection());
84
			} else if (labelProvider instanceof IConnectionStyleProvider) {
84
				conn.setText((text != null) ? text : "");
85
				styleConnection(conn, (IConnectionStyleProvider) labelProvider);
85
				conn.setImage(((ILabelProvider) labelProvider).getImage(conn
86
			}
86
						.getExternalConnection()));
87
			int swt = getLineStyleForZestStyle(conn.getConnectionStyle());
87
			}
88
			conn.setLineStyle(swt);
88
			if (labelProvider instanceof IEntityConnectionStyleProvider) {
89
			if (labelProvider instanceof ISelfStyleProvider) {
89
				styleEntityConnection(conn,
90
				((ISelfStyleProvider) labelProvider).selfStyleConnection(conn.getData(), conn);
90
						(IEntityConnectionStyleProvider) labelProvider);
91
			}
91
			} else if (labelProvider instanceof IConnectionStyleProvider) {
92
		}
92
				styleConnection(conn, (IConnectionStyleProvider) labelProvider);
93
	}
93
			}
94
94
			int swt = getLineStyleForZestStyle(conn.getConnectionStyle());
95
	/**
95
			conn.setLineStyle(swt);
96
	 * @param conn
96
			if (labelProvider instanceof ISelfStyleProvider) {
97
	 * @param provider
97
				((ISelfStyleProvider) labelProvider).selfStyleConnection(conn
98
	 */
98
						.getData(), conn);
99
	private static void styleConnection(GraphConnection conn, IConnectionStyleProvider provider) {
99
			}
100
		Object rel = conn.getExternalConnection();
100
		}
101
		Color c;
101
	}
102
		int style = provider.getConnectionStyle(rel);
102
103
		if (!ZestStyles.validateConnectionStyle(style)) {
103
	/**
104
			throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
104
	 * @param conn
105
		}
105
	 * @param provider
106
		if (style != ZestStyles.NONE) {
106
	 */
107
			conn.setConnectionStyle(style);
107
	private static void styleConnection(GraphConnection conn,
108
		}
108
			IConnectionStyleProvider provider) {
109
		// @tag bug(152530-Bezier(fix))
109
		Object rel = conn.getExternalConnection();
110
		// @tat TODO curves bezier: Add back the bezier connection stuff
110
		Color c;
111
		// if (ZestStyles.checkStyle(conn.getConnectionStyle(),
111
		int style = provider.getConnectionStyle(rel);
112
		// ZestStyles.CONNECTIONS_BEZIER)
112
		if (!ZestStyles.validateConnectionStyle(style)) {
113
		// && provider instanceof IConnectionStyleBezierExtension) {
113
			throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
114
		// IConnectionStyleBezierExtension bezier =
114
		}
115
		// (IConnectionStyleBezierExtension) provider;
115
		if (style != ZestStyles.NONE) {
116
		// double d;
116
			conn.setConnectionStyle(style);
117
		// if (!Double.isNaN((d = bezier.getStartAngle(rel)))) {
117
		}
118
		// conn.setStartAngle(d);
118
		// @tag bug(152530-Bezier(fix))
119
		// }
119
		// @tat TODO curves bezier: Add back the bezier connection stuff
120
		// if (!Double.isNaN((d = bezier.getEndAngle(rel)))) {
120
		// if (ZestStyles.checkStyle(conn.getConnectionStyle(),
121
		// conn.setEndAngle(d);
121
		// ZestStyles.CONNECTIONS_BEZIER)
122
		// }
122
		// && provider instanceof IConnectionStyleBezierExtension) {
123
		// if (!Double.isNaN((d = bezier.getStartDistance(rel)))) {
123
		// IConnectionStyleBezierExtension bezier =
124
		// conn.setStartLength(d);
124
		// (IConnectionStyleBezierExtension) provider;
125
		// }
125
		// double d;
126
		// if (!Double.isNaN((d = bezier.getEndDistance(rel)))) {
126
		// if (!Double.isNaN((d = bezier.getStartAngle(rel)))) {
127
		// conn.setEndLength(d);
127
		// conn.setStartAngle(d);
128
		// }
128
		// }
129
		// }
129
		// if (!Double.isNaN((d = bezier.getEndAngle(rel)))) {
130
		if ((c = provider.getHighlightColor(rel)) != null) {
130
		// conn.setEndAngle(d);
131
			conn.setHighlightColor(c);
131
		// }
132
		}
132
		// if (!Double.isNaN((d = bezier.getStartDistance(rel)))) {
133
		if ((c = provider.getColor(rel)) != null) {
133
		// conn.setStartLength(d);
134
			conn.setLineColor(c);
134
		// }
135
		}
135
		// if (!Double.isNaN((d = bezier.getEndDistance(rel)))) {
136
		IFigure tooltip;
136
		// conn.setEndLength(d);
137
		if ((tooltip = provider.getTooltip(rel)) != null) {
137
		// }
138
			conn.setTooltip(tooltip);
138
		// }
139
		}
139
		if ((c = provider.getHighlightColor(rel)) != null) {
140
		int w = -1;
140
			conn.setHighlightColor(c);
141
		if ((w = provider.getLineWidth(rel)) >= 0) {
141
		}
142
			conn.setLineWidth(w);
142
		if ((c = provider.getColor(rel)) != null) {
143
		}
143
			conn.setLineColor(c);
144
	}
144
		}
145
145
		IFigure tooltip;
146
	/**
146
		if ((tooltip = provider.getTooltip(rel)) != null) {
147
	 * @param conn
147
			conn.setTooltip(tooltip);
148
	 * @param provider
148
		}
149
	 */
149
		int w = -1;
150
	private static void styleEntityConnection(GraphConnection conn, IEntityConnectionStyleProvider provider) {
150
		if ((w = provider.getLineWidth(rel)) >= 0) {
151
		Object src = conn.getSource().getData();
151
			conn.setLineWidth(w);
152
		Object dest = conn.getDestination().getData();
152
		}
153
		Color c;
153
	}
154
		int style = provider.getConnectionStyle(src, dest);
154
155
		if (!ZestStyles.validateConnectionStyle(style)) {
155
	/**
156
			throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
156
	 * @param conn
157
		}
157
	 * @param provider
158
		if (style != ZestStyles.NONE) {
158
	 */
159
			conn.setConnectionStyle(style);
159
	private static void styleEntityConnection(GraphConnection conn,
160
		}
160
			IEntityConnectionStyleProvider provider) {
161
		// @tag bug(152530-Bezier(fisx))
161
		Object src = conn.getSource().getData();
162
		// @tag TODO curved connections bezier : add back the bezier connection stuff
162
		Object dest = conn.getDestination().getData();
163
		// if (ZestStyles.checkStyle(conn.getConnectionStyle(),
163
		Color c;
164
		// ZestStyles.CONNECTIONS_BEZIER)
164
		int style = provider.getConnectionStyle(src, dest);
165
		// && provider instanceof IEntityConnectionStyleBezierExtension) {
165
		if (!ZestStyles.validateConnectionStyle(style)) {
166
		// IEntityConnectionStyleBezierExtension bezier =
166
			throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
167
		// (IEntityConnectionStyleBezierExtension) provider;
167
		}
168
		// double d;
168
		if (style != ZestStyles.NONE) {
169
		// if (!Double.isNaN((d = bezier.getStartAngle(src, dest)))) {
169
			conn.setConnectionStyle(style);
170
		// conn.setStartAngle(d);
170
		}
171
		// }
171
		// @tag bug(152530-Bezier(fisx))
172
		// if (!Double.isNaN((d = bezier.getEndAngle(src, dest)))) {
172
		// @tag TODO curved connections bezier : add back the bezier connection
173
		// conn.setEndAngle(d);
173
		// stuff
174
		// }
174
		// if (ZestStyles.checkStyle(conn.getConnectionStyle(),
175
		// if (!Double.isNaN((d = bezier.getStartDistance(src, dest)))) {
175
		// ZestStyles.CONNECTIONS_BEZIER)
176
		// conn.setStartLength(d);
176
		// && provider instanceof IEntityConnectionStyleBezierExtension) {
177
		// }
177
		// IEntityConnectionStyleBezierExtension bezier =
178
		// if (!Double.isNaN((d = bezier.getEndDistance(src, dest)))) {
178
		// (IEntityConnectionStyleBezierExtension) provider;
179
		// conn.setEndLength(d);
179
		// double d;
180
		// }
180
		// if (!Double.isNaN((d = bezier.getStartAngle(src, dest)))) {
181
		// }
181
		// conn.setStartAngle(d);
182
		if ((c = provider.getColor(src, dest)) != null) {
182
		// }
183
			conn.setLineColor(c);
183
		// if (!Double.isNaN((d = bezier.getEndAngle(src, dest)))) {
184
		}
184
		// conn.setEndAngle(d);
185
		if ((c = provider.getHighlightColor(src, dest)) != null) {
185
		// }
186
			conn.setHighlightColor(c);
186
		// if (!Double.isNaN((d = bezier.getStartDistance(src, dest)))) {
187
		}
187
		// conn.setStartLength(d);
188
		int w = -1;
188
		// }
189
		if ((w = provider.getLineWidth(src, dest)) >= 0) {
189
		// if (!Double.isNaN((d = bezier.getEndDistance(src, dest)))) {
190
			conn.setLineWidth(w);
190
		// conn.setEndLength(d);
191
		}
191
		// }
192
	}
192
		// }
193
193
		if ((c = provider.getColor(src, dest)) != null) {
194
	/**
194
			conn.setLineColor(c);
195
	 * Styles the given node according to the properties in the style provider.
195
		}
196
	 * 
196
		if ((c = provider.getHighlightColor(src, dest)) != null) {
197
	 * @param node
197
			conn.setHighlightColor(c);
198
	 *            the graph element to style.
198
		}
199
	 * @param data
199
		int w = -1;
200
	 *            the element that is being styled.
200
		if ((w = provider.getLineWidth(src, dest)) >= 0) {
201
	 * @param provider
201
			conn.setLineWidth(w);
202
	 *            the style provier.
202
		}
203
	 */
203
	}
204
	// @tag bug(151327-Styles) : resolution
204
205
	private static void styleNode(GraphNode node, IEntityStyleProvider provider) {
205
	/**
206
		Object entity = node.getData();
206
	 * Styles the given node according to the properties in the style provider.
207
		// @tag ADJACENT : Removed highlight adjacent
207
	 * 
208
		//node.setHighlightAdjacentNodes(provider.highlightAdjacentEntities(entity));
208
	 * @param node
209
209
	 *            the graph element to style.
210
		// @tag ADJACENT : Removed highlight adjacent
210
	 * @param data
211
		/*
211
	 *            the element that is being styled.
212
		if (provider.highlightAdjacentEntities(entity)) {
212
	 * @param provider
213
			Color c = provider.getAdjacentEntityHighlightColor(entity);
213
	 *            the style provier.
214
			if (c != null) {
214
	 */
215
				node.setHighlightAdjacentColor(c);
215
	// @tag bug(151327-Styles) : resolution
216
			}
216
	private static void styleNode(GraphNode node, IEntityStyleProvider provider) {
217
		}
217
		Object entity = node.getData();
218
		*/
218
		// @tag ADJACENT : Removed highlight adjacent
219
		Color c;
219
		// node.setHighlightAdjacentNodes(provider.highlightAdjacentEntities(entity));
220
		IFigure figure;
220
221
		int width = -1;
221
		// @tag ADJACENT : Removed highlight adjacent
222
		if (provider.fisheyeNode(entity) == true) {
222
		/*
223
			node.setNodeStyle(node.getNodeStyle() | ZestStyles.NODES_FISHEYE);
223
		 * if (provider.highlightAdjacentEntities(entity)) { Color c =
224
		}
224
		 * provider.getAdjacentEntityHighlightColor(entity); if (c != null) {
225
		if ((c = provider.getBorderColor(entity)) != null) {
225
		 * node.setHighlightAdjacentColor(c); } }
226
			node.setBorderColor(c);
226
		 */
227
		}
227
		Color c;
228
		if ((c = provider.getBorderHighlightColor(entity)) != null) {
228
		IFigure figure;
229
			node.setBorderHighlightColor(c);
229
		int width = -1;
230
		}
230
		if (provider.fisheyeNode(entity) == true) {
231
		if ((c = provider.getNodeHighlightColor(entity)) != null) {
231
			node.setNodeStyle(node.getNodeStyle() | ZestStyles.NODES_FISHEYE);
232
			node.setHighlightColor(c);
232
		}
233
		}
233
		if ((c = provider.getBorderColor(entity)) != null) {
234
		if ((c = provider.getBackgroundColour(entity)) != null) {
234
			node.setBorderColor(c);
235
			node.setBackgroundColor(c);
235
		}
236
		}
236
		if ((c = provider.getBorderHighlightColor(entity)) != null) {
237
		if ((c = provider.getForegroundColour(entity)) != null) {
237
			node.setBorderHighlightColor(c);
238
			node.setForegroundColor(c);
238
		}
239
		}
239
		if ((c = provider.getNodeHighlightColor(entity)) != null) {
240
		if ((width = provider.getBorderWidth(entity)) >= 0) {
240
			node.setHighlightColor(c);
241
			node.setBorderWidth(width);
241
		}
242
		}
242
		if ((c = provider.getBackgroundColour(entity)) != null) {
243
		if ((figure = provider.getTooltip(entity)) != null) {
243
			node.setBackgroundColor(c);
244
			node.setTooltip(figure);
244
		}
245
		}
245
		if ((c = provider.getForegroundColour(entity)) != null) {
246
246
			node.setForegroundColor(c);
247
	}
247
		}
248
248
		if ((width = provider.getBorderWidth(entity)) >= 0) {
249
	/**
249
			node.setBorderWidth(width);
250
	 * Returns the SWT line style for the given zest connection style.
250
		}
251
	 * 
251
		if ((figure = provider.getTooltip(entity)) != null) {
252
	 */
252
			node.setTooltip(figure);
253
	public static int getLineStyleForZestStyle(int style) {
253
		}
254
		int lineStyles = ZestStyles.CONNECTIONS_DASH_DOT | ZestStyles.CONNECTIONS_DASH | ZestStyles.CONNECTIONS_DOT | ZestStyles.CONNECTIONS_SOLID;
254
255
		style = style & lineStyles;
255
	}
256
		if (style == 0) {
256
257
			style = ZestStyles.CONNECTIONS_SOLID;
257
	/**
258
		}
258
	 * Returns the SWT line style for the given zest connection style.
259
		switch (style) {
259
	 * 
260
		case ZestStyles.CONNECTIONS_DASH_DOT:
260
	 */
261
			return SWT.LINE_DASHDOT;
261
	public static int getLineStyleForZestStyle(int style) {
262
		case ZestStyles.CONNECTIONS_DASH:
262
		int lineStyles = ZestStyles.CONNECTIONS_DASH_DOT
263
			return SWT.LINE_DASH;
263
				| ZestStyles.CONNECTIONS_DASH | ZestStyles.CONNECTIONS_DOT
264
		case ZestStyles.CONNECTIONS_DOT:
264
				| ZestStyles.CONNECTIONS_SOLID;
265
			return SWT.LINE_DOT;
265
		style = style & lineStyles;
266
		}
266
		if (style == 0) {
267
		return SWT.LINE_SOLID;
267
			style = ZestStyles.CONNECTIONS_SOLID;
268
	}
268
		}
269
}
269
		switch (style) {
270
		case ZestStyles.CONNECTIONS_DASH_DOT:
271
			return SWT.LINE_DASHDOT;
272
		case ZestStyles.CONNECTIONS_DASH:
273
			return SWT.LINE_DASH;
274
		case ZestStyles.CONNECTIONS_DOT:
275
			return SWT.LINE_DOT;
276
		}
277
		return SWT.LINE_SOLID;
278
	}
279
}
(-)src/org/eclipse/zest/core/viewers/internal/GraphModelEntityFactory.java (-214 / +229 lines)
Lines 1-214 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials are made
3
 * All rights reserved. This program and the accompanying materials are made
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers.internal;
10
package org.eclipse.zest.core.viewers.internal;
11
11
12
import java.util.HashSet;
12
import java.util.HashSet;
13
import java.util.Iterator;
13
import java.util.Iterator;
14
import java.util.LinkedList;
14
import java.util.LinkedList;
15
import java.util.List;
15
import java.util.List;
16
import java.util.Set;
16
import java.util.Set;
17
17
18
import org.eclipse.zest.core.viewers.EntityConnectionData;
18
import org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer;
19
import org.eclipse.zest.core.viewers.IFigureProvider;
19
import org.eclipse.zest.core.viewers.EntityConnectionData;
20
import org.eclipse.zest.core.viewers.IGraphEntityContentProvider;
20
import org.eclipse.zest.core.viewers.IFigureProvider;
21
import org.eclipse.zest.core.widgets.Graph;
21
import org.eclipse.zest.core.viewers.IGraphEntityContentProvider;
22
import org.eclipse.zest.core.widgets.GraphConnection;
22
import org.eclipse.zest.core.widgets.Graph;
23
import org.eclipse.zest.core.widgets.GraphItem;
23
import org.eclipse.zest.core.widgets.GraphConnection;
24
import org.eclipse.zest.core.widgets.GraphNode;
24
import org.eclipse.zest.core.widgets.GraphItem;
25
25
import org.eclipse.zest.core.widgets.GraphNode;
26
/*
26
27
 * 
27
/*
28
 * @author Ian Bull
28
 * 
29
 */
29
 * @author Ian Bull
30
public class GraphModelEntityFactory extends AbstractStylingModelFactory {
30
 */
31
31
public class GraphModelEntityFactory extends AbstractStylingModelFactory {
32
	AbstractStructuredGraphViewer viewer = null;
32
33
33
	AbstractStructuredGraphViewer viewer = null;
34
	public GraphModelEntityFactory(AbstractStructuredGraphViewer viewer) {
34
35
		super(viewer);
35
	public GraphModelEntityFactory(AbstractStructuredGraphViewer viewer) {
36
		this.viewer = viewer;
36
		super(viewer);
37
	}
37
		this.viewer = viewer;
38
38
	}
39
	/*
39
40
	 * (non-Javadoc)
40
	/*
41
	 * 
41
	 * (non-Javadoc)
42
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#createGraphModel()
42
	 * 
43
	 */
43
	 * @seeorg.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
44
	public Graph createGraphModel(Graph model) {
44
	 * createGraphModel()
45
		doBuildGraph(model);
45
	 */
46
		return model;
46
	public Graph createGraphModel(Graph model) {
47
	}
47
		doBuildGraph(model);
48
48
		return model;
49
	/*
49
	}
50
	 * (non-Javadoc)
50
51
	 * 
51
	/*
52
	 * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
52
	 * (non-Javadoc)
53
	 */
53
	 * 
54
	protected void doBuildGraph(Graph model) {
54
	 * @see
55
		super.doBuildGraph(model);
55
	 * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory
56
		Object inputElement = getViewer().getInput();
56
	 * #doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
57
		Object entities[] = getContentProvider().getElements(inputElement);
57
	 */
58
		if (entities == null) {
58
	protected void doBuildGraph(Graph model) {
59
			return;
59
		super.doBuildGraph(model);
60
		}
60
		Object inputElement = getViewer().getInput();
61
		for (int i = 0; i < entities.length; i++) {
61
		Object entities[] = getContentProvider().getElements(inputElement);
62
			Object data = entities[i];
62
		if (entities == null) {
63
			IFigureProvider figureProvider = null;
63
			return;
64
			if (getLabelProvider() instanceof IFigureProvider) {
64
		}
65
				figureProvider = (IFigureProvider) getLabelProvider();
65
		for (int i = 0; i < entities.length; i++) {
66
			}
66
			Object data = entities[i];
67
			if (!filterElement(inputElement, data)) {
67
			IFigureProvider figureProvider = null;
68
				if (figureProvider != null) {
68
			if (getLabelProvider() instanceof IFigureProvider) {
69
					createNode(model, data, figureProvider.getFigure(data));
69
				figureProvider = (IFigureProvider) getLabelProvider();
70
				} else {
70
			}
71
					createNode(model, data);
71
			if (!filterElement(inputElement, data)) {
72
				}
72
				if (figureProvider != null) {
73
			}
73
					createNode(model, data, figureProvider.getFigure(data));
74
		}
74
				} else {
75
75
					createNode(model, data);
76
		// We may have other entities (such as children of containers) 
76
				}
77
		Set keySet = ((AbstractStructuredGraphViewer) getViewer()).getNodesMap().keySet();
77
			}
78
		entities = keySet.toArray();
78
		}
79
79
80
		for (int i = 0; i < entities.length; i++) {
80
		// We may have other entities (such as children of containers)
81
			Object data = entities[i];
81
		Set keySet = ((AbstractStructuredGraphViewer) getViewer())
82
82
				.getNodesMap().keySet();
83
			// If this element is filtered, continue to the next one.
83
		entities = keySet.toArray();
84
			if (filterElement(inputElement, data)) {
84
85
				continue;
85
		for (int i = 0; i < entities.length; i++) {
86
			}
86
			Object data = entities[i];
87
			Object[] related = ((IGraphEntityContentProvider) getContentProvider()).getConnectedTo(data);
87
88
88
			// If this element is filtered, continue to the next one.
89
			if (related != null) {
89
			if (filterElement(inputElement, data)) {
90
				for (int j = 0; j < related.length; j++) {
90
				continue;
91
					// if the node this node is connected to is filtered,
91
			}
92
					// don't display this edge
92
			Object[] related = ((IGraphEntityContentProvider) getContentProvider())
93
					if (filterElement(inputElement, related[j])) {
93
					.getConnectedTo(data);
94
						continue;
94
95
					}
95
			if (related != null) {
96
					EntityConnectionData connectionData = new EntityConnectionData(data, related[j]);
96
				for (int j = 0; j < related.length; j++) {
97
					if (filterElement(inputElement, connectionData)) {
97
					// if the node this node is connected to is filtered,
98
						continue;
98
					// don't display this edge
99
					}
99
					if (filterElement(inputElement, related[j])) {
100
					createConnection(model, connectionData, data, related[j]);
100
						continue;
101
				}
101
					}
102
			}
102
					EntityConnectionData connectionData = new EntityConnectionData(
103
		}
103
							data, related[j]);
104
	}
104
					if (filterElement(inputElement, connectionData)) {
105
105
						continue;
106
	/*
106
					}
107
	 * (non-Javadoc)
107
					createConnection(model, connectionData, data, related[j]);
108
	 * 
108
				}
109
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel,
109
			}
110
	 *      java.lang.Object)
110
		}
111
	 */
111
	}
112
	public void refresh(Graph graph, Object element, boolean refreshLabels) {
112
113
		if (element == null) {
113
	/*
114
			return;
114
	 * (non-Javadoc)
115
		}
115
	 * 
116
		GraphNode node = viewer.getGraphModelNode(element);
116
	 * @see
117
		if (node == null) {
117
	 * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
118
			// check to make sure that the user didn't send us an edge.
118
	 * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
119
			GraphConnection conn = viewer.getGraphModelConnection(element);
119
	 */
120
			if (conn != null) {
120
	public void refresh(Graph graph, Object element, boolean refreshLabels) {
121
				// refresh on the connected nodes.
121
		if (element == null) {
122
				refresh(graph, conn.getSource().getData(), refreshLabels);
122
			return;
123
				refresh(graph, conn.getDestination().getData(), refreshLabels);
123
		}
124
				return;
124
		GraphNode node = viewer.getGraphModelNode(element);
125
			}
125
		if (node == null) {
126
		}
126
			// check to make sure that the user didn't send us an edge.
127
		// can only refresh on nodes in this kind of factory.
127
			GraphConnection conn = viewer.getGraphModelConnection(element);
128
		if (node == null) {
128
			if (conn != null) {
129
			// do nothing
129
				// refresh on the connected nodes.
130
			return;
130
				refresh(graph, conn.getSource().getData(), refreshLabels);
131
		}
131
				refresh(graph, conn.getDestination().getData(), refreshLabels);
132
		reconnect(graph, element, refreshLabels);
132
				return;
133
133
			}
134
		if (refreshLabels) {
134
		}
135
			update(node);
135
		// can only refresh on nodes in this kind of factory.
136
			for (Iterator it = node.getSourceConnections().iterator(); it.hasNext();) {
136
		if (node == null) {
137
				update((GraphItem) it.next());
137
			// do nothing
138
			}
138
			return;
139
			for (Iterator it = node.getTargetConnections().iterator(); it.hasNext();) {
139
		}
140
				update((GraphItem) it.next());
140
		reconnect(graph, element, refreshLabels);
141
			}
141
142
		}
142
		if (refreshLabels) {
143
	}
143
			update(node);
144
144
			for (Iterator it = node.getSourceConnections().iterator(); it
145
	/**
145
					.hasNext();) {
146
	 * @param graph
146
				update((GraphItem) it.next());
147
	 * @param element
147
			}
148
	 * @param refreshLabels
148
			for (Iterator it = node.getTargetConnections().iterator(); it
149
	 */
149
					.hasNext();) {
150
	private void reconnect(Graph graph, Object element, boolean refreshLabels) {
150
				update((GraphItem) it.next());
151
		GraphNode node = viewer.getGraphModelNode(element);
151
			}
152
		Object[] related = ((IGraphEntityContentProvider) getContentProvider()).getConnectedTo(element);
152
		}
153
		List connections = node.getSourceConnections();
153
	}
154
		LinkedList toAdd = new LinkedList();
154
155
		LinkedList toDelete = new LinkedList();
155
	/**
156
		LinkedList toKeep = new LinkedList();
156
	 * @param graph
157
		HashSet oldExternalConnections = new HashSet();
157
	 * @param element
158
		HashSet newExternalConnections = new HashSet();
158
	 * @param refreshLabels
159
		for (Iterator it = connections.iterator(); it.hasNext();) {
159
	 */
160
			oldExternalConnections.add(((GraphConnection) it.next()).getExternalConnection());
160
	private void reconnect(Graph graph, Object element, boolean refreshLabels) {
161
		}
161
		GraphNode node = viewer.getGraphModelNode(element);
162
		for (int i = 0; i < related.length; i++) {
162
		Object[] related = ((IGraphEntityContentProvider) getContentProvider())
163
			newExternalConnections.add(new EntityConnectionData(element, related[i]));
163
				.getConnectedTo(element);
164
		}
164
		List connections = node.getSourceConnections();
165
		for (Iterator it = oldExternalConnections.iterator(); it.hasNext();) {
165
		LinkedList toAdd = new LinkedList();
166
			Object next = it.next();
166
		LinkedList toDelete = new LinkedList();
167
			if (!newExternalConnections.contains(next)) {
167
		LinkedList toKeep = new LinkedList();
168
				toDelete.add(next);
168
		HashSet oldExternalConnections = new HashSet();
169
			} else {
169
		HashSet newExternalConnections = new HashSet();
170
				toKeep.add(next);
170
		for (Iterator it = connections.iterator(); it.hasNext();) {
171
			}
171
			oldExternalConnections.add(((GraphConnection) it.next())
172
		}
172
					.getExternalConnection());
173
		for (Iterator it = newExternalConnections.iterator(); it.hasNext();) {
173
		}
174
			Object next = it.next();
174
		for (int i = 0; i < related.length; i++) {
175
			if (!oldExternalConnections.contains(next)) {
175
			newExternalConnections.add(new EntityConnectionData(element,
176
				toAdd.add(next);
176
					related[i]));
177
			}
177
		}
178
		}
178
		for (Iterator it = oldExternalConnections.iterator(); it.hasNext();) {
179
		for (Iterator it = toDelete.iterator(); it.hasNext();) {
179
			Object next = it.next();
180
			viewer.removeGraphModelConnection(it.next());
180
			if (!newExternalConnections.contains(next)) {
181
		}
181
				toDelete.add(next);
182
		toDelete.clear();
182
			} else {
183
		LinkedList newNodeList = new LinkedList();
183
				toKeep.add(next);
184
		for (Iterator it = toAdd.iterator(); it.hasNext();) {
184
			}
185
			EntityConnectionData data = (EntityConnectionData) it.next();
185
		}
186
			GraphNode dest = viewer.getGraphModelNode(data.dest);
186
		for (Iterator it = newExternalConnections.iterator(); it.hasNext();) {
187
			if (dest == null) {
187
			Object next = it.next();
188
				newNodeList.add(data.dest);
188
			if (!oldExternalConnections.contains(next)) {
189
			}
189
				toAdd.add(next);
190
			createConnection(graph, data, data.source, data.dest);
190
			}
191
		}
191
		}
192
		toAdd.clear();
192
		for (Iterator it = toDelete.iterator(); it.hasNext();) {
193
		if (refreshLabels) {
193
			viewer.removeGraphModelConnection(it.next());
194
			for (Iterator i = toKeep.iterator(); i.hasNext();) {
194
		}
195
				styleItem(viewer.getGraphModelConnection(i.next()));
195
		toDelete.clear();
196
			}
196
		LinkedList newNodeList = new LinkedList();
197
		}
197
		for (Iterator it = toAdd.iterator(); it.hasNext();) {
198
		for (Iterator it = newNodeList.iterator(); it.hasNext();) {
198
			EntityConnectionData data = (EntityConnectionData) it.next();
199
			// refresh the new nodes so that we get a fully-up-to-date graph.
199
			GraphNode dest = viewer.getGraphModelNode(data.dest);
200
			refresh(graph, it.next());
200
			if (dest == null) {
201
		}
201
				newNodeList.add(data.dest);
202
	}
202
			}
203
203
			createConnection(graph, data, data.source, data.dest);
204
	/*
204
		}
205
	 * (non-Javadoc)
205
		toAdd.clear();
206
	 * 
206
		if (refreshLabels) {
207
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel,
207
			for (Iterator i = toKeep.iterator(); i.hasNext();) {
208
	 *      java.lang.Object, boolean)
208
				styleItem(viewer.getGraphModelConnection(i.next()));
209
	 */
209
			}
210
	public void refresh(Graph graph, Object element) {
210
		}
211
		refresh(graph, element, false);
211
		for (Iterator it = newNodeList.iterator(); it.hasNext();) {
212
	}
212
			// refresh the new nodes so that we get a fully-up-to-date graph.
213
213
			refresh(graph, it.next());
214
}
214
		}
215
	}
216
217
	/*
218
	 * (non-Javadoc)
219
	 * 
220
	 * @see
221
	 * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
222
	 * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object,
223
	 * boolean)
224
	 */
225
	public void refresh(Graph graph, Object element) {
226
		refresh(graph, element, false);
227
	}
228
229
}
(-)src/org/eclipse/zest/core/viewers/internal/GraphModelEntityRelationshipFactory.java (-137 / +152 lines)
Lines 1-137 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers.internal;
10
package org.eclipse.zest.core.viewers.internal;
11
11
12
import java.util.ArrayList;
12
import java.util.ArrayList;
13
import java.util.List;
13
import java.util.List;
14
14
15
import org.eclipse.zest.core.viewers.IGraphEntityRelationshipContentProvider;
15
import org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer;
16
import org.eclipse.zest.core.widgets.Graph;
16
import org.eclipse.zest.core.viewers.IGraphEntityRelationshipContentProvider;
17
import org.eclipse.zest.core.widgets.GraphContainer;
17
import org.eclipse.zest.core.widgets.Graph;
18
import org.eclipse.zest.core.widgets.GraphNode;
18
import org.eclipse.zest.core.widgets.GraphContainer;
19
19
import org.eclipse.zest.core.widgets.GraphNode;
20
/*
20
21
 * A factory for the IGraphEntityRelationshipContentProvider.
21
/*
22
 * 
22
 * A factory for the IGraphEntityRelationshipContentProvider.
23
 * @author Del Myers
23
 * 
24
 */
24
 * @author Del Myers
25
// @tag bug.154580-Content.fix
25
 */
26
// @tag bug.160367-Refreshing.fix : updated to use new
26
// @tag bug.154580-Content.fix
27
// AbstractStylingModelFactory
27
// @tag bug.160367-Refreshing.fix : updated to use new
28
public class GraphModelEntityRelationshipFactory extends AbstractStylingModelFactory {
28
// AbstractStylingModelFactory
29
29
public class GraphModelEntityRelationshipFactory extends
30
	public GraphModelEntityRelationshipFactory(AbstractStructuredGraphViewer viewer) {
30
		AbstractStylingModelFactory {
31
		super(viewer);
31
32
		if (!(viewer.getContentProvider() instanceof IGraphEntityRelationshipContentProvider)) {
32
	public GraphModelEntityRelationshipFactory(
33
			throw new IllegalArgumentException("Expected IGraphEntityRelationshipContentProvider");
33
			AbstractStructuredGraphViewer viewer) {
34
		}
34
		super(viewer);
35
	}
35
		if (!(viewer.getContentProvider() instanceof IGraphEntityRelationshipContentProvider)) {
36
36
			throw new IllegalArgumentException(
37
	/*
37
					"Expected IGraphEntityRelationshipContentProvider");
38
	 * (non-Javadoc)
38
		}
39
	 * 
39
	}
40
	 * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#createGraphModel()
40
41
	 */
41
	/*
42
	public Graph createGraphModel(Graph model) {
42
	 * (non-Javadoc)
43
		doBuildGraph(model);
43
	 * 
44
		return model;
44
	 * @see
45
	}
45
	 * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory
46
46
	 * #createGraphModel()
47
	/*
47
	 */
48
	 * (non-Javadoc)
48
	public Graph createGraphModel(Graph model) {
49
	 * 
49
		doBuildGraph(model);
50
	 * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
50
		return model;
51
	 */
51
	}
52
	protected void doBuildGraph(Graph model) {
52
53
		super.doBuildGraph(model);
53
	/*
54
		Object[] nodes = getContentProvider().getElements(getViewer().getInput());
54
	 * (non-Javadoc)
55
		nodes = filter(getViewer().getInput(), nodes);
55
	 * 
56
		createModelNodes(model, nodes);
56
	 * @see
57
		createModelRelationships(model);
57
	 * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory
58
	}
58
	 * #doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
59
59
	 */
60
	/**
60
	protected void doBuildGraph(Graph model) {
61
	 * Creates all the model relationships. Assumes that all of the model nodes
61
		super.doBuildGraph(model);
62
	 * have been created in the graph model already. Runtime O(n^2) + O(r).
62
		Object[] nodes = getContentProvider().getElements(
63
	 * 
63
				getViewer().getInput());
64
	 * @param model
64
		nodes = filter(getViewer().getInput(), nodes);
65
	 *            the model to create the relationship on.
65
		createModelNodes(model, nodes);
66
	 */
66
		createModelRelationships(model);
67
	private void createModelRelationships(Graph model) {
67
	}
68
		GraphNode[] modelNodes = getNodesArray(model);
68
69
		List listOfNodes = new ArrayList();
69
	/**
70
		for (int i = 0; i < modelNodes.length; i++) {
70
	 * Creates all the model relationships. Assumes that all of the model nodes
71
			listOfNodes.add(modelNodes[i]);
71
	 * have been created in the graph model already. Runtime O(n^2) + O(r).
72
		}
72
	 * 
73
73
	 * @param model
74
		for (int i = 0; i < listOfNodes.size(); i++) {
74
	 *            the model to create the relationship on.
75
			GraphNode node = (GraphNode) listOfNodes.get(i);
75
	 */
76
			if (node instanceof GraphContainer) {
76
	private void createModelRelationships(Graph model) {
77
				List childNodes = ((GraphContainer) node).getNodes();
77
		GraphNode[] modelNodes = getNodesArray(model);
78
				listOfNodes.addAll(childNodes);
78
		List listOfNodes = new ArrayList();
79
			}
79
		for (int i = 0; i < modelNodes.length; i++) {
80
		}
80
			listOfNodes.add(modelNodes[i]);
81
		modelNodes = (GraphNode[]) listOfNodes.toArray(new GraphNode[listOfNodes.size()]);
81
		}
82
82
83
		IGraphEntityRelationshipContentProvider content = getCastedContent();
83
		for (int i = 0; i < listOfNodes.size(); i++) {
84
		for (int i = 0; i < modelNodes.length; i++) {
84
			GraphNode node = (GraphNode) listOfNodes.get(i);
85
			for (int j = 0; j < modelNodes.length; j++) {
85
			if (node instanceof GraphContainer) {
86
				Object[] rels = content.getRelationships(modelNodes[i].getData(), modelNodes[j].getData());
86
				List childNodes = ((GraphContainer) node).getNodes();
87
				if (rels != null) {
87
				listOfNodes.addAll(childNodes);
88
					rels = filter(getViewer().getInput(), rels);
88
			}
89
					for (int r = 0; r < rels.length; r++) {
89
		}
90
						createConnection(model, rels[r], modelNodes[i].getData(), modelNodes[j].getData());
90
		modelNodes = (GraphNode[]) listOfNodes
91
					}
91
				.toArray(new GraphNode[listOfNodes.size()]);
92
				}
92
93
			}
93
		IGraphEntityRelationshipContentProvider content = getCastedContent();
94
		}
94
		for (int i = 0; i < modelNodes.length; i++) {
95
	}
95
			for (int j = 0; j < modelNodes.length; j++) {
96
96
				Object[] rels = content.getRelationships(modelNodes[i]
97
	/**
97
						.getData(), modelNodes[j].getData());
98
	 * Creates the model nodes for the given external nodes.
98
				if (rels != null) {
99
	 * 
99
					rels = filter(getViewer().getInput(), rels);
100
	 * @param model
100
					for (int r = 0; r < rels.length; r++) {
101
	 *            the graph model.
101
						createConnection(model, rels[r], modelNodes[i]
102
	 * @param nodes
102
								.getData(), modelNodes[j].getData());
103
	 *            the external nodes.
103
					}
104
	 */
104
				}
105
	private void createModelNodes(Graph model, Object[] nodes) {
105
			}
106
		for (int i = 0; i < nodes.length; i++) {
106
		}
107
			createNode(model, nodes[i]);
107
	}
108
		}
108
109
	}
109
	/**
110
110
	 * Creates the model nodes for the given external nodes.
111
	/*
111
	 * 
112
	 * (non-Javadoc)
112
	 * @param model
113
	 * 
113
	 *            the graph model.
114
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel,
114
	 * @param nodes
115
	 *      java.lang.Object)
115
	 *            the external nodes.
116
	 */
116
	 */
117
	public void refresh(Graph graph, Object element) {
117
	private void createModelNodes(Graph model, Object[] nodes) {
118
		refresh(graph, element, false);
118
		for (int i = 0; i < nodes.length; i++) {
119
	}
119
			createNode(model, nodes[i]);
120
120
		}
121
	/*
121
	}
122
	 * (non-Javadoc)
122
123
	 * 
123
	/*
124
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel,
124
	 * (non-Javadoc)
125
	 *      java.lang.Object, boolean)
125
	 * 
126
	 */
126
	 * @see
127
	public void refresh(Graph graph, Object element, boolean updateLabels) {
127
	 * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
128
		// with this kind of graph, it is just as easy and cost-effective to
128
	 * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
129
		// rebuild the whole thing.
129
	 */
130
		refreshGraph(graph);
130
	public void refresh(Graph graph, Object element) {
131
	}
131
		refresh(graph, element, false);
132
132
	}
133
	private IGraphEntityRelationshipContentProvider getCastedContent() {
133
134
		return (IGraphEntityRelationshipContentProvider) getContentProvider();
134
	/*
135
	}
135
	 * (non-Javadoc)
136
136
	 * 
137
}
137
	 * @see
138
	 * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
139
	 * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object,
140
	 * boolean)
141
	 */
142
	public void refresh(Graph graph, Object element, boolean updateLabels) {
143
		// with this kind of graph, it is just as easy and cost-effective to
144
		// rebuild the whole thing.
145
		refreshGraph(graph);
146
	}
147
148
	private IGraphEntityRelationshipContentProvider getCastedContent() {
149
		return (IGraphEntityRelationshipContentProvider) getContentProvider();
150
	}
151
152
}
(-)src/org/eclipse/zest/core/viewers/internal/GraphModelFactory.java (-171 / +181 lines)
Lines 1-171 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials are made
3
 * All rights reserved. This program and the accompanying materials are made
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.viewers.internal;
10
package org.eclipse.zest.core.viewers.internal;
11
11
12
import java.util.Iterator;
12
import java.util.Iterator;
13
import java.util.List;
13
import java.util.List;
14
14
15
import org.eclipse.zest.core.viewers.IFigureProvider;
15
import org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer;
16
import org.eclipse.zest.core.viewers.IGraphContentProvider;
16
import org.eclipse.zest.core.viewers.IFigureProvider;
17
import org.eclipse.zest.core.widgets.Graph;
17
import org.eclipse.zest.core.viewers.IGraphContentProvider;
18
import org.eclipse.zest.core.widgets.GraphConnection;
18
import org.eclipse.zest.core.widgets.Graph;
19
import org.eclipse.zest.core.widgets.GraphNode;
19
import org.eclipse.zest.core.widgets.GraphConnection;
20
20
import org.eclipse.zest.core.widgets.GraphNode;
21
/**
21
22
 * This factory helps make models (nodes & connections).
22
/**
23
 * 
23
 * This factory helps make models (nodes & connections).
24
 * @author Ian Bull
24
 * 
25
 * @author Chris Callendar
25
 * @author Ian Bull
26
 */
26
 * @author Chris Callendar
27
public class GraphModelFactory extends AbstractStylingModelFactory {
27
 */
28
28
public class GraphModelFactory extends AbstractStylingModelFactory {
29
	AbstractStructuredGraphViewer viewer = null;
29
30
30
	AbstractStructuredGraphViewer viewer = null;
31
	public GraphModelFactory(AbstractStructuredGraphViewer viewer) {
31
32
		super(viewer);
32
	public GraphModelFactory(AbstractStructuredGraphViewer viewer) {
33
		this.viewer = viewer;
33
		super(viewer);
34
	}
34
		this.viewer = viewer;
35
35
	}
36
	/*
36
37
	 * (non-Javadoc)
37
	/*
38
	 * 
38
	 * (non-Javadoc)
39
	 * @see ca.uvic.cs.zest.internal.graphmodel.IGraphModelFactory#createModel()
39
	 * 
40
	 */
40
	 * @see ca.uvic.cs.zest.internal.graphmodel.IGraphModelFactory#createModel()
41
	public Graph createGraphModel(Graph model) {
41
	 */
42
		doBuildGraph(model);
42
	public Graph createGraphModel(Graph model) {
43
		return model;
43
		doBuildGraph(model);
44
	}
44
		return model;
45
45
	}
46
	/*
46
47
	 * (non-Javadoc)
47
	/*
48
	 * 
48
	 * (non-Javadoc)
49
	 * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
49
	 * 
50
	 */
50
	 * @see
51
	protected void doBuildGraph(Graph model) {
51
	 * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory
52
		super.doBuildGraph(model);
52
	 * #doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
53
		// make the model have the same styles as the viewer
53
	 */
54
		Object rels[] = getContentProvider().getElements(getViewer().getInput());
54
	protected void doBuildGraph(Graph model) {
55
		if (rels != null) {
55
		super.doBuildGraph(model);
56
			IFigureProvider figureProvider = null;
56
		// make the model have the same styles as the viewer
57
			if (getLabelProvider() instanceof IFigureProvider) {
57
		Object rels[] = getContentProvider()
58
				figureProvider = (IFigureProvider) getLabelProvider();
58
				.getElements(getViewer().getInput());
59
			}
59
		if (rels != null) {
60
60
			IFigureProvider figureProvider = null;
61
			// If rels returns null then just continue
61
			if (getLabelProvider() instanceof IFigureProvider) {
62
			// @tag zest(bug(134928(fix))) : An empty graph causes an NPE
62
				figureProvider = (IFigureProvider) getLabelProvider();
63
			for (int i = 0; i < rels.length; i++) {
63
			}
64
				// Check the filter on the source
64
65
				Object source = getCastedContent().getSource(rels[i]);
65
			// If rels returns null then just continue
66
				source = filterElement(getViewer().getInput(), source) ? null : source;
66
			// @tag zest(bug(134928(fix))) : An empty graph causes an NPE
67
67
			for (int i = 0; i < rels.length; i++) {
68
				// Check hte filter on the dest
68
				// Check the filter on the source
69
				Object dest = getCastedContent().getDestination(rels[i]);
69
				Object source = getCastedContent().getSource(rels[i]);
70
				dest = filterElement(getViewer().getInput(), dest) ? null : dest;
70
				source = filterElement(getViewer().getInput(), source) ? null
71
71
						: source;
72
				if (source == null) {
72
73
					// just create the node for the destination
73
				// Check hte filter on the dest
74
					if (dest != null) {
74
				Object dest = getCastedContent().getDestination(rels[i]);
75
						if (figureProvider != null) {
75
				dest = filterElement(getViewer().getInput(), dest) ? null
76
							createNode(model, dest, figureProvider.getFigure(dest));
76
						: dest;
77
						} else {
77
78
							createNode(model, dest);
78
				if (source == null) {
79
						}
79
					// just create the node for the destination
80
					}
80
					if (dest != null) {
81
					continue;
81
						if (figureProvider != null) {
82
				} else if (dest == null) {
82
							createNode(model, dest, figureProvider
83
					// just create the node for the source
83
									.getFigure(dest));
84
					if (source != null) {
84
						} else {
85
						if (figureProvider != null) {
85
							createNode(model, dest);
86
							createNode(model, source, figureProvider.getFigure(dest));
86
						}
87
						} else {
87
					}
88
							createNode(model, source);
88
					continue;
89
						}
89
				} else if (dest == null) {
90
					}
90
					// just create the node for the source
91
					continue;
91
					if (source != null) {
92
				}
92
						if (figureProvider != null) {
93
				// If any of the source, dest is null or the edge is filtered,
93
							createNode(model, source, figureProvider
94
				// don't create the graph.
94
									.getFigure(dest));
95
				if (source != null && dest != null && !filterElement(getViewer().getInput(), rels[i])) {
95
						} else {
96
					createConnection(model, rels[i], getCastedContent().getSource(rels[i]), getCastedContent().getDestination(rels[i]));
96
							createNode(model, source);
97
				}
97
						}
98
			}
98
					}
99
		}
99
					continue;
100
100
				}
101
	}
101
				// If any of the source, dest is null or the edge is filtered,
102
102
				// don't create the graph.
103
	private IGraphContentProvider getCastedContent() {
103
				if (source != null && dest != null
104
		return (IGraphContentProvider) getContentProvider();
104
						&& !filterElement(getViewer().getInput(), rels[i])) {
105
	}
105
					createConnection(model, rels[i], getCastedContent()
106
106
							.getSource(rels[i]), getCastedContent()
107
	/*
107
							.getDestination(rels[i]));
108
	 * (non-Javadoc)
108
				}
109
	 * 
109
			}
110
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel,
110
		}
111
	 *      java.lang.Object)
111
112
	 */
112
	}
113
	public void refresh(Graph graph, Object element) {
113
114
		refresh(graph, element, false);
114
	private IGraphContentProvider getCastedContent() {
115
	}
115
		return (IGraphContentProvider) getContentProvider();
116
116
	}
117
	/*
117
118
	 * (non-Javadoc)
118
	/*
119
	 * 
119
	 * (non-Javadoc)
120
	 * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel,
120
	 * 
121
	 *      java.lang.Object, boolean)
121
	 * @see
122
	 */
122
	 * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
123
	public void refresh(Graph graph, Object element, boolean updateLabels) {
123
	 * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
124
		GraphConnection conn = viewer.getGraphModelConnection(element);
124
	 */
125
		if (conn == null) {
125
	public void refresh(Graph graph, Object element) {
126
			// did the user send us a node? Check all of the connections on the
126
		refresh(graph, element, false);
127
			// node.
127
	}
128
			GraphNode node = viewer.getGraphModelNode(element);
128
129
			if (node != null) {
129
	/*
130
				List connections = node.getSourceConnections();
130
	 * (non-Javadoc)
131
				for (Iterator it = connections.iterator(); it.hasNext();) {
131
	 * 
132
					GraphConnection c = (GraphConnection) it.next();
132
	 * @see
133
					refresh(graph, c.getExternalConnection(), updateLabels);
133
	 * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
134
				}
134
	 * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object,
135
				connections = node.getTargetConnections();
135
	 * boolean)
136
				for (Iterator it = connections.iterator(); it.hasNext();) {
136
	 */
137
					GraphConnection c = (GraphConnection) it.next();
137
	public void refresh(Graph graph, Object element, boolean updateLabels) {
138
					refresh(graph, c.getExternalConnection(), updateLabels);
138
		GraphConnection conn = viewer.getGraphModelConnection(element);
139
				}
139
		if (conn == null) {
140
			}
140
			// did the user send us a node? Check all of the connections on the
141
			return;
141
			// node.
142
		}
142
			GraphNode node = viewer.getGraphModelNode(element);
143
		Object oldSource = conn.getSource().getData();
143
			if (node != null) {
144
		Object oldDest = conn.getDestination().getData();
144
				List connections = node.getSourceConnections();
145
		Object newSource = getCastedContent().getSource(element);
145
				for (Iterator it = connections.iterator(); it.hasNext();) {
146
		Object newDest = getCastedContent().getDestination(element);
146
					GraphConnection c = (GraphConnection) it.next();
147
		if (!(oldSource.equals(newSource) && oldDest.equals(newDest))) {
147
					refresh(graph, c.getExternalConnection(), updateLabels);
148
			GraphNode internalSource = viewer.getGraphModelNode(newSource);
148
				}
149
			GraphNode internalDest = viewer.getGraphModelNode(newDest);
149
				connections = node.getTargetConnections();
150
			if (internalSource == null) {
150
				for (Iterator it = connections.iterator(); it.hasNext();) {
151
				internalSource = createNode(graph, newSource);
151
					GraphConnection c = (GraphConnection) it.next();
152
			} else if (updateLabels) {
152
					refresh(graph, c.getExternalConnection(), updateLabels);
153
				styleItem(internalSource);
153
				}
154
			}
154
			}
155
			if (internalDest == null) {
155
			return;
156
				internalDest = createNode(graph, newDest);
156
		}
157
			} else if (updateLabels) {
157
		Object oldSource = conn.getSource().getData();
158
				styleItem(internalDest);
158
		Object oldDest = conn.getDestination().getData();
159
			}
159
		Object newSource = getCastedContent().getSource(element);
160
160
		Object newDest = getCastedContent().getDestination(element);
161
			// @tag TODO: Remove these lines
161
		if (!(oldSource.equals(newSource) && oldDest.equals(newDest))) {
162
			// conn.disconnect();
162
			GraphNode internalSource = viewer.getGraphModelNode(newSource);
163
			// conn.reconnect(internalSource, internalDest);
163
			GraphNode internalDest = viewer.getGraphModelNode(newDest);
164
			if (updateLabels) {
164
			if (internalSource == null) {
165
				styleItem(conn);
165
				internalSource = createNode(graph, newSource);
166
			}
166
			} else if (updateLabels) {
167
		}
167
				styleItem(internalSource);
168
168
			}
169
	}
169
			if (internalDest == null) {
170
170
				internalDest = createNode(graph, newDest);
171
}
171
			} else if (updateLabels) {
172
				styleItem(internalDest);
173
			}
174
			if (updateLabels) {
175
				styleItem(conn);
176
			}
177
		}
178
179
	}
180
181
}
(-)src/org/eclipse/zest/core/viewers/internal/IStylingGraphModelFactory.java (-171 / +172 lines)
Lines 1-171 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.viewers.internal;
11
package org.eclipse.zest.core.viewers.internal;
12
12
13
import org.eclipse.jface.viewers.IBaseLabelProvider;
13
import org.eclipse.jface.viewers.IBaseLabelProvider;
14
import org.eclipse.jface.viewers.IStructuredContentProvider;
14
import org.eclipse.jface.viewers.IStructuredContentProvider;
15
import org.eclipse.jface.viewers.StructuredViewer;
15
import org.eclipse.jface.viewers.StructuredViewer;
16
import org.eclipse.zest.core.widgets.Graph;
16
import org.eclipse.zest.core.widgets.Graph;
17
import org.eclipse.zest.core.widgets.GraphConnection;
17
import org.eclipse.zest.core.widgets.GraphConnection;
18
import org.eclipse.zest.core.widgets.GraphItem;
18
import org.eclipse.zest.core.widgets.GraphItem;
19
import org.eclipse.zest.core.widgets.GraphNode;
19
import org.eclipse.zest.core.widgets.GraphNode;
20
20
21
/**
21
/**
22
 * A Graph model factory that supports the structural and visual refreshing of
22
 * A Graph model factory that supports the structural and visual refreshing of
23
 * graph elements based on the content provider and label provider in the viewer
23
 * graph elements based on the content provider and label provider in the viewer
24
 * that this factory is associated with. Model elements are created using the
24
 * that this factory is associated with. Model elements are created using the
25
 * content provider supplied by getContentProvider(), and styled using the label
25
 * content provider supplied by getContentProvider(), and styled using the label
26
 * provider supplied by getLabelProvider(). By the end of creation and
26
 * provider supplied by getLabelProvider(). By the end of creation and
27
 * refreshing, the graph model elements are expected to be styled according to
27
 * refreshing, the graph model elements are expected to be styled according to
28
 * the given label provider, however, default styles are dependant on the
28
 * the given label provider, however, default styles are dependant on the
29
 * particular implementation of IStylingGraphModelFactory. Unless otherwise
29
 * particular implementation of IStylingGraphModelFactory. Unless otherwise
30
 * documented, clients should expect that the implementation of
30
 * documented, clients should expect that the implementation of
31
 * IStylingGraphModelFactory adheres to the general defaults found in
31
 * IStylingGraphModelFactory adheres to the general defaults found in
32
 * {@link IZestGraphDefaults}.
32
 * {@link IZestGraphDefaults}.
33
 * 
33
 * 
34
 * @author Del Myers
34
 * @author Del Myers
35
 */
35
 */
36
36
37
public interface IStylingGraphModelFactory {
37
public interface IStylingGraphModelFactory {
38
	/**
38
	/**
39
	 * Returns the label provider used in this factory.
39
	 * Returns the label provider used in this factory.
40
	 * 
40
	 * 
41
	 * @return the label provider used in this factory.
41
	 * @return the label provider used in this factory.
42
	 */
42
	 */
43
	public IBaseLabelProvider getLabelProvider();
43
	public IBaseLabelProvider getLabelProvider();
44
44
45
	/**
45
	/**
46
	 * Returns the content provider used in this factory.
46
	 * Returns the content provider used in this factory.
47
	 * 
47
	 * 
48
	 * @return the content provider used in this factory.
48
	 * @return the content provider used in this factory.
49
	 */
49
	 */
50
	public IStructuredContentProvider getContentProvider();
50
	public IStructuredContentProvider getContentProvider();
51
51
52
	/**
52
	/**
53
	 * Creates and returns the graph model from this factory based on the label
53
	 * Creates and returns the graph model from this factory based on the label
54
	 * provider and the label provider returned in getContentProvider() and
54
	 * provider and the label provider returned in getContentProvider() and
55
	 * getLabelProvider().
55
	 * getLabelProvider().
56
	 * 
56
	 * 
57
	 * @return the created graph model.
57
	 * @return the created graph model.
58
	 */
58
	 */
59
	public Graph createGraphModel(Graph model);
59
	public Graph createGraphModel(Graph model);
60
60
61
	/**
61
	/**
62
	 * Creates and returns a node on the given graph based on the user model
62
	 * Creates and returns a node on the given graph based on the user model
63
	 * data, "data", using the content provider returned by
63
	 * data, "data", using the content provider returned by
64
	 * getContentProvider(). They node will also be styled according to the
64
	 * getContentProvider(). They node will also be styled according to the
65
	 * information given by the label provider. If the node already exists in
65
	 * information given by the label provider. If the node already exists in
66
	 * the graph, it is restyled and returned; no new node is created.
66
	 * the graph, it is restyled and returned; no new node is created.
67
	 * 
67
	 * 
68
	 * @param graph
68
	 * @param graph
69
	 *            the graph to create or retrieve the node on.
69
	 *            the graph to create or retrieve the node on.
70
	 * @param element
70
	 * @param element
71
	 *            the user model data to use in the node.
71
	 *            the user model data to use in the node.
72
	 * @return the node created or retrieved for the given graph.
72
	 * @return the node created or retrieved for the given graph.
73
	 */
73
	 */
74
	public GraphNode createNode(Graph graph, Object element);
74
	public GraphNode createNode(Graph graph, Object element);
75
75
76
	/**
76
	/**
77
	 * Creates and returns a connection with the given source and destination
77
	 * Creates and returns a connection with the given source and destination
78
	 * objects from the user model. If the source and destination nodes don't
78
	 * objects from the user model. If the source and destination nodes don't
79
	 * exist for the given user model objects, they are created using
79
	 * exist for the given user model objects, they are created using
80
	 * createNode(GraphModel, Object). If a connection already exists for the
80
	 * createNode(GraphModel, Object). If a connection already exists for the
81
	 * given user data, but with different source or destinations, it is
81
	 * given user data, but with different source or destinations, it is
82
	 * disconnected and reconnected to the given source and destination. It is
82
	 * disconnected and reconnected to the given source and destination. It is
83
	 * always styled according to the label provider provided by
83
	 * always styled according to the label provider provided by
84
	 * getLabelProvider().
84
	 * getLabelProvider().
85
	 * 
85
	 * 
86
	 * @param graph
86
	 * @param graph
87
	 *            the graph to create or retrieve the connection on.
87
	 *            the graph to create or retrieve the connection on.
88
	 * @param element
88
	 * @param element
89
	 *            the user model data to use in this connection.
89
	 *            the user model data to use in this connection.
90
	 * @param source
90
	 * @param source
91
	 *            the user model data used for the source node.
91
	 *            the user model data used for the source node.
92
	 * @param dest
92
	 * @param dest
93
	 *            the user model data used for the destination node.
93
	 *            the user model data used for the destination node.
94
	 * @return the created or retrieved connection for the given graph.
94
	 * @return the created or retrieved connection for the given graph.
95
	 */
95
	 */
96
	public GraphConnection createConnection(Graph graph, Object element, Object source, Object dest);
96
	public GraphConnection createConnection(Graph graph, Object element,
97
97
			Object source, Object dest);
98
	/**
98
99
	 * Restyles the given graph items according to the label provider supplied
99
	/**
100
	 * by getLabelProvider().
100
	 * Restyles the given graph items according to the label provider supplied
101
	 * 
101
	 * by getLabelProvider().
102
	 * @param items
102
	 * 
103
	 *            the items to update.
103
	 * @param items
104
	 */
104
	 *            the items to update.
105
	public void update(GraphItem[] items);
105
	 */
106
106
	public void update(GraphItem[] items);
107
	/**
107
108
	 * Restyles the given graph item according to the label provider supplied by
108
	/**
109
	 * getLabelProvider().
109
	 * Restyles the given graph item according to the label provider supplied by
110
	 * 
110
	 * getLabelProvider().
111
	 * @param item
111
	 * 
112
	 *            the item to update.
112
	 * @param item
113
	 */
113
	 *            the item to update.
114
	public void update(GraphItem item);
114
	 */
115
115
	public void update(GraphItem item);
116
	/**
116
117
	 * Structurally refreshes the graph model nodes and connections associated
117
	/**
118
	 * with the given user model element. Does nothing if the element does not
118
	 * Structurally refreshes the graph model nodes and connections associated
119
	 * currently exist in the view. No restyling is done by default.
119
	 * with the given user model element. Does nothing if the element does not
120
	 * 
120
	 * currently exist in the view. No restyling is done by default.
121
	 * @param graph
121
	 * 
122
	 * @param element
122
	 * @param graph
123
	 *            the element to restructure.
123
	 * @param element
124
	 */
124
	 *            the element to restructure.
125
	public void refresh(Graph graph, Object element);
125
	 */
126
126
	public void refresh(Graph graph, Object element);
127
	/**
127
128
	 * Structurally refreshes the graph model nodes and connections associated
128
	/**
129
	 * with the given user model element. If updateLabels is true, then the
129
	 * Structurally refreshes the graph model nodes and connections associated
130
	 * labels are updated as well. Does nothing if the element does not
130
	 * with the given user model element. If updateLabels is true, then the
131
	 * currently exist in the view.
131
	 * labels are updated as well. Does nothing if the element does not
132
	 * 
132
	 * currently exist in the view.
133
	 * @param graph
133
	 * 
134
	 *            the graph to find the element on.
134
	 * @param graph
135
	 * @param element
135
	 *            the graph to find the element on.
136
	 *            the user model element.
136
	 * @param element
137
	 * @param updateLabels
137
	 *            the user model element.
138
	 *            true if the labels should be updated as well.
138
	 * @param updateLabels
139
	 */
139
	 *            true if the labels should be updated as well.
140
	public void refresh(Graph graph, Object element, boolean updateLabels);
140
	 */
141
141
	public void refresh(Graph graph, Object element, boolean updateLabels);
142
	/**
142
143
	 * Structurally refreshes the entire graph.
143
	/**
144
	 * 
144
	 * Structurally refreshes the entire graph.
145
	 * @param graph
145
	 * 
146
	 *            the graph to refresh;
146
	 * @param graph
147
	 */
147
	 *            the graph to refresh;
148
	public void refreshGraph(Graph graph);
148
	 */
149
149
	public void refreshGraph(Graph graph);
150
	/**
150
151
	 * Returns the viewer that this factory is building the model for.
151
	/**
152
	 * 
152
	 * Returns the viewer that this factory is building the model for.
153
	 * @return the viewer that this factory is building the model for.
153
	 * 
154
	 */
154
	 * @return the viewer that this factory is building the model for.
155
	public StructuredViewer getViewer();
155
	 */
156
156
	public StructuredViewer getViewer();
157
	public void setConnectionStyle(int style);
157
158
158
	public void setConnectionStyle(int style);
159
	/**
159
160
	 * @return the connectionStyle
160
	/**
161
	 */
161
	 * @return the connectionStyle
162
	public int getConnectionStyle();
162
	 */
163
163
	public int getConnectionStyle();
164
	public void setNodeStyle(int style);
164
165
165
	public void setNodeStyle(int style);
166
	/**
166
167
	 * @return the nodeStyle
167
	/**
168
	 */
168
	 * @return the nodeStyle
169
	public int getNodeStyle();
169
	 */
170
170
	public int getNodeStyle();
171
}
171
172
}
(-)src/org/eclipse/zest/core/viewers/internal/SharedMessages.java (-32 / +32 lines)
Lines 1-32 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2003, 2005 IBM Corporation and others. All rights reserved.
2
 * Copyright (c) 2003, 2005-2010 IBM Corporation and others. All rights reserved.
3
 * This program and the accompanying materials are made available under the
3
 * This program and the accompanying materials are made available under the
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
6
 * 
7
 * Contributors: IBM Corporation - initial API and implementation
7
 * Contributors: IBM Corporation - initial API and implementation
8
 ******************************************************************************/
8
 ******************************************************************************/
9
package org.eclipse.zest.core.viewers.internal;
9
package org.eclipse.zest.core.viewers.internal;
10
10
11
/**
11
/**
12
 * This class contains UI strings (translated, if available) that clients can
12
 * This class contains UI strings (translated, if available) that clients can
13
 * use.
13
 * use.
14
 * 
14
 * 
15
 * @author Eric Bordeau
15
 * @author Eric Bordeau
16
 */
16
 */
17
public class SharedMessages {
17
public class SharedMessages {
18
18
19
	/**
19
	/**
20
	 * The string "Page".
20
	 * The string "Page".
21
	 */
21
	 */
22
	public static String FitAllAction_Label = "Page"; //GEFMessages.FitAllAction_Label;
22
	public static String FitAllAction_Label = "Page"; // GEFMessages.FitAllAction_Label;
23
	/**
23
	/**
24
	 * The string "Width".
24
	 * The string "Width".
25
	 */
25
	 */
26
	public static String FitWidthAction_Label = "Width"; //GEFMessages.FitWidthAction_Label;
26
	public static String FitWidthAction_Label = "Width"; // GEFMessages.FitWidthAction_Label;
27
	/**
27
	/**
28
	 * The string "Height".
28
	 * The string "Height".
29
	 */
29
	 */
30
	public static String FitHeightAction_Label = "Height"; //GEFMessages.FitHeightAction_Label;
30
	public static String FitHeightAction_Label = "Height"; // GEFMessages.FitHeightAction_Label;
31
31
32
}
32
}
(-)src/org/eclipse/zest/core/widgets/DAGExpandCollapseManager.java (+300 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import java.util.HashSet;
13
import java.util.Iterator;
14
15
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
16
import org.eclipse.zest.layouts.interfaces.ContextListener;
17
import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager;
18
import org.eclipse.zest.layouts.interfaces.GraphStructureListener;
19
import org.eclipse.zest.layouts.interfaces.LayoutContext;
20
import org.eclipse.zest.layouts.interfaces.NodeLayout;
21
22
/**
23
 * <p>
24
 * An {@link ExpandCollapseManager} specialized for Directed Acyclic Graphs. It
25
 * works correctly only when all connections are directed (and of course nodes
26
 * form an acyclic graph). It's supposed to be used with
27
 * {@link InternalLayoutContext}.
28
 * </p>
29
 * <p>
30
 * When a node is collapsed, all its outgoing connections are hidden and these
31
 * successors that have no visible incoming nodes are pruned. When a node is
32
 * expanded, all its successors are unpruned and connections pointing to them
33
 * are shown.
34
 * </p>
35
 * </p>
36
 * <p>
37
 * <b>NOTE:</b> A <code>Graph</code> using this manger should use
38
 * {@link DefaultSubgraph}, which doesn't show any information about subgraphs
39
 * in the graph. That's because for this manager it doesn't matter which
40
 * subgraph a node belongs to (each pruning creates a new subgraph). Also, this
41
 * manager adds a label to each collapsed node showing number of its successors.
42
 * </p>
43
 * One instance of this class can serve only one instance of <code>Graph</code>.
44
 * 
45
 * @since 2.0
46
 */
47
public class DAGExpandCollapseManager implements ExpandCollapseManager {
48
49
	private InternalLayoutContext context;
50
51
	private HashSet expandedNodes = new HashSet();
52
53
	private HashSet nodesToPrune = new HashSet();
54
55
	private HashSet nodesToUnprune = new HashSet();
56
57
	private HashSet nodesToUpdate = new HashSet();
58
59
	private boolean cleanLayoutScheduled = false;
60
61
	public void initExpansion(final LayoutContext context2) {
62
		if (!(context2 instanceof InternalLayoutContext)) {
63
			throw new RuntimeException(
64
					"This manager works only with org.eclipse.zest.core.widgets.InternalLayoutContext");
65
		}
66
		context = (InternalLayoutContext) context2;
67
68
		context.addGraphStructureListener(new GraphStructureListener() {
69
			public boolean nodeRemoved(LayoutContext context, NodeLayout node) {
70
				if (isExpanded(node)) {
71
					collapse(node);
72
				}
73
				flushChanges(false, true);
74
				return false;
75
			}
76
77
			public boolean nodeAdded(LayoutContext context, NodeLayout node) {
78
				resetState(node);
79
				flushChanges(false, true);
80
				return false;
81
			}
82
83
			public boolean connectionRemoved(LayoutContext context,
84
					ConnectionLayout connection) {
85
				NodeLayout target = connection.getTarget();
86
				if (!isExpanded(target)
87
						&& target.getIncomingConnections().length == 0) {
88
					expand(target);
89
				}
90
				flushChanges(false, true);
91
				return false;
92
			}
93
94
			public boolean connectionAdded(LayoutContext context,
95
					ConnectionLayout connection) {
96
				resetState(connection.getTarget());
97
				updateNodeLabel(connection.getSource());
98
				flushChanges(false, true);
99
				return false;
100
			}
101
102
		});
103
104
		context.addContextListener(new ContextListener.Stub() {
105
			public void backgroundEnableChanged(LayoutContext context) {
106
				flushChanges(false, false);
107
			}
108
		});
109
	}
110
111
	public boolean canCollapse(LayoutContext context, NodeLayout node) {
112
		return isExpanded(node) && !node.isPruned()
113
				&& node.getOutgoingConnections().length > 0;
114
	}
115
116
	public boolean canExpand(LayoutContext context, NodeLayout node) {
117
		return !isExpanded(node) && !node.isPruned()
118
				&& node.getOutgoingConnections().length > 0;
119
	}
120
121
	private void collapseAllConnections(NodeLayout node) {
122
		ConnectionLayout[] outgoingConnections = node.getOutgoingConnections();
123
		for (int i = 0; i < outgoingConnections.length; i++) {
124
			outgoingConnections[i].setVisible(false);
125
		}
126
		flushChanges(true, true);
127
	}
128
129
	private void expandAllConnections(NodeLayout node) {
130
		ConnectionLayout[] outgoingConnections = node.getOutgoingConnections();
131
		for (int i = 0; i < outgoingConnections.length; i++) {
132
			outgoingConnections[i].setVisible(true);
133
		}
134
		flushChanges(true, true);
135
	}
136
137
	public void setExpanded(LayoutContext context, NodeLayout node,
138
			boolean expanded) {
139
140
		// if (isExpanded(node) == expanded)
141
		// return;
142
		if (expanded) {
143
			if (canExpand(context, node)) {
144
				expand(node);
145
			}
146
			expandAllConnections(node);
147
		} else {
148
			if (canCollapse(context, node)) {
149
				collapse(node);
150
			}
151
			collapseAllConnections(node);
152
		}
153
		flushChanges(true, true);
154
	}
155
156
	private void expand(NodeLayout node) {
157
		setExpanded(node, true);
158
		NodeLayout[] successingNodes = node.getSuccessingNodes();
159
		for (int i = 0; i < successingNodes.length; i++) {
160
			unpruneNode(successingNodes[i]);
161
		}
162
		updateNodeLabel(node);
163
	}
164
165
	private void collapse(NodeLayout node) {
166
		if (isExpanded(node)) {
167
			setExpanded(node, false);
168
		} else {
169
			return;
170
		}
171
		NodeLayout[] successors = node.getSuccessingNodes();
172
		for (int i = 0; i < successors.length; i++) {
173
			checkPruning(successors[i]);
174
			if (isPruned(successors[i])) {
175
				collapse(successors[i]);
176
			}
177
		}
178
		updateNodeLabel(node);
179
	}
180
181
	private void checkPruning(NodeLayout node) {
182
		boolean prune = true;
183
		NodeLayout[] predecessors = node.getPredecessingNodes();
184
		for (int j = 0; j < predecessors.length; j++) {
185
			if (isExpanded(predecessors[j])) {
186
				prune = false;
187
				break;
188
			}
189
		}
190
		if (prune) {
191
			pruneNode(node);
192
		} else {
193
			unpruneNode(node);
194
		}
195
	}
196
197
	/**
198
	 * By default nodes at the top (having no predecessors) are expanded. The
199
	 * rest are collapsed and pruned if they don't have any expanded
200
	 * predecessors
201
	 * 
202
	 * @param target
203
	 */
204
	private void resetState(NodeLayout node) {
205
		NodeLayout[] predecessors = node.getPredecessingNodes();
206
		if (predecessors.length == 0) {
207
			expand(node);
208
		} else {
209
			collapse(node);
210
			checkPruning(node);
211
		}
212
	}
213
214
	/**
215
	 * If given node belongs to a layout context using
216
	 * {@link PrunedSuccessorsSubgraph}, update of the nodes's label is forced.
217
	 * 
218
	 * @param node
219
	 *            node to update
220
	 */
221
	private void updateNodeLabel(NodeLayout node) {
222
		nodesToUpdate.add(node);
223
	}
224
225
	private void updateNodeLabel2(InternalNodeLayout node) {
226
		SubgraphFactory subgraphFactory = node.getOwnerLayoutContext()
227
				.getSubgraphFactory();
228
		if (subgraphFactory instanceof DefaultSubgraph.PrunedSuccessorsSubgraphFactory) {
229
			((DefaultSubgraph.PrunedSuccessorsSubgraphFactory) subgraphFactory)
230
					.updateLabelForNode(node);
231
		}
232
	}
233
234
	private void pruneNode(NodeLayout node) {
235
		if (isPruned(node)) {
236
			return;
237
		}
238
		nodesToUnprune.remove(node);
239
		nodesToPrune.add(node);
240
	}
241
242
	private void unpruneNode(NodeLayout node) {
243
		if (!isPruned(node)) {
244
			return;
245
		}
246
		nodesToPrune.remove(node);
247
		nodesToUnprune.add(node);
248
	}
249
250
	private boolean isPruned(NodeLayout node) {
251
		if (nodesToUnprune.contains(node)) {
252
			return false;
253
		}
254
		if (nodesToPrune.contains(node)) {
255
			return true;
256
		}
257
		return node.isPruned();
258
	}
259
260
	private void flushChanges(boolean force, boolean clean) {
261
		cleanLayoutScheduled = cleanLayoutScheduled || clean;
262
		if (!force && !context.isBackgroundLayoutEnabled()) {
263
			return;
264
		}
265
266
		for (Iterator iterator = nodesToUnprune.iterator(); iterator.hasNext();) {
267
			NodeLayout node = (NodeLayout) iterator.next();
268
			node.prune(null);
269
		}
270
		nodesToUnprune.clear();
271
272
		if (!nodesToPrune.isEmpty()) {
273
			context.createSubgraph((NodeLayout[]) nodesToPrune
274
					.toArray(new NodeLayout[nodesToPrune.size()]));
275
			nodesToPrune.clear();
276
		}
277
278
		for (Iterator iterator = nodesToUpdate.iterator(); iterator.hasNext();) {
279
			InternalNodeLayout node = (InternalNodeLayout) iterator.next();
280
			updateNodeLabel2(node);
281
		}
282
		nodesToUpdate.clear();
283
284
		(context).applyLayout(cleanLayoutScheduled);
285
		cleanLayoutScheduled = false;
286
		context.flushChanges(true);
287
	}
288
289
	private boolean isExpanded(NodeLayout node) {
290
		return expandedNodes.contains(node);
291
	}
292
293
	private void setExpanded(NodeLayout node, boolean expanded) {
294
		if (expanded) {
295
			expandedNodes.add(node);
296
		} else {
297
			expandedNodes.remove(node);
298
		}
299
	}
300
}
(-)src/org/eclipse/zest/core/widgets/DefaultSubgraph.java (+380 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import java.util.HashMap;
13
import java.util.HashSet;
14
import java.util.Iterator;
15
import java.util.Set;
16
17
import org.eclipse.draw2d.ColorConstants;
18
import org.eclipse.swt.graphics.Color;
19
import org.eclipse.zest.core.widgets.custom.LabelSubgraph;
20
import org.eclipse.zest.core.widgets.custom.TriangleSubgraph;
21
import org.eclipse.zest.core.widgets.custom.TriangleSubgraph.TriangleParameters;
22
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
23
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
24
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
25
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
26
import org.eclipse.zest.layouts.interfaces.EntityLayout;
27
import org.eclipse.zest.layouts.interfaces.LayoutContext;
28
import org.eclipse.zest.layouts.interfaces.NodeLayout;
29
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
30
31
/**
32
 * Default implementation of {@link SubgraphLayout}. Every subgraph added to
33
 * Zest {@link Graph} should inherit from this class. The default implementation
34
 * is very simple. A node pruned to this subgraph is minimized and all
35
 * connections adjacent to it are made invisible. No additional graphic elements
36
 * are added to the graph, but subclasses may add them.
37
 * 
38
 * @since 2.0
39
 */
40
public class DefaultSubgraph implements SubgraphLayout {
41
42
	/**
43
	 * Default factory for {@link DefaultSubgraph}. It creates one subgraph for
44
	 * a whole graph and throws every node intimageo it.
45
	 */
46
	public static class DefaultSubgraphFactory implements SubgraphFactory {
47
		private HashMap contextToSubgraph = new HashMap();
48
49
		public SubgraphLayout createSubgraph(NodeLayout[] nodes,
50
				LayoutContext context) {
51
			DefaultSubgraph subgraph = (DefaultSubgraph) contextToSubgraph
52
					.get(context);
53
			if (subgraph == null) {
54
				subgraph = new DefaultSubgraph(context);
55
				contextToSubgraph.put(context, subgraph);
56
			}
57
			subgraph.addNodes(nodes);
58
			return subgraph;
59
		}
60
	};
61
62
	public static class LabelSubgraphFactory implements SubgraphFactory {
63
		private Color defaultForegroundColor = ColorConstants.black;
64
		private Color defaultBackgroundColor = ColorConstants.yellow;
65
66
		/**
67
		 * Changes the default foreground color for newly created subgraphs.
68
		 * 
69
		 * @param c
70
		 *            color to use
71
		 */
72
		public void setDefualtForegroundColor(Color c) {
73
			defaultForegroundColor = c;
74
		}
75
76
		/**
77
		 * Changes the default background color for newly created subgraphs.
78
		 * 
79
		 * @param c
80
		 *            color to use
81
		 */
82
		public void setDefaultBackgroundColor(Color c) {
83
			defaultBackgroundColor = c;
84
		}
85
86
		public SubgraphLayout createSubgraph(NodeLayout[] nodes,
87
				LayoutContext context) {
88
			return new LabelSubgraph(nodes, context, defaultForegroundColor,
89
					defaultBackgroundColor);
90
		}
91
	};
92
93
	public static class TriangleSubgraphFactory implements SubgraphFactory {
94
		private TriangleParameters parameters = new TriangleParameters();
95
96
		public SubgraphLayout createSubgraph(NodeLayout[] nodes,
97
				LayoutContext context) {
98
			return new TriangleSubgraph(nodes, context,
99
					(TriangleParameters) parameters.clone());
100
		}
101
102
		/**
103
		 * 
104
		 * @return initial color of triangles created with this factory
105
		 */
106
		public Color getColor() {
107
			return parameters.color;
108
		}
109
110
		/**
111
		 * Changes the default color for newly created subgraphs.
112
		 * 
113
		 * @param color
114
		 *            color to use
115
		 */
116
		public void setColor(Color color) {
117
			parameters.color = color;
118
		}
119
120
		/**
121
		 * 
122
		 * @return initial direction of triangles created with this factory
123
		 */
124
		public int getDirection() {
125
			return parameters.direction;
126
		}
127
128
		/**
129
		 * Changes the default direction for newly cretaed subgraphs.
130
		 * 
131
		 * @param direction
132
		 *            direction to use, can be {@link SubgraphLayout#TOP_DOWN},
133
		 *            {@link SubgraphLayout#BOTTOM_UP},
134
		 *            {@link SubgraphLayout#LEFT_RIGHT}, or
135
		 *            {@link SubgraphLayout#RIGHT_LEFT}
136
		 */
137
		public void setDirection(int direction) {
138
			parameters.direction = direction;
139
		}
140
141
		/**
142
		 * 
143
		 * @return maximum height of triangles created with this factory
144
		 */
145
		public double getReferenceHeight() {
146
			return parameters.referenceHeight;
147
		}
148
149
		/**
150
		 * Sets the maximum height for the triangle visualizing this subgraph.
151
		 * 
152
		 * @param referenceHeight
153
		 *            height to use
154
		 */
155
		public void setReferenceHeight(double referenceHeight) {
156
			parameters.referenceHeight = referenceHeight;
157
		}
158
159
		/**
160
		 * 
161
		 * @return maximum base length of triangles created with this factory
162
		 */
163
		public double getReferenceBase() {
164
			return parameters.referenceBase;
165
		}
166
167
		/**
168
		 * Sets the maximum base length for the triangle visualizing this
169
		 * subgraph.
170
		 * 
171
		 * @param referenceBase
172
		 *            base length to use
173
		 */
174
175
		public void setReferenceBase(double referenceBase) {
176
			parameters.referenceBase = referenceBase;
177
		}
178
	};
179
180
	/**
181
	 * Factory for {@link PrunedSuccessorsSubgraph}. It creates one subgraph for
182
	 * a whole graph and throws every node into it.
183
	 */
184
	public static class PrunedSuccessorsSubgraphFactory implements
185
			SubgraphFactory {
186
		private HashMap contextToSubgraph = new HashMap();
187
188
		public SubgraphLayout createSubgraph(NodeLayout[] nodes,
189
				LayoutContext context) {
190
			PrunedSuccessorsSubgraph subgraph = (PrunedSuccessorsSubgraph) contextToSubgraph
191
					.get(context);
192
			if (subgraph == null) {
193
				subgraph = new PrunedSuccessorsSubgraph(context);
194
				contextToSubgraph.put(context, subgraph);
195
			}
196
			subgraph.addNodes(nodes);
197
			return subgraph;
198
		}
199
200
		/**
201
		 * Updates a label for given node (creates the label if necessary).
202
		 * 
203
		 * @param node
204
		 *            node to update
205
		 */
206
		public void updateLabelForNode(InternalNodeLayout node) {
207
			InternalLayoutContext context = node.getOwnerLayoutContext();
208
			PrunedSuccessorsSubgraph subgraph = (PrunedSuccessorsSubgraph) contextToSubgraph
209
					.get(context);
210
			if (subgraph == null) {
211
				subgraph = new PrunedSuccessorsSubgraph(context);
212
				contextToSubgraph.put(context, subgraph);
213
			}
214
			subgraph.updateNodeLabel(node);
215
		}
216
217
	};
218
219
	protected final InternalLayoutContext context;
220
221
	protected final Set nodes = new HashSet();
222
223
	protected boolean disposed = false;
224
225
	protected DefaultSubgraph(LayoutContext context2) {
226
		if (context2 instanceof InternalLayoutContext) {
227
			this.context = (InternalLayoutContext) context2;
228
		} else {
229
			throw new RuntimeException(
230
					"This subgraph can be only created with LayoutContext provided by Zest Graph");
231
		}
232
	}
233
234
	public boolean isGraphEntity() {
235
		return false;
236
	}
237
238
	public void setSize(double width, double height) {
239
		// do nothing
240
		context.checkChangesAllowed();
241
	}
242
243
	public void setLocation(double x, double y) {
244
		// do nothing
245
		context.checkChangesAllowed();
246
	}
247
248
	public boolean isResizable() {
249
		return false;
250
	}
251
252
	public boolean isMovable() {
253
		return false;
254
	}
255
256
	public EntityLayout[] getSuccessingEntities() {
257
		return new EntityLayout[0];
258
	}
259
260
	public DisplayIndependentDimension getSize() {
261
		DisplayIndependentRectangle bounds = context.getBounds();
262
		return new DisplayIndependentDimension(bounds.width, bounds.height);
263
	}
264
265
	public double getPreferredAspectRatio() {
266
		return 0;
267
	}
268
269
	public EntityLayout[] getPredecessingEntities() {
270
		return new EntityLayout[0];
271
	}
272
273
	public DisplayIndependentPoint getLocation() {
274
		DisplayIndependentRectangle bounds = context.getBounds();
275
		return new DisplayIndependentPoint(bounds.x + bounds.width / 2,
276
				bounds.y + bounds.height / 2);
277
	}
278
279
	public boolean isDirectionDependant() {
280
		return false;
281
	}
282
283
	public void setDirection(int direction) {
284
		context.checkChangesAllowed();
285
		// do nothing
286
	}
287
288
	public void removeNodes(NodeLayout[] nodes) {
289
		context.checkChangesAllowed();
290
		for (int i = 0; i < nodes.length; i++) {
291
			if (this.nodes.remove(nodes[i])) {
292
				nodes[i].prune(null);
293
				nodes[i].setMinimized(false);
294
				refreshConnectionsVisibility(nodes[i].getIncomingConnections());
295
				refreshConnectionsVisibility(nodes[i].getOutgoingConnections());
296
			}
297
		}
298
		if (this.nodes.isEmpty()) {
299
			dispose();
300
		}
301
	}
302
303
	public void removeDisposedNodes() {
304
		for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
305
			InternalNodeLayout node = (InternalNodeLayout) iterator.next();
306
			if (node.isDisposed()) {
307
				iterator.remove();
308
			}
309
		}
310
	}
311
312
	public NodeLayout[] getNodes() {
313
		InternalNodeLayout[] result = new InternalNodeLayout[nodes.size()];
314
		int i = 0;
315
		for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
316
			result[i] = (InternalNodeLayout) iterator.next();
317
			if (!context.isLayoutItemFiltered(result[i].getNode())) {
318
				i++;
319
			}
320
		}
321
		if (i == nodes.size()) {
322
			return result;
323
		}
324
325
		NodeLayout[] result2 = new NodeLayout[i];
326
		System.arraycopy(result, 0, result2, 0, i);
327
		return result2;
328
	}
329
330
	public int countNodes() {
331
		return nodes.size();
332
	}
333
334
	public void addNodes(NodeLayout[] nodes) {
335
		context.checkChangesAllowed();
336
		for (int i = 0; i < nodes.length; i++) {
337
			if (this.nodes.add(nodes[i])) {
338
				nodes[i].prune(this);
339
				nodes[i].setMinimized(true);
340
				refreshConnectionsVisibility(nodes[i].getIncomingConnections());
341
				refreshConnectionsVisibility(nodes[i].getOutgoingConnections());
342
			}
343
		}
344
	}
345
346
	protected void refreshConnectionsVisibility(ConnectionLayout[] connections) {
347
		for (int i = 0; i < connections.length; i++) {
348
			connections[i].setVisible(!connections[i].getSource().isPruned()
349
					&& !connections[i].getTarget().isPruned());
350
		}
351
	}
352
353
	/**
354
	 * Makes sure that value returned by {@link #getLocation()} will be equal to
355
	 * current location of this subgraph.
356
	 */
357
	protected void refreshLocation() {
358
		// do nothing, to be reimplemented in subclasses
359
	}
360
361
	/**
362
	 * Makes sure that value returned by {@link #getSize()} will be equal to
363
	 * current size of this subgraph.
364
	 */
365
	protected void refreshSize() {
366
		// do nothing, to be reimplemented in subclasses
367
	}
368
369
	protected void applyLayoutChanges() {
370
		// do nothing
371
	}
372
373
	protected void dispose() {
374
		if (!disposed) {
375
			context.removeSubgrah(this);
376
			disposed = true;
377
		}
378
	}
379
380
};
(-)src/org/eclipse/zest/core/widgets/FigureSubgraph.java (+198 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import java.util.Iterator;
13
14
import org.eclipse.draw2d.Animation;
15
import org.eclipse.draw2d.FigureListener;
16
import org.eclipse.draw2d.IFigure;
17
import org.eclipse.draw2d.geometry.Dimension;
18
import org.eclipse.draw2d.geometry.Point;
19
import org.eclipse.draw2d.geometry.Rectangle;
20
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
21
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
22
import org.eclipse.zest.layouts.interfaces.EntityLayout;
23
import org.eclipse.zest.layouts.interfaces.LayoutContext;
24
import org.eclipse.zest.layouts.interfaces.NodeLayout;
25
26
/**
27
 * A subgraph layout that represents a subgraph as a single figure. An entity
28
 * representing subgraph is not resizable by layout algorithm unless proper
29
 * methods are redefined in a subclass.
30
 * 
31
 * @since 2.0
32
 */
33
public abstract class FigureSubgraph extends DefaultSubgraph {
34
35
	protected IFigure figure;
36
	private DisplayIndependentPoint location;
37
	private boolean isLayoutBeingApplied = false;
38
39
	/**
40
	 * Listens to changes in this subgraph's figure and fires proper event in
41
	 * its layout context.
42
	 */
43
	protected class SubgraphFigrueListener implements FigureListener {
44
		private Rectangle previousBounds = figure.getBounds().getCopy();
45
46
		public void figureMoved(IFigure source) {
47
			if (Animation.isAnimating() || isLayoutBeingApplied) {
48
				return;
49
			}
50
			Rectangle newBounds = figure.getBounds();
51
			if (!newBounds.getSize().equals(previousBounds.getSize())) {
52
				(context).fireSubgraphResizedEvent(FigureSubgraph.this);
53
			} else if (!newBounds.getLocation().equals(
54
					previousBounds.getLocation())) {
55
				(context).fireSubgraphMovedEvent(FigureSubgraph.this);
56
			}
57
			previousBounds = newBounds.getCopy();
58
		}
59
	};
60
61
	/**
62
	 * Creates a figure for this subgraph and stores it in {@link #figure}.
63
	 * 
64
	 * This method may not be called right after creation of the subgraph but
65
	 * later when the figure is actually needed (lazy initialization).
66
	 */
67
	protected abstract void createFigure();
68
69
	/**
70
	 * Updates the figure stored in {@link #figure} depending on current nodes
71
	 * contained in this subgraph. If this method creates a new instance of
72
	 * IFigure, it should remember to add a {@link SubgraphFigrueListener} to
73
	 * it.
74
	 */
75
	protected abstract void updateFigure();
76
77
	public IFigure getFigure() {
78
		if (figure == null) {
79
			createFigure();
80
			updateFigure();
81
			figure.addFigureListener(new SubgraphFigrueListener());
82
			(context).container.addSubgraphFigure(figure);
83
		}
84
		return figure;
85
	}
86
87
	protected FigureSubgraph(NodeLayout[] nodes, LayoutContext context) {
88
		super(context);
89
		addNodes(nodes);
90
	}
91
92
	/**
93
	 * {@inheritDoc}
94
	 * 
95
	 * All nodes added to this subgraph are moved to the center of the figure
96
	 * (so that collapsing and expanding animation looks cool).
97
	 */
98
	public void addNodes(NodeLayout[] nodes) {
99
		int initialCount = this.nodes.size();
100
		super.addNodes(nodes);
101
		if (this.nodes.size() > initialCount && figure != null) {
102
			updateFigure();
103
			if (location != null) {
104
				for (int i = 0; i < nodes.length; i++) {
105
					nodes[i].setLocation(location.x, location.y);
106
				}
107
			}
108
		}
109
	}
110
111
	public void removeNodes(NodeLayout[] nodes) {
112
		int initialCount = this.nodes.size();
113
		super.removeNodes(nodes);
114
		if (this.nodes.size() < initialCount && figure != null && !disposed) {
115
			updateFigure();
116
		}
117
	}
118
119
	public EntityLayout[] getSuccessingEntities() {
120
		// TODO Auto-generated method stub
121
		return super.getSuccessingEntities();
122
	}
123
124
	public EntityLayout[] getPredecessingEntities() {
125
		// TODO Auto-generated method stub
126
		return super.getPredecessingEntities();
127
	}
128
129
	public DisplayIndependentDimension getSize() {
130
		Dimension size = getFigure().getSize();
131
		return new DisplayIndependentDimension(size.width, size.height);
132
	}
133
134
	public DisplayIndependentPoint getLocation() {
135
		if (location == null) {
136
			Point location2 = getFigure().getBounds().getLocation();
137
			Dimension size = getFigure().getSize();
138
			return new DisplayIndependentPoint(location2.x + size.width / 2,
139
					location2.y + size.height / 2);
140
		}
141
		return new DisplayIndependentPoint(location);
142
	}
143
144
	public void setLocation(double x, double y) {
145
		super.setLocation(x, y);
146
		for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
147
			NodeLayout node = (NodeLayout) iterator.next();
148
			node.setLocation(x, y);
149
		}
150
151
		if (location != null) {
152
			location.x = x;
153
			location.y = y;
154
		} else {
155
			location = new DisplayIndependentPoint(x, y);
156
			// the first location change will be applied immediately
157
			applyLayoutChanges();
158
		}
159
	}
160
161
	protected void refreshLocation() {
162
		Rectangle bounds = figure.getBounds();
163
		if (location == null) {
164
			location = new DisplayIndependentPoint(0, 0);
165
		}
166
		location.x = bounds.x + bounds.width / 2;
167
		location.y = bounds.y + bounds.height / 2;
168
	}
169
170
	public boolean isGraphEntity() {
171
		return true;
172
	}
173
174
	public boolean isMovable() {
175
		return true;
176
	}
177
178
	protected void dispose() {
179
		if (!disposed) {
180
			super.dispose();
181
			if (figure != null) {
182
				context.container.getGraph().removeSubgraphFigure(figure);
183
			}
184
		}
185
	}
186
187
	protected void applyLayoutChanges() {
188
		getFigure();
189
		if (location != null) {
190
			isLayoutBeingApplied = true;
191
			Dimension size = figure.getSize();
192
			figure.setLocation(new Point(location.x - size.width / 2,
193
					location.y - size.height / 2));
194
			isLayoutBeingApplied = false;
195
		}
196
	}
197
198
}
(-)src/org/eclipse/zest/core/widgets/FisheyeListener.java (+61 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import org.eclipse.draw2d.IFigure;
13
14
/**
15
 * Interface for listener that can be added to {@link Graph} and receive
16
 * notifications when fisheye figures are added, removed or replaced in it.
17
 * 
18
 * @since 2.0
19
 */
20
public interface FisheyeListener {
21
22
	/**
23
	 * Called when a fisheye figure is added to an observed graph.
24
	 * 
25
	 * @param graph
26
	 *            observed graph
27
	 * @param originalFigure
28
	 *            figure to be fisheyed
29
	 * @param fisheyeFigure
30
	 *            the added fisheye figure
31
	 */
32
	public void fisheyeAdded(Graph graph, IFigure originalFigure,
33
			IFigure fisheyeFigure);
34
35
	/**
36
	 * Called when a fisheye figure is removed form an observed graph.
37
	 * 
38
	 * @param graph
39
	 *            observed graph
40
	 * @param originalFigure
41
	 *            figure that was fisheyed
42
	 * @param fisheyeFigure
43
	 *            the removed fisheye figure
44
	 */
45
	public void fisheyeRemoved(Graph graph, IFigure originalFigure,
46
			IFigure fisheyeFigure);
47
48
	/**
49
	 * Called when one fisheye figure is replaced by another in an observed
50
	 * graph.
51
	 * 
52
	 * @param graph
53
	 *            observed graph
54
	 * @param oldFisheyeFigure
55
	 *            fisheye figure that is replaced
56
	 * @param newFisheyeFigure
57
	 *            fisheye figure that replaces the old figure
58
	 */
59
	public void fisheyeReplaced(Graph graph, IFigure oldFisheyeFigure,
60
			IFigure newFisheyeFigure);
61
}
(-)src/org/eclipse/zest/core/widgets/Graph.java (-1198 / +1217 lines)
Lines 1-1198 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * All rights reserved. This program and the accompanying materials are made
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria 
9
 ******************************************************************************/
9
 *               Mateusz Matela
10
package org.eclipse.zest.core.widgets;
10
 ******************************************************************************/
11
11
package org.eclipse.zest.core.widgets;
12
import java.util.ArrayList;
12
13
import java.util.HashMap;
13
import java.util.ArrayList;
14
import java.util.Iterator;
14
import java.util.HashMap;
15
import java.util.LinkedList;
15
import java.util.HashSet;
16
import java.util.List;
16
import java.util.Iterator;
17
17
import java.util.List;
18
import org.eclipse.draw2d.Animation;
18
19
import org.eclipse.draw2d.ColorConstants;
19
import org.eclipse.draw2d.Animation;
20
import org.eclipse.draw2d.FigureCanvas;
20
import org.eclipse.draw2d.ColorConstants;
21
import org.eclipse.draw2d.FreeformLayer;
21
import org.eclipse.draw2d.CoordinateListener;
22
import org.eclipse.draw2d.FreeformLayout;
22
import org.eclipse.draw2d.FigureCanvas;
23
import org.eclipse.draw2d.FreeformViewport;
23
import org.eclipse.draw2d.FreeformLayer;
24
import org.eclipse.draw2d.IFigure;
24
import org.eclipse.draw2d.FreeformLayout;
25
import org.eclipse.draw2d.LayoutAnimator;
25
import org.eclipse.draw2d.FreeformViewport;
26
import org.eclipse.draw2d.MouseMotionListener;
26
import org.eclipse.draw2d.IFigure;
27
import org.eclipse.draw2d.PolylineConnection;
27
import org.eclipse.draw2d.LayoutAnimator;
28
import org.eclipse.draw2d.SWTEventDispatcher;
28
import org.eclipse.draw2d.MouseMotionListener;
29
import org.eclipse.draw2d.ScalableFigure;
29
import org.eclipse.draw2d.PolylineConnection;
30
import org.eclipse.draw2d.ScalableFreeformLayeredPane;
30
import org.eclipse.draw2d.SWTEventDispatcher;
31
import org.eclipse.draw2d.ScrollPane;
31
import org.eclipse.draw2d.ScalableFigure;
32
import org.eclipse.draw2d.TreeSearch;
32
import org.eclipse.draw2d.ScalableFreeformLayeredPane;
33
import org.eclipse.draw2d.geometry.Dimension;
33
import org.eclipse.draw2d.ScrollPane;
34
import org.eclipse.draw2d.geometry.Point;
34
import org.eclipse.draw2d.TreeSearch;
35
import org.eclipse.draw2d.geometry.Rectangle;
35
import org.eclipse.draw2d.geometry.Dimension;
36
import org.eclipse.swt.SWT;
36
import org.eclipse.draw2d.geometry.Point;
37
import org.eclipse.swt.events.PaintEvent;
37
import org.eclipse.draw2d.geometry.Rectangle;
38
import org.eclipse.swt.events.PaintListener;
38
import org.eclipse.swt.SWT;
39
import org.eclipse.swt.events.SelectionAdapter;
39
import org.eclipse.swt.events.ControlEvent;
40
import org.eclipse.swt.events.SelectionEvent;
40
import org.eclipse.swt.events.ControlListener;
41
import org.eclipse.swt.events.SelectionListener;
41
import org.eclipse.swt.events.PaintEvent;
42
import org.eclipse.swt.graphics.Color;
42
import org.eclipse.swt.events.PaintListener;
43
import org.eclipse.swt.widgets.Composite;
43
import org.eclipse.swt.events.SelectionAdapter;
44
import org.eclipse.swt.widgets.Control;
44
import org.eclipse.swt.events.SelectionEvent;
45
import org.eclipse.swt.widgets.Display;
45
import org.eclipse.swt.events.SelectionListener;
46
import org.eclipse.swt.widgets.Event;
46
import org.eclipse.swt.graphics.Color;
47
import org.eclipse.swt.widgets.Item;
47
import org.eclipse.swt.widgets.Composite;
48
import org.eclipse.zest.core.widgets.internal.ContainerFigure;
48
import org.eclipse.swt.widgets.Display;
49
import org.eclipse.zest.core.widgets.internal.RevealListener;
49
import org.eclipse.swt.widgets.Event;
50
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
50
import org.eclipse.swt.widgets.Item;
51
import org.eclipse.zest.layouts.InvalidLayoutConfiguration;
51
import org.eclipse.swt.widgets.Widget;
52
import org.eclipse.zest.layouts.LayoutAlgorithm;
52
import org.eclipse.zest.core.widgets.internal.ContainerFigure;
53
import org.eclipse.zest.layouts.LayoutEntity;
53
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
54
import org.eclipse.zest.layouts.LayoutRelationship;
54
import org.eclipse.zest.layouts.LayoutAlgorithm;
55
import org.eclipse.zest.layouts.LayoutStyles;
55
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
56
import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
56
import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager;
57
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
57
58
58
/**
59
/*
59
 * @since 1.0
60
 * Holds the nodes and connections for the graph.
60
 */
61
 * 
61
public class Graph extends FigureCanvas implements IContainer {
62
 * @author Chris Callendar
62
63
 * 
63
	// CLASS CONSTANTS
64
 * @author Ian Bull
64
	public static final int ANIMATION_TIME = 500;
65
 */
65
	public static final int FISHEYE_ANIMATION_TIME = 100;
66
public class Graph extends FigureCanvas implements IContainer {
66
67
67
	// @tag CGraph.Colors : These are the colour constants for the graph, they
68
	// CLASS CONSTANTS
68
	// are disposed on clean-up
69
	public static final int ANIMATION_TIME = 500;
69
	public Color LIGHT_BLUE = new Color(null, 216, 228, 248);
70
	public static final int FISHEYE_ANIMATION_TIME = 100;
70
	public Color LIGHT_BLUE_CYAN = new Color(null, 213, 243, 255);
71
71
	public Color GREY_BLUE = new Color(null, 139, 150, 171);
72
	// @tag CGraph.Colors : These are the colour constants for the graph, they
72
	public Color DARK_BLUE = new Color(null, 1, 70, 122);
73
	// are disposed on clean-up
73
	public Color LIGHT_YELLOW = new Color(null, 255, 255, 206);
74
	public Color LIGHT_BLUE = new Color(null, 216, 228, 248);
74
75
	public Color LIGHT_BLUE_CYAN = new Color(null, 213, 243, 255);
75
	public Color HIGHLIGHT_COLOR = ColorConstants.yellow;
76
	public Color GREY_BLUE = new Color(null, 139, 150, 171);
76
	public Color HIGHLIGHT_ADJACENT_COLOR = ColorConstants.orange;
77
	public Color DARK_BLUE = new Color(null, 1, 70, 122);
77
	public Color DEFAULT_NODE_COLOR = LIGHT_BLUE;
78
	public Color LIGHT_YELLOW = new Color(null, 255, 255, 206);
78
79
79
	/**
80
	public Color HIGHLIGHT_COLOR = ColorConstants.yellow;
80
	 * These are all the children of this graph. These lists contains all nodes
81
	public Color HIGHLIGHT_ADJACENT_COLOR = ColorConstants.orange;
81
	 * and connections that have added themselves to this graph.
82
	public Color DEFAULT_NODE_COLOR = LIGHT_BLUE;
82
	 */
83
83
	private List nodes;
84
	/**
84
	protected List connections;
85
	 * These are all the children of this graph. These lists contains all nodes
85
	HashSet subgraphFigures;
86
	 * and connections that have added themselves to this graph.
86
	private List selectedItems = null;
87
	 */
87
	private ArrayList fisheyeListeners = new ArrayList();
88
	private final List nodes;
88
	private List selectionListeners = null;
89
	protected List connections;
89
90
	private List selectedItems = null;
90
	/** This maps all visible nodes to their model element. */
91
	IFigure fisheyedFigure = null;
91
	private HashMap figure2ItemMap = null;
92
	private List /* SelectionListener */selectionListeners = null;
92
93
93
	private int connectionStyle;
94
	/** This maps all visible nodes to their model element. */
94
	private int nodeStyle;
95
	private HashMap figure2ItemMap = null;
95
	private ScalableFreeformLayeredPane fishEyeLayer = null;
96
96
	private InternalLayoutContext layoutContext = null;
97
	/** Maps user nodes to internal nodes */
97
	private volatile boolean shouldSheduleLayout;
98
	private int connectionStyle;
98
	private volatile Runnable scheduledLayoutRunnable = null;
99
	private int nodeStyle;
99
	private volatile boolean scheduledLayoutClean = false;
100
	private List constraintAdapters;
100
	private Dimension preferredSize = null;
101
	private List revealListeners = null;
101
	int style = 0;
102
102
103
	private ScalableFreeformLayeredPane fishEyeLayer = null;
103
	private ScalableFreeformLayeredPane rootlayer;
104
	LayoutAlgorithm layoutAlgorithm = null;
104
	private ZestRootLayer zestRootLayer;
105
	private Dimension preferredSize = null;
105
106
	int style = 0;
106
	/**
107
107
	 * Constructor for a Graph. This widget represents the root of the graph,
108
	private ScalableFreeformLayeredPane rootlayer;
108
	 * and can contain graph items such as graph nodes and graph connections.
109
	private ZestRootLayer zestRootLayer;
109
	 * 
110
110
	 * @param parent
111
	/**
111
	 * @param style
112
	 * Constructor for a Graph. This widget represents the root of the graph,
112
	 */
113
	 * and can contain graph items such as graph nodes and graph connections.
113
	public Graph(Composite parent, int style) {
114
	 * 
114
		super(parent, style | SWT.DOUBLE_BUFFERED);
115
	 * @param parent
115
		this.style = style;
116
	 * @param style
116
		this.setBackground(ColorConstants.white);
117
	 */
117
118
	public Graph(Composite parent, int style) {
118
		this.setViewport(new FreeformViewport());
119
		super(parent, style | SWT.DOUBLE_BUFFERED);
119
120
		this.style = style;
120
		this.getVerticalBar().addSelectionListener(new SelectionAdapter() {
121
		this.setBackground(ColorConstants.white);
121
			public void widgetSelected(SelectionEvent e) {
122
122
				Graph.this.redraw();
123
		LIGHT_BLUE = new Color(Display.getDefault(), 216, 228, 248);
123
			}
124
		LIGHT_BLUE_CYAN = new Color(Display.getDefault(), 213, 243, 255);
124
125
		GREY_BLUE = new Color(Display.getDefault(), 139, 150, 171);
125
		});
126
		DARK_BLUE = new Color(Display.getDefault(), 1, 70, 122);
126
		this.getHorizontalBar().addSelectionListener(new SelectionAdapter() {
127
		LIGHT_YELLOW = new Color(Display.getDefault(), 255, 255, 206);
127
			public void widgetSelected(SelectionEvent e) {
128
128
				Graph.this.redraw();
129
		this.setViewport(new FreeformViewport());
129
			}
130
130
		});
131
		this.getVerticalBar().addSelectionListener(new SelectionAdapter() {
131
132
			public void widgetSelected(SelectionEvent e) {
132
		// @tag CGraph.workaround : this allows me to handle mouse events
133
				Graph.this.redraw();
133
		// outside of the canvas
134
			}
134
		this.getLightweightSystem().setEventDispatcher(
135
135
				new SWTEventDispatcher() {
136
		});
136
					public void dispatchMouseMoved(
137
		this.getHorizontalBar().addSelectionListener(new SelectionAdapter() {
137
							org.eclipse.swt.events.MouseEvent me) {
138
			public void widgetSelected(SelectionEvent e) {
138
						super.dispatchMouseMoved(me);
139
				Graph.this.redraw();
139
140
			}
140
						// If the current event is null, return
141
		});
141
						if (getCurrentEvent() == null) {
142
142
							return;
143
		// @tag CGraph.workaround : this allows me to handle mouse events
143
						}
144
		// outside of the canvas
144
145
		this.getLightweightSystem().setEventDispatcher(new SWTEventDispatcher() {
145
						if (getMouseTarget() == null) {
146
			public void dispatchMouseMoved(org.eclipse.swt.events.MouseEvent me) {
146
							setMouseTarget(getRoot());
147
				super.dispatchMouseMoved(me);
147
						}
148
148
						if ((me.stateMask & SWT.BUTTON_MASK) != 0) {
149
				// If the current event is null, return
149
							// Sometimes getCurrentEvent() returns null
150
				if (getCurrentEvent() == null) {
150
							getMouseTarget().handleMouseDragged(
151
					return;
151
									getCurrentEvent());
152
				}
152
						} else {
153
153
							getMouseTarget()
154
				if (getMouseTarget() == null) {
154
									.handleMouseMoved(getCurrentEvent());
155
					setMouseTarget(getRoot());
155
						}
156
				}
156
					}
157
				if ((me.stateMask & SWT.BUTTON_MASK) != 0) {
157
				});
158
					// Sometimes getCurrentEvent() returns null
158
159
					getMouseTarget().handleMouseDragged(getCurrentEvent());
159
		this.setContents(createLayers());
160
				} else {
160
		DragSupport dragSupport = new DragSupport();
161
					getMouseTarget().handleMouseMoved(getCurrentEvent());
161
		this.getLightweightSystem().getRootFigure().addMouseListener(
162
				}
162
				dragSupport);
163
			}
163
		this.getLightweightSystem().getRootFigure().addMouseMotionListener(
164
		});
164
				dragSupport);
165
165
166
		this.setContents(createLayers());
166
		this.nodes = new ArrayList();
167
		DragSupport dragSupport = new DragSupport(this);
167
		this.preferredSize = new Dimension(-1, -1);
168
		this.getLightweightSystem().getRootFigure().addMouseListener(dragSupport);
168
		this.connectionStyle = ZestStyles.NONE;
169
		this.getLightweightSystem().getRootFigure().addMouseMotionListener(dragSupport);
169
		this.nodeStyle = ZestStyles.NONE;
170
170
		this.connections = new ArrayList();
171
		this.nodes = new ArrayList();
171
		this.subgraphFigures = new HashSet();
172
		this.preferredSize = new Dimension(-1, -1);
172
		this.selectedItems = new ArrayList();
173
		this.connectionStyle = ZestStyles.NONE;
173
		this.selectionListeners = new ArrayList();
174
		this.nodeStyle = ZestStyles.NONE;
174
		this.figure2ItemMap = new HashMap();
175
		this.connections = new ArrayList();
175
176
		this.constraintAdapters = new ArrayList();
176
		this.addPaintListener(new PaintListener() {
177
		this.selectedItems = new ArrayList();
177
			public void paintControl(PaintEvent e) {
178
		this.selectionListeners = new ArrayList();
178
				if (shouldSheduleLayout) {
179
		this.figure2ItemMap = new HashMap();
179
					applyLayoutInternal(true);
180
180
					shouldSheduleLayout = false;
181
		revealListeners = new ArrayList(1);
181
				}
182
		this.addPaintListener(new PaintListener() {
182
			}
183
			public void paintControl(PaintEvent e) {
183
		});
184
				if (!revealListeners.isEmpty()) {
184
185
					// Go through the reveal list and let everyone know that the
185
		this.addControlListener(new ControlListener() {
186
					// view is now available. Remove the listeners so they are
186
187
					// only
187
			public void controlResized(ControlEvent e) {
188
					// called once!
188
				if (preferredSize.width == -1 || preferredSize.height == -1) {
189
					Iterator iterator = revealListeners.iterator();
189
					getLayoutContext().fireBoundsChangedEvent();
190
					while (iterator.hasNext()) {
190
				}
191
						RevealListener reveallisetner = (RevealListener) iterator.next();
191
			}
192
						reveallisetner.revealed(Graph.this);
192
193
						iterator.remove();
193
			public void controlMoved(ControlEvent e) {
194
					}
194
				// do nothing
195
				}
195
			}
196
				/*
196
		});
197
				Iterator iterator = getNodes().iterator();
197
	}
198
				while (iterator.hasNext()) {
198
199
					GraphNode node = (GraphNode) iterator.next();
199
	/**
200
					node.paint();
200
	 * This adds a listener to the set of listeners that will be called when a
201
				}
201
	 * selection event occurs.
202
				*/
202
	 * 
203
			}
203
	 * @param selectionListener
204
		});
204
	 */
205
205
	public void addSelectionListener(SelectionListener selectionListener) {
206
	}
206
		if (!selectionListeners.contains(selectionListener)) {
207
207
			selectionListeners.add(selectionListener);
208
	/**
208
		}
209
	 * This adds a listener to the set of listeners that will be called when a
209
	}
210
	 * selection event occurs.
210
211
	 * 
211
	public void removeSelectionListener(SelectionListener selectionListener) {
212
	 * @param selectionListener
212
		if (selectionListeners.contains(selectionListener)) {
213
	 */
213
			selectionListeners.remove(selectionListener);
214
	public void addSelectionListener(SelectionListener selectionListener) {
214
		}
215
		if (!selectionListeners.contains(selectionListener)) {
215
	}
216
			selectionListeners.add(selectionListener);
216
217
		}
217
	/**
218
	}
218
	 * Gets a list of the GraphModelNode children objects under the root node in
219
219
	 * this diagram. If the root node is null then all the top level nodes are
220
	public void removeSelectionListener(SelectionListener selectionListener) {
220
	 * returned.
221
		if (selectionListeners.contains(selectionListener)) {
221
	 * 
222
			selectionListeners.remove(selectionListener);
222
	 * @return List of GraphModelNode objects
223
		}
223
	 */
224
	}
224
	public List getNodes() {
225
225
		return nodes;
226
	/**
226
	}
227
	 * Gets a list of the GraphModelNode children objects under the root node in
227
228
	 * this diagram. If the root node is null then all the top level nodes are
228
	/**
229
	 * returned.
229
	 * Gets the root layer for this graph
230
	 * 
230
	 * 
231
	 * @return List of GraphModelNode objects
231
	 * @return
232
	 */
232
	 */
233
	public List getNodes() {
233
	public ScalableFigure getRootLayer() {
234
		return nodes;
234
		return rootlayer;
235
	}
235
	}
236
236
237
	/**
237
	/**
238
	 * Adds a new constraint adapter to the list of constraint adapters 
238
	 * Sets the default connection style.
239
	 * @param constraintAdapter
239
	 * 
240
	 */
240
	 * @param connection
241
	public void addConstraintAdapter(ConstraintAdapter constraintAdapter) {
241
	 *            style the connection style to set
242
		this.constraintAdapters.add(constraintAdapter);
242
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
243
	}
243
	 */
244
244
	public void setConnectionStyle(int connectionStyle) {
245
	/**
245
		this.connectionStyle = connectionStyle;
246
	 * Sets the constraint adapters on this model
246
	}
247
	 * 
247
248
	 * @param constraintAdapters
248
	/**
249
	 */
249
	 * Gets the default connection style.
250
	public void setConstraintAdapters(List /* ConstraintAdapters */constraintAdapters) {
250
	 * 
251
		this.constraintAdapters = constraintAdapters;
251
	 * @return the connection style
252
	}
252
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
253
253
	 */
254
	/**
254
	public int getConnectionStyle() {
255
	 * Gets the root layer for this graph
255
		return connectionStyle;
256
	 * 
256
	}
257
	 * @return
257
258
	 */
258
	/**
259
	public ScalableFigure getRootLayer() {
259
	 * Sets the default node style.
260
		return rootlayer;
260
	 * 
261
	}
261
	 * @param nodeStyle
262
262
	 *            the node style to set
263
	/**
263
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
264
	 * Sets the default connection style.
264
	 */
265
	 * 
265
	public void setNodeStyle(int nodeStyle) {
266
	 * @param connection
266
		this.nodeStyle = nodeStyle;
267
	 *            style the connection style to set
267
	}
268
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
268
269
	 */
269
	/**
270
	public void setConnectionStyle(int connectionStyle) {
270
	 * Gets the default node style.
271
		this.connectionStyle = connectionStyle;
271
	 * 
272
	}
272
	 * @return the node style
273
273
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
274
	/**
274
	 */
275
	 * Gets the default connection style.
275
	public int getNodeStyle() {
276
	 * 
276
		return nodeStyle;
277
	 * @return the connection style
277
	}
278
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
278
279
	 */
279
	/**
280
	public int getConnectionStyle() {
280
	 * Gets the list of GraphModelConnection objects.
281
		return connectionStyle;
281
	 * 
282
	}
282
	 * @return list of GraphModelConnection objects
283
283
	 */
284
	/**
284
	public List getConnections() {
285
	 * Sets the default node style.
285
		return this.connections;
286
	 * 
286
	}
287
	 * @param nodeStyle
287
288
	 *            the node style to set
288
	/**
289
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
289
	 * Changes the selection to the list of items
290
	 */
290
	 * 
291
	public void setNodeStyle(int nodeStyle) {
291
	 * @param l
292
		this.nodeStyle = nodeStyle;
292
	 */
293
	}
293
	public void setSelection(GraphItem[] nodes) {
294
294
		clearSelection();
295
	/**
295
		if (nodes != null) {
296
	 * Gets the default node style.
296
			for (int i = 0; i < nodes.length; i++) {
297
	 * 
297
				if (nodes[i] != null && nodes[i] instanceof GraphItem) {
298
	 * @return the node style
298
					selectedItems.add(nodes[i]);
299
	 * @see org.eclipse.mylar.zest.core.widgets.ZestStyles
299
					(nodes[i]).highlight();
300
	 */
300
				}
301
	public int getNodeStyle() {
301
			}
302
		return nodeStyle;
302
		}
303
	}
303
		// TODO shouldn't this method fire a selection event?
304
304
	}
305
	/**
305
306
	 * Gets the list of GraphModelConnection objects.
306
	public void selectAll() {
307
	 * 
307
		clearSelection();
308
	 * @return list of GraphModelConnection objects
308
		for (int i = 0; i < nodes.size(); i++) {
309
	 */
309
			selectedItems.add(nodes.get(i));
310
	public List getConnections() {
310
			((GraphNode) nodes.get(i)).highlight();
311
		return this.connections;
311
		}
312
	}
312
		// TODO shouldn't this method fire a selection event?
313
313
	}
314
	/**
314
315
	 * Changes the selection to the list of items
315
	/**
316
	 * 
316
	 * Gets the list of currently selected GraphNodes
317
	 * @param l
317
	 * 
318
	 */
318
	 * @return Currently selected graph node
319
	public void setSelection(GraphItem[] nodes) {
319
	 */
320
		clearSelection();
320
	public List getSelection() {
321
		if (nodes != null) {
321
		return selectedItems;
322
			for (int i = 0; i < nodes.length; i++) {
322
	}
323
				if (nodes[i] != null && nodes[i] instanceof GraphItem) {
323
324
					selectedItems.add(nodes[i]);
324
	/*
325
					(nodes[i]).highlight();
325
	 * (non-Javadoc)
326
				}
326
	 * 
327
			}
327
	 * @see org.eclipse.swt.widgets.Widget#toString()
328
		}
328
	 */
329
	}
329
	public String toString() {
330
330
		return "GraphModel {" + nodes.size() + " nodes, " + connections.size()
331
	public void selectAll() {
331
				+ " connections}";
332
		clearSelection();
332
	}
333
		for (int i = 0; i < nodes.size(); i++) {
333
334
			selectedItems.add(nodes.get(i));
334
	/**
335
			((GraphNode) nodes.get(i)).highlight();
335
	 * Dispose of the nodes and edges when the graph is disposed.
336
		}
336
	 */
337
	}
337
	public void dispose() {
338
338
		while (nodes.size() > 0) {
339
	/**
339
			GraphNode node = (GraphNode) nodes.get(0);
340
	 * Gets the list of currently selected GraphNodes
340
			if (node != null && !node.isDisposed()) {
341
	 * 
341
				node.dispose();
342
	 * @return Currently selected graph node
342
			}
343
	 */
343
		}
344
	public List getSelection() {
344
		while (connections.size() > 0) {
345
		return selectedItems;
345
			GraphConnection connection = (GraphConnection) connections.get(0);
346
	}
346
			if (connection != null && !connection.isDisposed()) {
347
347
				connection.dispose();
348
	/*
348
			}
349
	 * (non-Javadoc)
349
		}
350
	 * 
350
		super.dispose();
351
	 * @see org.eclipse.swt.widgets.Widget#toString()
351
352
	 */
352
		LIGHT_BLUE.dispose();
353
	public String toString() {
353
		LIGHT_BLUE_CYAN.dispose();
354
		return "GraphModel {" + nodes.size() + " nodes, " + connections.size() + " connections}";
354
		GREY_BLUE.dispose();
355
	}
355
		DARK_BLUE.dispose();
356
356
		LIGHT_YELLOW.dispose();
357
	/*
357
	}
358
	 * (non-Javadoc)
358
359
	 * 
359
	/**
360
	 * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem#getGraphModel()
360
	 * Runs the layout on this graph. If the view is not visible layout will be
361
	 */
361
	 * deferred until after the view is available.
362
	public Graph getGraphModel() {
362
	 */
363
		return this;
363
	public void applyLayout() {
364
	}
364
		scheduleLayoutOnReveal(true);
365
365
	}
366
	/**
366
367
	 * Dispose of the nodes and edges when the graph is disposed.
367
	/**
368
	 */
368
	 * @since 2.0
369
	public void dispose() {
369
	 */
370
		while (nodes.size() > 0) {
370
	public void applyLayoutNow() {
371
			GraphNode node = (GraphNode) nodes.get(0);
371
		getLayoutContext().applyLayout(true);
372
			if (node != null && !node.isDisposed()) {
372
		layoutContext.flushChanges(false);
373
				node.dispose();
373
	}
374
			}
374
375
		}
375
	/**
376
		while (connections.size() > 0) {
376
	 * Enables or disables dynamic layout (that is layout algorithm performing
377
			GraphConnection connection = (GraphConnection) connections.get(0);
377
	 * layout in background or when certain events occur). Dynamic layout should
378
			if (connection != null && !connection.isDisposed()) {
378
	 * be disabled before doing a long series of changes in the graph to make
379
				connection.dispose();
379
	 * sure that layout algorithm won't interfere with these changes.
380
			}
380
	 * 
381
		}
381
	 * Enabling dynamic layout causes the layout algorithm to be applied even if
382
		super.dispose();
382
	 * it's not actually a dynamic algorithm.
383
383
	 * 
384
		LIGHT_BLUE.dispose();
384
	 * @param enabled
385
		LIGHT_BLUE_CYAN.dispose();
385
	 * 
386
		GREY_BLUE.dispose();
386
	 * @since 2.0
387
		DARK_BLUE.dispose();
387
	 */
388
		LIGHT_YELLOW.dispose();
388
	public void setDynamicLayout(boolean enabled) {
389
	}
389
		if (getLayoutContext().isBackgroundLayoutEnabled() != enabled) {
390
390
			layoutContext.setBackgroundLayoutEnabled(enabled);
391
	/**
391
			if (enabled) {
392
	 * Runs the layout on this graph. It uses the reveal listner to run the
392
				scheduleLayoutOnReveal(false);
393
	 * layout only if the view is visible. Otherwise it will be deferred until
393
			}
394
	 * after the view is available.
394
		}
395
	 */
395
	}
396
	public void applyLayout() {
396
397
		this.addRevealListener(new RevealListener() {
397
	/**
398
			public void revealed(Control c) {
398
	 * 
399
				Display.getDefault().asyncExec(new Runnable() {
399
	 * @return true if dynamic layout is enabled (see
400
400
	 *         {@link #setDynamicLayout(boolean)})
401
					public void run() {
401
	 * @since 2.0
402
						applyLayoutInternal();
402
	 */
403
					}
403
	public boolean isDynamicLayoutEnabled() {
404
				});
404
		return getLayoutContext().isBackgroundLayoutEnabled();
405
			}
405
	}
406
		});
406
407
	}
407
	private void applyLayoutInternal(boolean clean) {
408
408
		if (getLayoutContext().getLayoutAlgorithm() == null) {
409
	/**
409
			return;
410
	 * Sets the preferred size of the layout area. Size of ( -1, -1) uses the
410
		}
411
	 * current canvas size.
411
		scheduledLayoutClean = scheduledLayoutClean || clean;
412
	 * 
412
		synchronized (this) {
413
	 * @param width
413
			if (scheduledLayoutRunnable == null) {
414
	 * @param height
414
				Display.getDefault().asyncExec(
415
	 */
415
						scheduledLayoutRunnable = new Runnable() {
416
	public void setPreferredSize(int width, int height) {
416
							public void run() {
417
		this.preferredSize = new Dimension(width, height);
417
								Animation.markBegin();
418
	}
418
								getLayoutContext().applyLayout(
419
419
										scheduledLayoutClean);
420
	/**
420
								layoutContext.flushChanges(false);
421
	 * @param algorithm
421
								Animation.run(ANIMATION_TIME);
422
	 */
422
								getLightweightSystem().getUpdateManager()
423
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean applyLayout) {
423
										.performUpdate();
424
		this.layoutAlgorithm = algorithm;
424
								synchronized (Graph.this) {
425
		if (applyLayout) {
425
									scheduledLayoutRunnable = null;
426
			applyLayout();
426
									scheduledLayoutClean = false;
427
		}
427
								}
428
	}
428
							}
429
429
						});
430
	public LayoutAlgorithm getLayoutAlgorithm() {
430
			}
431
		return this.layoutAlgorithm;
431
		}
432
	}
432
	}
433
433
434
	/**
434
	/**
435
	 * Finds a figure at the location X, Y in the graph
435
	 * Sets the preferred size of the layout area. Size of ( -1, -1) uses the
436
	 * 
436
	 * current canvas size.
437
	 * This point should be translated to relative before calling findFigureAt
437
	 * 
438
	 */
438
	 * @param width
439
	public IFigure getFigureAt(int x, int y) {
439
	 * @param height
440
		IFigure figureUnderMouse = this.getContents().findFigureAt(x, y, new TreeSearch() {
440
	 */
441
441
	public void setPreferredSize(int width, int height) {
442
			public boolean accept(IFigure figure) {
442
		this.preferredSize = new Dimension(width, height);
443
				return true;
443
		getLayoutContext().fireBoundsChangedEvent();
444
			}
444
	}
445
445
446
			public boolean prune(IFigure figure) {
446
	/**
447
				IFigure parent = figure.getParent();
447
	 * @return the preferred size of the layout area.
448
				// @tag TODO Zest : change these to from getParent to their actual layer names
448
	 * @since 2.0
449
449
	 */
450
				if (parent == fishEyeLayer) {
450
	public Dimension getPreferredSize() {
451
					// If it node is on the fish eye layer, don't worry about it.
451
		if (preferredSize.width < 0 || preferredSize.height < 0) {
452
					return true;
452
			org.eclipse.swt.graphics.Point size = getSize();
453
				}
453
			double scale = getRootLayer().getScale();
454
				if (parent instanceof ContainerFigure && figure instanceof PolylineConnection) {
454
			return new Dimension((int) (size.x / scale + 0.5), (int) (size.y
455
					return false;
455
					/ scale + 0.5));
456
				}
456
		}
457
				if (parent == zestRootLayer || parent == zestRootLayer.getParent() || parent == zestRootLayer.getParent().getParent()) {
457
		return preferredSize;
458
					return false;
458
	}
459
				}
459
460
				GraphItem item = (GraphItem) figure2ItemMap.get(figure);
460
	/**
461
				if (item != null && item.getItemType() == GraphItem.CONTAINER) {
461
	 * @noreference This method is not intended to be referenced by clients.
462
					return false;
462
	 */
463
				} else if (figure instanceof FreeformLayer || parent instanceof FreeformLayer || figure instanceof ScrollPane || parent instanceof ScrollPane || parent instanceof ScalableFreeformLayeredPane || figure instanceof ScalableFreeformLayeredPane || figure instanceof FreeformViewport ||
463
	public InternalLayoutContext getLayoutContext() {
464
						parent instanceof FreeformViewport) {
464
		if (layoutContext == null) {
465
					return false;
465
			layoutContext = new InternalLayoutContext(this);
466
				}
466
		}
467
				return true;
467
		return layoutContext;
468
			}
468
	}
469
469
470
		});
470
	/**
471
		return figureUnderMouse;
471
	 * @param algorithm
472
472
	 * @since 2.0
473
	}
473
	 */
474
474
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm,
475
	// /////////////////////////////////////////////////////////////////////////////////
475
			boolean applyLayout) {
476
	// PRIVATE METHODS. These are NON API
476
		getLayoutContext().setLayoutAlgorithm(algorithm);
477
	// /////////////////////////////////////////////////////////////////////////////////
477
		if (applyLayout) {
478
	class DragSupport implements MouseMotionListener, org.eclipse.draw2d.MouseListener {
478
			applyLayout();
479
		/**
479
		}
480
		 * 
480
	}
481
		 */
481
482
		Graph graph = null;
482
	/**
483
		Point lastLocation = null;
483
	 * @since 2.0
484
		GraphItem fisheyedItem = null;
484
	 */
485
		boolean isDragging = false;
485
	public LayoutAlgorithm getLayoutAlgorithm() {
486
486
		return getLayoutContext().getLayoutAlgorithm();
487
		DragSupport(Graph graph) {
487
	}
488
			this.graph = graph;
488
489
		}
489
	/**
490
490
	 * @since 2.0
491
		public void mouseDragged(org.eclipse.draw2d.MouseEvent me) {
491
	 */
492
			if (!isDragging) {
492
	public void setSubgraphFactory(SubgraphFactory factory) {
493
				return;
493
		getLayoutContext().setSubgraphFactory(factory);
494
			}
494
	}
495
			Point mousePoint = new Point(me.x, me.y);
495
496
			Point tempPoint = mousePoint.getCopy();
496
	/**
497
			if (selectedItems.size() > 0) {
497
	 * @since 2.0
498
				Iterator iterator = selectedItems.iterator();
498
	 */
499
				while (iterator.hasNext()) {
499
	public SubgraphFactory getSubgraphFactory() {
500
					GraphItem item = (GraphItem) iterator.next();
500
		return getLayoutContext().getSubgraphFactory();
501
					if ((item.getItemType() == GraphItem.NODE) || (item.getItemType() == GraphItem.CONTAINER)) {
501
	}
502
						// @tag Zest.selection Zest.move : This is where the node movement is tracked
502
503
						Point pointCopy = mousePoint.getCopy();
503
	/**
504
504
	 * @since 2.0
505
						Point tempLastLocation = lastLocation.getCopy();
505
	 */
506
						item.getFigure().getParent().translateToRelative(tempLastLocation);
506
	public void setExpandCollapseManager(
507
						item.getFigure().getParent().translateFromParent(tempLastLocation);
507
			ExpandCollapseManager expandCollapseManager) {
508
508
		getLayoutContext().setExpandCollapseManager(expandCollapseManager);
509
						item.getFigure().getParent().translateToRelative(pointCopy);
509
	}
510
						item.getFigure().getParent().translateFromParent(pointCopy);
510
511
						Point delta = new Point(pointCopy.x - tempLastLocation.x, pointCopy.y - tempLastLocation.y);
511
	/**
512
						if (item.getItemType() == GraphItem.NODE || item.getItemType() == GraphItem.CONTAINER) {
512
	 * @since 2.0
513
							GraphNode node = (GraphNode) item;
513
	 */
514
							node.setLocation(node.getLocation().x + delta.x, node.getLocation().y + delta.y);
514
	public ExpandCollapseManager getExpandCollapseManager() {
515
515
		return getLayoutContext().getExpandCollapseManager();
516
						}
516
	}
517
						/*
517
518
						else if (item.getItemType() == GraphItem.CONTAINER) {
518
	/**
519
							GraphContainer container = (GraphContainer) item;
519
	 * Adds a filter used for hiding elements from layout algorithm.
520
							container.setLocation(container.getLocation().x + delta.x, container.getLocation().y + delta.y);
520
	 * 
521
						}
521
	 * NOTE: If a node or subgraph if filtered out, all connections adjacent to
522
						*/
522
	 * it should also be filtered out. Otherwise layout algorithm may behave in
523
					} else {
523
	 * an unexpected way.
524
						// There is no movement for connection
524
	 * 
525
					}
525
	 * @param filter
526
				}
526
	 *            filter to add
527
				if (fisheyedFigure != null) {
527
	 * @since 2.0
528
					Point pointCopy = mousePoint.getCopy();
528
	 */
529
529
	public void addLayoutFilter(LayoutFilter filter) {
530
					Point tempLastLocation = lastLocation.getCopy();
530
		getLayoutContext().addFilter(filter);
531
					fisheyedFigure.translateToRelative(tempLastLocation);
531
	}
532
					fisheyedFigure.translateFromParent(tempLastLocation);
532
533
533
	/**
534
					fisheyedFigure.translateToRelative(pointCopy);
534
	 * Removes given layout filter. If it had not been added to this graph, this
535
					fisheyedFigure.translateFromParent(pointCopy);
535
	 * method does nothing.
536
					Point delta = new Point(pointCopy.x - tempLastLocation.x, pointCopy.y - tempLastLocation.y);
536
	 * 
537
					Point point = new Point(fisheyedFigure.getBounds().x + delta.x, fisheyedFigure.getBounds().y + delta.y);
537
	 * @param filter
538
					fishEyeLayer.setConstraint(fisheyedFigure, new Rectangle(point, fisheyedFigure.getSize()));
538
	 *            filter to remove
539
					fishEyeLayer.getUpdateManager().performUpdate();
539
	 * @since 2.0
540
					//fisheyedFigure.setBounds(new Rectangle(point2, fisheyedFigure.getSize()));
540
	 */
541
					//fisheyedFigure.setLocation(new Point(fisheyedFigure.getBounds().x + delta.x, fisheyedFigure.getBounds().y + delta.y));
541
	public void removeLayoutFilter(LayoutFilter filter) {
542
				}
542
		getLayoutContext().removeFilter(filter);
543
			}
543
	}
544
			lastLocation = tempPoint;
544
545
			//oldLocation = mousePoint;
545
	/**
546
		}
546
	 * Finds a figure at the location X, Y in the graph
547
547
	 * 
548
		public void mouseEntered(org.eclipse.draw2d.MouseEvent me) {
548
	 * This point should be translated to relative before calling findFigureAt
549
549
	 */
550
		}
550
	public IFigure getFigureAt(int x, int y) {
551
551
		IFigure figureUnderMouse = this.getContents().findFigureAt(x, y,
552
		public void mouseExited(org.eclipse.draw2d.MouseEvent me) {
552
				new TreeSearch() {
553
553
554
		}
554
					public boolean accept(IFigure figure) {
555
555
						return true;
556
		public void mouseHover(org.eclipse.draw2d.MouseEvent me) {
556
					}
557
557
558
		}
558
					public boolean prune(IFigure figure) {
559
559
						IFigure parent = figure.getParent();
560
		/**
560
						// @tag TODO Zest : change these to from getParent to
561
		 * This tracks whenever a mouse moves. The only thing we care about is
561
						// their
562
		 * fisheye(ing) nodes.  This means whenever the mouse moves we check if
562
						// actual layer names
563
		 * we need to fisheye on a node or not.
563
564
		 */
564
						if (parent == fishEyeLayer) {
565
		public void mouseMoved(org.eclipse.draw2d.MouseEvent me) {
565
							// If it node is on the fish eye layer, don't worry
566
			Point mousePoint = new Point(me.x, me.y);
566
							// about
567
			getRootLayer().translateToRelative(mousePoint);
567
							// it.
568
			IFigure figureUnderMouse = getFigureAt(mousePoint.x, mousePoint.y);
568
							return true;
569
569
						}
570
			if (figureUnderMouse != null) {
570
						if (parent instanceof ContainerFigure
571
				// There is a figure under this mouse
571
								&& figure instanceof PolylineConnection) {
572
				GraphItem itemUnderMouse = (GraphItem) figure2ItemMap.get(figureUnderMouse);
572
							return false;
573
				if (itemUnderMouse == fisheyedItem) {
573
						}
574
574
						if (parent == zestRootLayer
575
				} else if (itemUnderMouse != null && itemUnderMouse.getItemType() == GraphItem.NODE) {
575
								|| parent == zestRootLayer.getParent()
576
					fisheyedItem = itemUnderMouse;
576
								|| parent == zestRootLayer.getParent()
577
					fisheyedFigure = ((GraphNode) itemUnderMouse).fishEye(true, true);
577
										.getParent()) {
578
					if (fisheyedFigure == null) {
578
							return false;
579
						// If there is no fisheye figure (this means that the node does not support a fish eye)
579
						}
580
						// then remove the fisheyed item
580
						GraphItem item = (GraphItem) figure2ItemMap.get(figure);
581
						fisheyedItem = null;
581
						if (item != null
582
					}
582
								&& item.getItemType() == GraphItem.CONTAINER) {
583
				} else if (fisheyedItem != null) {
583
							return false;
584
					((GraphNode) fisheyedItem).fishEye(false, true);
584
						} else if (figure instanceof FreeformLayer
585
					fisheyedItem = null;
585
								|| parent instanceof FreeformLayer
586
					fisheyedFigure = null;
586
								|| figure instanceof ScrollPane
587
				}
587
								|| parent instanceof ScrollPane
588
			} else {
588
								|| parent instanceof ScalableFreeformLayeredPane
589
				if (fisheyedItem != null) {
589
								|| figure instanceof ScalableFreeformLayeredPane
590
					((GraphNode) fisheyedItem).fishEye(false, true);
590
								|| figure instanceof FreeformViewport
591
					fisheyedItem = null;
591
								|| parent instanceof FreeformViewport) {
592
					fisheyedFigure = null;
592
							return false;
593
				}
593
						}
594
			}
594
						return true;
595
		}
595
					}
596
596
597
		public void mouseDoubleClicked(org.eclipse.draw2d.MouseEvent me) {
597
				});
598
598
		return figureUnderMouse;
599
		}
599
600
600
	}
601
		public void mousePressed(org.eclipse.draw2d.MouseEvent me) {
601
602
			isDragging = true;
602
	private class DragSupport implements MouseMotionListener,
603
			Point mousePoint = new Point(me.x, me.y);
603
			org.eclipse.draw2d.MouseListener {
604
			lastLocation = mousePoint.getCopy();
604
605
605
		Point dragStartLocation = null;
606
			getRootLayer().translateToRelative(mousePoint);
606
		IFigure draggedSubgraphFigure = null;
607
607
		/** locations of dragged items relative to cursor position */
608
			if (me.getState() == org.eclipse.draw2d.MouseEvent.ALT) {
608
		ArrayList relativeLocations = new ArrayList();
609
				double scale = getRootLayer().getScale();
609
		GraphItem fisheyedItem = null;
610
				scale *= 1.05;
610
		boolean isDragging = false;
611
				getRootLayer().setScale(scale);
611
612
				Point newMousePoint = mousePoint.getCopy().scale(1.05);
612
		public void mouseDragged(org.eclipse.draw2d.MouseEvent me) {
613
				Point delta = new Point(newMousePoint.x - mousePoint.x, newMousePoint.y - mousePoint.y);
613
			if (!isDragging) {
614
				Point newViewLocation = getViewport().getViewLocation().getCopy().translate(delta);
614
				return;
615
				getViewport().setViewLocation(newViewLocation);
615
			}
616
				lastLocation.scale(scale);
616
			if (selectedItems.isEmpty()) {
617
617
				IFigure figureUnderMouse = getFigureAt(dragStartLocation.x,
618
				clearSelection();
618
						dragStartLocation.y);
619
				return;
619
				if (subgraphFigures.contains(figureUnderMouse)) {
620
			} else if (me.getState() == (org.eclipse.draw2d.MouseEvent.ALT | org.eclipse.draw2d.MouseEvent.SHIFT)) {
620
					draggedSubgraphFigure = figureUnderMouse;
621
				double scale = getRootLayer().getScale();
621
				}
622
				scale /= 1.05;
622
			}
623
				getRootLayer().setScale(scale);
623
624
624
			Point mousePoint = new Point(me.x, me.y);
625
				Point newMousePoint = mousePoint.getCopy().scale(1 / 1.05);
625
			if (!selectedItems.isEmpty() || draggedSubgraphFigure != null) {
626
				Point delta = new Point(newMousePoint.x - mousePoint.x, newMousePoint.y - mousePoint.y);
626
627
				Point newViewLocation = getViewport().getViewLocation().getCopy().translate(delta);
627
				if (relativeLocations.isEmpty()) {
628
				getViewport().setViewLocation(newViewLocation);
628
					for (Iterator iterator = selectedItems.iterator(); iterator
629
				clearSelection();
629
							.hasNext();) {
630
				return;
630
						GraphItem item = (GraphItem) iterator.next();
631
			} else {
631
						if ((item.getItemType() == GraphItem.NODE)
632
				boolean hasSelection = selectedItems.size() > 0;
632
								|| (item.getItemType() == GraphItem.CONTAINER)) {
633
				IFigure figureUnderMouse = getFigureAt(mousePoint.x, mousePoint.y);
633
							relativeLocations.add(getRelativeLocation(item
634
				getRootLayer().translateFromParent(mousePoint);
634
									.getFigure()));
635
635
						}
636
				if (figureUnderMouse != null) {
636
					}
637
					figureUnderMouse.getParent().translateFromParent(mousePoint);
637
					if (draggedSubgraphFigure != null) {
638
				}
638
						relativeLocations
639
				// If the figure under the mouse is the canvas, and CTRL is not being held down, then select
639
								.add(getRelativeLocation(draggedSubgraphFigure));
640
				// nothing
640
					}
641
				if (figureUnderMouse == null || figureUnderMouse == graph) {
641
				}
642
					if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) {
642
643
						clearSelection();
643
				Iterator locationsIterator = relativeLocations.iterator();
644
						if (hasSelection) {
644
				for (Iterator selectionIterator = selectedItems.iterator(); selectionIterator
645
							fireWidgetSelectedEvent(null);
645
						.hasNext();) {
646
							hasSelection = false;
646
					GraphItem item = (GraphItem) selectionIterator.next();
647
						}
647
					if ((item.getItemType() == GraphItem.NODE)
648
					}
648
							|| (item.getItemType() == GraphItem.CONTAINER)) {
649
					return;
649
						Point pointCopy = mousePoint.getCopy();
650
				}
650
						Point relativeLocation = (Point) locationsIterator
651
651
								.next();
652
				GraphItem itemUnderMouse = (GraphItem) figure2ItemMap.get(figureUnderMouse);
652
653
				if (itemUnderMouse == null) {
653
						item.getFigure().getParent().translateToRelative(
654
					if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) {
654
								pointCopy);
655
						clearSelection();
655
						item.getFigure().getParent().translateFromParent(
656
						if (hasSelection) {
656
								pointCopy);
657
							fireWidgetSelectedEvent(null);
657
658
							hasSelection = false;
658
						((GraphNode) item)
659
						}
659
								.setLocation(relativeLocation.x + pointCopy.x,
660
					}
660
										relativeLocation.y + pointCopy.y);
661
					return;
661
					} else {
662
				}
662
						// There is no movement for connection
663
				if (selectedItems.contains(itemUnderMouse)) {
663
					}
664
					// We have already selected this node, and CTRL is being held down, remove this selection
664
				}
665
					// @tag Zest.selection : This deselects when you have CTRL pressed
665
				if (draggedSubgraphFigure != null) {
666
					if (me.getState() == org.eclipse.draw2d.MouseEvent.CONTROL) {
666
					Point pointCopy = mousePoint.getCopy();
667
						selectedItems.remove(itemUnderMouse);
667
					draggedSubgraphFigure.getParent().translateToRelative(
668
						(itemUnderMouse).unhighlight();
668
							pointCopy);
669
						fireWidgetSelectedEvent(itemUnderMouse);
669
					draggedSubgraphFigure.getParent().translateFromParent(
670
					}
670
							pointCopy);
671
					return;
671
					Point relativeLocation = (Point) locationsIterator.next();
672
				}
672
					pointCopy.x += relativeLocation.x;
673
673
					pointCopy.y += relativeLocation.y;
674
				if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) {
674
675
					clearSelection();
675
					draggedSubgraphFigure.setLocation(pointCopy);
676
				}
676
				}
677
677
			}
678
				if (itemUnderMouse.getItemType() == GraphItem.NODE) {
678
		}
679
					// @tag Zest.selection : This is where the nodes are selected
679
680
					selectedItems.add(itemUnderMouse);
680
		private Point getRelativeLocation(IFigure figure) {
681
					((GraphNode) itemUnderMouse).highlight();
681
			Point location = figure.getBounds().getTopLeft();
682
					fireWidgetSelectedEvent(itemUnderMouse);
682
			Point mousePointCopy = dragStartLocation.getCopy();
683
				} else if (itemUnderMouse.getItemType() == GraphItem.CONNECTION) {
683
			figure.getParent().translateToRelative(mousePointCopy);
684
					selectedItems.add(itemUnderMouse);
684
			figure.getParent().translateFromParent(mousePointCopy);
685
					((GraphConnection) itemUnderMouse).highlight();
685
			location.x -= mousePointCopy.x;
686
					fireWidgetSelectedEvent(itemUnderMouse);
686
			location.y -= mousePointCopy.y;
687
687
			return location;
688
				} else if (itemUnderMouse.getItemType() == GraphItem.CONTAINER) {
688
		}
689
					selectedItems.add(itemUnderMouse);
689
690
					((GraphContainer) itemUnderMouse).highlight();
690
		public void mouseEntered(org.eclipse.draw2d.MouseEvent me) {
691
					fireWidgetSelectedEvent(itemUnderMouse);
691
692
				}
692
		}
693
			}
693
694
694
		public void mouseExited(org.eclipse.draw2d.MouseEvent me) {
695
		}
695
696
696
		}
697
		public void mouseReleased(org.eclipse.draw2d.MouseEvent me) {
697
698
			isDragging = false;
698
		public void mouseHover(org.eclipse.draw2d.MouseEvent me) {
699
699
700
		}
700
		}
701
701
702
	}
702
		/**
703
703
		 * This tracks whenever a mouse moves. The only thing we care about is
704
	private void clearSelection() {
704
		 * fisheye(ing) nodes. This means whenever the mouse moves we check if
705
		if (selectedItems.size() > 0) {
705
		 * we need to fisheye on a node or not.
706
			Iterator iterator = selectedItems.iterator();
706
		 */
707
			while (iterator.hasNext()) {
707
		public void mouseMoved(org.eclipse.draw2d.MouseEvent me) {
708
				GraphItem item = (GraphItem) iterator.next();
708
			Point mousePoint = new Point(me.x, me.y);
709
				item.unhighlight();
709
			getRootLayer().translateToRelative(mousePoint);
710
				iterator.remove();
710
			IFigure figureUnderMouse = getFigureAt(mousePoint.x, mousePoint.y);
711
			}
711
712
		}
712
			if (figureUnderMouse != null) {
713
	}
713
				// There is a figure under this mouse
714
714
				GraphItem itemUnderMouse = (GraphItem) figure2ItemMap
715
	private void fireWidgetSelectedEvent(Item item) {
715
						.get(figureUnderMouse);
716
		Iterator iterator = selectionListeners.iterator();
716
				if (itemUnderMouse == fisheyedItem) {
717
		while (iterator.hasNext()) {
717
					return;
718
			SelectionListener selectionListener = (SelectionListener) iterator.next();
718
				}
719
			Event swtEvent = new Event();
719
				if (fisheyedItem != null) {
720
			swtEvent.item = item;
720
					((GraphNode) fisheyedItem).fishEye(false, true);
721
			swtEvent.widget = this;
721
					fisheyedItem = null;
722
			SelectionEvent event = new SelectionEvent(swtEvent);
722
				}
723
			selectionListener.widgetSelected(event);
723
				if (itemUnderMouse != null
724
		}
724
						&& itemUnderMouse.getItemType() == GraphItem.NODE) {
725
725
					fisheyedItem = itemUnderMouse;
726
	}
726
					IFigure fisheyedFigure = ((GraphNode) itemUnderMouse)
727
727
							.fishEye(true, true);
728
	/**
728
					if (fisheyedFigure == null) {
729
	 * Moves the edge to the highlight layer. This moves the edge above the
729
						// If there is no fisheye figure (this means that the
730
	 * nodes
730
						// node does not support a fish eye)
731
	 * 
731
						// then remove the fisheyed item
732
	 * @param connection
732
						fisheyedItem = null;
733
	 */
733
					}
734
	void highlightEdge(GraphConnection connection) {
734
				}
735
		IFigure figure = connection.getConnectionFigure();
735
			} else {
736
		if (figure != null && !connection.isHighlighted()) {
736
				if (fisheyedItem != null) {
737
			zestRootLayer.highlightConnection(figure);
737
					((GraphNode) fisheyedItem).fishEye(false, true);
738
		}
738
					fisheyedItem = null;
739
	}
739
				}
740
740
			}
741
	/**
741
		}
742
	 * Moves the edge from the edge feedback layer back to the edge layer
742
743
	 * 
743
		public void mouseDoubleClicked(org.eclipse.draw2d.MouseEvent me) {
744
	 * @param graphConnection
744
745
	 */
745
		}
746
	void unhighlightEdge(GraphConnection connection) {
746
747
		IFigure figure = connection.getConnectionFigure();
747
		public void mousePressed(org.eclipse.draw2d.MouseEvent me) {
748
		if (figure != null && connection.isHighlighted()) {
748
			isDragging = true;
749
			zestRootLayer.unHighlightConnection(figure);
749
			Point mousePoint = new Point(me.x, me.y);
750
		}
750
			dragStartLocation = mousePoint.getCopy();
751
	}
751
752
752
			getRootLayer().translateToRelative(mousePoint);
753
	/**
753
754
	 * Moves the node onto the node feedback layer
754
			if (me.getState() == org.eclipse.draw2d.MouseEvent.ALT) {
755
	 * 
755
				double scale = getRootLayer().getScale();
756
	 * @param node
756
				scale *= 1.05;
757
	 */
757
				getRootLayer().setScale(scale);
758
	void highlightNode(GraphNode node) {
758
				Point newMousePoint = mousePoint.getCopy().scale(1.05);
759
		IFigure figure = node.getNodeFigure();
759
				Point delta = new Point(newMousePoint.x - mousePoint.x,
760
		if (figure != null && !node.isHighlighted()) {
760
						newMousePoint.y - mousePoint.y);
761
			zestRootLayer.highlightNode(figure);
761
				Point newViewLocation = getViewport().getViewLocation()
762
		}
762
						.getCopy().translate(delta);
763
	}
763
				getViewport().setViewLocation(newViewLocation);
764
764
765
	/**
765
				clearSelection();
766
	 * Moves the node onto the node feedback layer
766
				return;
767
	 * 
767
			} else if (me.getState() == (org.eclipse.draw2d.MouseEvent.ALT | org.eclipse.draw2d.MouseEvent.SHIFT)) {
768
	 * @param node
768
				double scale = getRootLayer().getScale();
769
	 */
769
				scale /= 1.05;
770
	void highlightNode(GraphContainer node) {
770
				getRootLayer().setScale(scale);
771
		IFigure figure = node.getNodeFigure();
771
772
		if (figure != null && !node.isHighlighted()) {
772
				Point newMousePoint = mousePoint.getCopy().scale(1 / 1.05);
773
			zestRootLayer.highlightNode(figure);
773
				Point delta = new Point(newMousePoint.x - mousePoint.x,
774
		}
774
						newMousePoint.y - mousePoint.y);
775
	}
775
				Point newViewLocation = getViewport().getViewLocation()
776
776
						.getCopy().translate(delta);
777
	/**
777
				getViewport().setViewLocation(newViewLocation);
778
	 * Moves the node off the node feedback layer
778
				clearSelection();
779
	 * 
779
				return;
780
	 * @param node
780
			} else {
781
	 */
781
				boolean hasSelection = selectedItems.size() > 0;
782
	void unhighlightNode(GraphContainer node) {
782
				IFigure figureUnderMouse = getFigureAt(mousePoint.x,
783
		IFigure figure = node.getNodeFigure();
783
						mousePoint.y);
784
		if (figure != null && node.isHighlighted()) {
784
				getRootLayer().translateFromParent(mousePoint);
785
			zestRootLayer.unHighlightNode(figure);
785
786
		}
786
				if (figureUnderMouse != null) {
787
	}
787
					figureUnderMouse.getParent()
788
788
							.translateFromParent(mousePoint);
789
	/**
789
				}
790
	 * Moves the node off the node feedback layer
790
				// If the figure under the mouse is the canvas, and CTRL is not
791
	 * 
791
				// being held down, then select
792
	 * @param node
792
				// nothing
793
	 */
793
				if (figureUnderMouse == null || figureUnderMouse == Graph.this) {
794
	void unhighlightNode(GraphNode node) {
794
					if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) {
795
		IFigure figure = node.getNodeFigure();
795
						clearSelection();
796
		if (figure != null && node.isHighlighted()) {
796
						if (hasSelection) {
797
			zestRootLayer.unHighlightNode(figure);
797
							fireWidgetSelectedEvent(null);
798
		}
798
							hasSelection = false;
799
	}
799
						}
800
800
					}
801
	/**
801
					return;
802
	 * Converts the list of GraphModelConnection objects into an array and
802
				}
803
	 * returns it.
803
804
	 * 
804
				GraphItem itemUnderMouse = (GraphItem) figure2ItemMap
805
	 * @return GraphModelConnection[]
805
						.get(figureUnderMouse);
806
	 */
806
				if (itemUnderMouse == null) {
807
	GraphConnection[] getConnectionsArray() {
807
					if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) {
808
		GraphConnection[] connsArray = new GraphConnection[connections.size()];
808
						clearSelection();
809
		connsArray = (GraphConnection[]) connections.toArray(connsArray);
809
						if (hasSelection) {
810
		return connsArray;
810
							fireWidgetSelectedEvent(null);
811
	}
811
							hasSelection = false;
812
812
						}
813
	LayoutRelationship[] getConnectionsToLayout(List nodesToLayout) {
813
					}
814
		// @tag zest.bug.156528-Filters.follows : make sure not to layout
814
					return;
815
		// filtered connections, if the style says so.
815
				}
816
		LayoutRelationship[] entities;
816
				if (selectedItems.contains(itemUnderMouse)) {
817
		if (ZestStyles.checkStyle(style, ZestStyles.IGNORE_INVISIBLE_LAYOUT)) {
817
					// We have already selected this node, and CTRL is being
818
			LinkedList connectionList = new LinkedList();
818
					// held down, remove this selection
819
			for (Iterator i = this.getConnections().iterator(); i.hasNext();) {
819
					// @tag Zest.selection : This deselects when you have CTRL
820
				GraphConnection next = (GraphConnection) i.next();
820
					// pressed
821
				if (next.isVisible() && nodesToLayout.contains(next.getSource()) && nodesToLayout.contains(next.getDestination())) {
821
					if (me.getState() == org.eclipse.draw2d.MouseEvent.CONTROL) {
822
					connectionList.add(next.getLayoutRelationship());
822
						selectedItems.remove(itemUnderMouse);
823
				}
823
						(itemUnderMouse).unhighlight();
824
			}
824
						fireWidgetSelectedEvent(itemUnderMouse);
825
			entities = (LayoutRelationship[]) connectionList.toArray(new LayoutRelationship[] {});
825
					}
826
		} else {
826
					return;
827
			LinkedList nodeList = new LinkedList();
827
				}
828
			for (Iterator i = this.getConnections().iterator(); i.hasNext();) {
828
829
				GraphConnection next = (GraphConnection) i.next();
829
				if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) {
830
				if (nodesToLayout.contains(next.getSource()) && nodesToLayout.contains(next.getDestination())) {
830
					clearSelection();
831
					nodeList.add(next.getLayoutRelationship());
831
				}
832
				}
832
833
			}
833
				if (itemUnderMouse.getItemType() == GraphItem.NODE) {
834
			entities = (LayoutRelationship[]) nodeList.toArray(new LayoutRelationship[] {});
834
					// @tag Zest.selection : This is where the nodes are
835
		}
835
					// selected
836
		return entities;
836
					selectedItems.add(itemUnderMouse);
837
	}
837
					((GraphNode) itemUnderMouse).highlight();
838
838
					fireWidgetSelectedEvent(itemUnderMouse);
839
	LayoutEntity[] getNodesToLayout(List nodes) {
839
				} else if (itemUnderMouse.getItemType() == GraphItem.CONNECTION) {
840
		// @tag zest.bug.156528-Filters.follows : make sure not to layout
840
					selectedItems.add(itemUnderMouse);
841
		// filtered nodes, if the style says so.
841
					((GraphConnection) itemUnderMouse).highlight();
842
		LayoutEntity[] entities;
842
					fireWidgetSelectedEvent(itemUnderMouse);
843
		if (ZestStyles.checkStyle(style, ZestStyles.IGNORE_INVISIBLE_LAYOUT)) {
843
844
			LinkedList nodeList = new LinkedList();
844
				} else if (itemUnderMouse.getItemType() == GraphItem.CONTAINER) {
845
			for (Iterator i = nodes.iterator(); i.hasNext();) {
845
					selectedItems.add(itemUnderMouse);
846
				GraphNode next = (GraphNode) i.next();
846
					((GraphContainer) itemUnderMouse).highlight();
847
				if (next.isVisible()) {
847
					fireWidgetSelectedEvent(itemUnderMouse);
848
					nodeList.add(next.getLayoutEntity());
848
				}
849
				}
849
			}
850
			}
850
851
			entities = (LayoutEntity[]) nodeList.toArray(new LayoutEntity[] {});
851
		}
852
		} else {
852
853
			LinkedList nodeList = new LinkedList();
853
		public void mouseReleased(org.eclipse.draw2d.MouseEvent me) {
854
			for (Iterator i = nodes.iterator(); i.hasNext();) {
854
			isDragging = false;
855
				GraphNode next = (GraphNode) i.next();
855
			relativeLocations.clear();
856
				nodeList.add(next.getLayoutEntity());
856
			draggedSubgraphFigure = null;
857
			}
857
		}
858
			entities = (LayoutEntity[]) nodeList.toArray(new LayoutEntity[] {});
858
859
		}
859
	}
860
		return entities;
860
861
	}
861
	private void clearSelection() {
862
862
		if (selectedItems.size() > 0) {
863
	void removeConnection(GraphConnection connection) {
863
			Iterator iterator = selectedItems.iterator();
864
		IFigure figure = connection.getConnectionFigure();
864
			while (iterator.hasNext()) {
865
		PolylineConnection sourceContainerConnectionFigure = connection.getSourceContainerConnectionFigure();
865
				GraphItem item = (GraphItem) iterator.next();
866
		PolylineConnection targetContainerConnectionFigure = connection.getTargetContainerConnectionFigure();
866
				item.unhighlight();
867
		connection.removeFigure();
867
				iterator.remove();
868
		this.getConnections().remove(connection);
868
			}
869
		figure2ItemMap.remove(figure);
869
		}
870
		if (sourceContainerConnectionFigure != null) {
870
	}
871
			figure2ItemMap.remove(sourceContainerConnectionFigure);
871
872
		}
872
	private void fireWidgetSelectedEvent(Item item) {
873
		if (targetContainerConnectionFigure != null) {
873
		Iterator iterator = selectionListeners.iterator();
874
			figure2ItemMap.remove(targetContainerConnectionFigure);
874
		while (iterator.hasNext()) {
875
		}
875
			SelectionListener selectionListener = (SelectionListener) iterator
876
	}
876
					.next();
877
877
			Event swtEvent = new Event();
878
	void removeNode(GraphNode node) {
878
			swtEvent.item = item;
879
		IFigure figure = node.getNodeFigure();
879
			swtEvent.widget = this;
880
		if (figure.getParent() != null) {
880
			SelectionEvent event = new SelectionEvent(swtEvent);
881
			if (figure.getParent() instanceof ZestRootLayer) {
881
			selectionListener.widgetSelected(event);
882
				((ZestRootLayer) figure.getParent()).removeNode(figure);
882
		}
883
			} else {
883
884
				figure.getParent().remove(figure);
884
	}
885
			}
885
886
		}
886
	/**
887
		this.getNodes().remove(node);
887
	 * Converts the list of GraphModelConnection objects into an array and
888
		figure2ItemMap.remove(figure);
888
	 * returns it.
889
	}
889
	 * 
890
890
	 * @return GraphModelConnection[]
891
	void addConnection(GraphConnection connection, boolean addToEdgeLayer) {
891
	 */
892
		this.getConnections().add(connection);
892
	GraphConnection[] getConnectionsArray() {
893
		if (addToEdgeLayer) {
893
		GraphConnection[] connsArray = new GraphConnection[connections.size()];
894
			zestRootLayer.addConnection(connection.getFigure());
894
		connsArray = (GraphConnection[]) connections.toArray(connsArray);
895
		}
895
		return connsArray;
896
	}
896
	}
897
897
898
	/*
898
	void removeConnection(GraphConnection connection) {
899
	public void redraw() {
899
		IFigure figure = connection.getConnectionFigure();
900
900
		PolylineConnection sourceContainerConnectionFigure = connection
901
		Iterator iterator = this.getConnections().iterator();
901
				.getSourceContainerConnectionFigure();
902
		while (iterator.hasNext()) {
902
		PolylineConnection targetContainerConnectionFigure = connection
903
			GraphConnection connection = (GraphConnection) iterator.next();
903
				.getTargetContainerConnectionFigure();
904
			IFigure figure = connection.getFigure();
904
		connection.removeFigure();
905
			if (!zestRootLayer.getChildren().contains(figure)) {
905
		this.getConnections().remove(connection);
906
				if (true || false || false) {
906
		this.selectedItems.remove(connection);
907
					zestRootLayer.addConnection(connection.getFigure());
907
		figure2ItemMap.remove(figure);
908
				}
908
		if (sourceContainerConnectionFigure != null) {
909
			}
909
			figure2ItemMap.remove(sourceContainerConnectionFigure);
910
		}
910
		}
911
		iterator = this.getNodes().iterator();
911
		if (targetContainerConnectionFigure != null) {
912
		while (iterator.hasNext()) {
912
			figure2ItemMap.remove(targetContainerConnectionFigure);
913
			GraphNode graphNode = (GraphNode) iterator.next();
913
		}
914
			IFigure figure = graphNode.getFigure();
914
		getLayoutContext().fireConnectionRemovedEvent(connection.getLayout());
915
			if (!zestRootLayer.getChildren().contains(figure)) {
915
	}
916
				zestRootLayer.addNode(graphNode.getFigure());
916
917
			}
917
	void removeNode(GraphNode node) {
918
		}
918
		IFigure figure = node.getNodeFigure();
919
919
		if (figure.getParent() != null) {
920
		super.redraw();
920
			figure.getParent().remove(figure);
921
921
		}
922
	}
922
		this.getNodes().remove(node);
923
	*/
923
		this.selectedItems.remove(node);
924
924
		figure2ItemMap.remove(figure);
925
	void addNode(GraphNode node) {
925
		node.getLayout().dispose();
926
		this.getNodes().add(node);
926
	}
927
		zestRootLayer.addNode(node.getFigure());
927
928
	}
928
	void addConnection(GraphConnection connection, boolean addToEdgeLayer) {
929
929
		this.getConnections().add(connection);
930
	void addNode(GraphContainer graphContainer) {
930
		if (addToEdgeLayer) {
931
		this.getNodes().add(graphContainer);
931
			zestRootLayer.addConnection(connection.getFigure());
932
		zestRootLayer.addNode(graphContainer.getFigure());
932
		}
933
933
		getLayoutContext().fireConnectionAddedEvent(connection.getLayout());
934
	}
934
	}
935
935
936
	void registerItem(GraphItem item) {
936
	/**
937
		if (item.getItemType() == GraphItem.NODE) {
937
	 * @noreference This method is not intended to be referenced by clients.
938
			IFigure figure = item.getFigure();
938
	 */
939
			figure2ItemMap.put(figure, item);
939
	public void addNode(GraphNode node) {
940
		} else if (item.getItemType() == GraphItem.CONNECTION) {
940
		this.getNodes().add(node);
941
			IFigure figure = item.getFigure();
941
		zestRootLayer.addNode(node.getFigure());
942
			figure2ItemMap.put(figure, item);
942
		getLayoutContext().fireNodeAddedEvent(node.getLayout());
943
			if (((GraphConnection) item).getSourceContainerConnectionFigure() != null) {
943
	}
944
				figure2ItemMap.put(((GraphConnection) item).getSourceContainerConnectionFigure(), item);
944
945
			}
945
	/**
946
			if (((GraphConnection) item).getTargetContainerConnectionFigure() != null) {
946
	 * @noreference This method is not intended to be referenced by clients.
947
				figure2ItemMap.put(((GraphConnection) item).getTargetContainerConnectionFigure(), item);
947
	 */
948
			}
948
	public void addSubgraphFigure(IFigure figure) {
949
		} else if (item.getItemType() == GraphItem.CONTAINER) {
949
		zestRootLayer.addSubgraph(figure);
950
			IFigure figure = item.getFigure();
950
		subgraphFigures.add(figure);
951
			figure2ItemMap.put(figure, item);
951
	}
952
		} else {
952
953
			throw new RuntimeException("Unknown item type: " + item.getItemType());
953
	void removeSubgraphFigure(IFigure figure) {
954
		}
954
		subgraphFigures.remove(figure);
955
	}
955
		figure.getParent().remove(figure);
956
956
	}
957
	/*
957
958
958
	void registerItem(GraphItem item) {
959
959
		if (item.getItemType() == GraphItem.NODE) {
960
	/**
960
			IFigure figure = item.getFigure();
961
	 * Changes the figure for a particular node
961
			figure2ItemMap.put(figure, item);
962
	 */
962
		} else if (item.getItemType() == GraphItem.CONNECTION) {
963
	void changeNodeFigure(IFigure oldValue, IFigure newFigure, GraphNode graphItem) {
963
			IFigure figure = item.getFigure();
964
		if (zestRootLayer.getChildren().contains(oldValue)) {
964
			figure2ItemMap.put(figure, item);
965
			zestRootLayer.remove(oldValue);
965
			if (((GraphConnection) item).getSourceContainerConnectionFigure() != null) {
966
			figure2ItemMap.remove(oldValue);
966
				figure2ItemMap.put(((GraphConnection) item)
967
		}
967
						.getSourceContainerConnectionFigure(), item);
968
		figure2ItemMap.put(newFigure, graphItem);
968
			}
969
		zestRootLayer.add(newFigure);
969
			if (((GraphConnection) item).getTargetContainerConnectionFigure() != null) {
970
	}
970
				figure2ItemMap.put(((GraphConnection) item)
971
971
						.getTargetContainerConnectionFigure(), item);
972
	/**
972
			}
973
	 * Invoke all the constraint adapaters for this constraints
973
		} else if (item.getItemType() == GraphItem.CONTAINER) {
974
	 * 
974
			IFigure figure = item.getFigure();
975
	 * @param object
975
			figure2ItemMap.put(figure, item);
976
	 * @param constraint
976
		} else {
977
	 */
977
			throw new RuntimeException("Unknown item type: "
978
	void invokeConstraintAdapters(Object object, LayoutConstraint constraint) {
978
					+ item.getItemType());
979
		if (constraintAdapters == null) {
979
		}
980
			return;
980
	}
981
		}
981
982
		Iterator iterator = this.constraintAdapters.iterator();
982
	/**
983
		while (iterator.hasNext()) {
983
	 * Schedules a layout to be performed after the view is revealed (or
984
			ConstraintAdapter constraintAdapter = (ConstraintAdapter) iterator.next();
984
	 * immediately, if the view is already revealed).
985
			constraintAdapter.populateConstraint(object, constraint);
985
	 * 
986
		}
986
	 * @param clean
987
	}
987
	 */
988
988
	private void scheduleLayoutOnReveal(final boolean clean) {
989
	private void applyLayoutInternal() {
989
990
990
		final boolean[] isVisibleSync = new boolean[1];
991
		if ((this.getNodes().size() == 0)) {
991
		Display.getDefault().syncExec(new Runnable() {
992
			return;
992
			public void run() {
993
		}
993
				isVisibleSync[0] = isVisible();
994
994
			}
995
		int layoutStyle = 0;
995
		});
996
996
997
		if ((nodeStyle & ZestStyles.NODES_NO_LAYOUT_RESIZE) > 0) {
997
		if (isVisibleSync[0]) {
998
			layoutStyle = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
998
			applyLayoutInternal(clean);
999
		}
999
		} else {
1000
1000
			shouldSheduleLayout = true;
1001
		if (layoutAlgorithm == null) {
1001
		}
1002
			layoutAlgorithm = new TreeLayoutAlgorithm(layoutStyle);
1002
	}
1003
		}
1003
1004
1004
	private ScalableFigure createLayers() {
1005
		layoutAlgorithm.setStyle(layoutAlgorithm.getStyle() | layoutStyle);
1005
		rootlayer = new ScalableFreeformLayeredPane();
1006
1006
		rootlayer.setLayoutManager(new FreeformLayout());
1007
		// calculate the size for the layout algorithm
1007
		zestRootLayer = new ZestRootLayer();
1008
		Dimension d = this.getViewport().getSize();
1008
1009
		d.width = d.width - 10;
1009
		zestRootLayer.setLayoutManager(new FreeformLayout());
1010
		d.height = d.height - 10;
1010
1011
1011
		fishEyeLayer = new ScalableFreeformLayeredPane();
1012
		if (this.preferredSize.width >= 0) {
1012
		fishEyeLayer.setLayoutManager(new FreeformLayout());
1013
			d.width = preferredSize.width;
1013
1014
		}
1014
		rootlayer.add(zestRootLayer);
1015
		if (this.preferredSize.height >= 0) {
1015
		rootlayer.add(fishEyeLayer);
1016
			d.height = preferredSize.height;
1016
1017
		}
1017
		zestRootLayer.addLayoutListener(LayoutAnimator.getDefault());
1018
1018
		fishEyeLayer.addLayoutListener(LayoutAnimator.getDefault());
1019
		if (d.isEmpty()) {
1019
1020
			return;
1020
		rootlayer.addCoordinateListener(new CoordinateListener() {
1021
		}
1021
			public void coordinateSystemChanged(IFigure source) {
1022
		LayoutRelationship[] connectionsToLayout = getConnectionsToLayout(nodes);
1022
				if (preferredSize.width == -1 && preferredSize.height == -1) {
1023
		LayoutEntity[] nodesToLayout = getNodesToLayout(getNodes());
1023
					getLayoutContext().fireBoundsChangedEvent();
1024
1024
				}
1025
		try {
1025
			}
1026
			Animation.markBegin();
1026
		});
1027
			layoutAlgorithm.applyLayout(nodesToLayout, connectionsToLayout, 0, 0, d.width, d.height, false, false);
1027
1028
			Animation.run(ANIMATION_TIME);
1028
		return rootlayer;
1029
			getLightweightSystem().getUpdateManager().performUpdate();
1029
	}
1030
1030
1031
		} catch (InvalidLayoutConfiguration e) {
1031
	/**
1032
			e.printStackTrace();
1032
	 * This removes the fisheye from the graph. It uses an animation to make the
1033
		}
1033
	 * fisheye shrink, and then it finally clears the fisheye layer. This
1034
1034
	 * assumes that there is ever only 1 node on the fisheye layer at any time.
1035
	}
1035
	 * 
1036
1036
	 * @param fishEyeFigure
1037
	interface MyRunnable extends Runnable {
1037
	 *            The fisheye figure
1038
		public boolean isVisible();
1038
	 * @param regularFigure
1039
	}
1039
	 *            The regular figure (i.e. the non fisheye version)
1040
1040
	 */
1041
	/**
1041
	void removeFishEye(final IFigure fishEyeFigure,
1042
	 * Adds a reveal listener to the view. Note: A reveal listener will only
1042
			final IFigure regularFigure, boolean animate) {
1043
	 * every be called ONCE!!! even if a view comes and goes. There is no remove
1043
1044
	 * reveal listener. This is used to defer some events until after the view
1044
		if (!fishEyeLayer.getChildren().contains(fishEyeFigure)) {
1045
	 * is revealed.
1045
			return;
1046
	 * 
1046
		}
1047
	 * @param revealListener
1047
		if (animate) {
1048
	 */
1048
			Animation.markBegin();
1049
	private void addRevealListener(final RevealListener revealListener) {
1049
		}
1050
1050
1051
		MyRunnable myRunnable = new MyRunnable() {
1051
		Rectangle bounds = regularFigure.getBounds().getCopy();
1052
			boolean isVisible;
1052
		regularFigure.translateToAbsolute(bounds);
1053
1053
1054
			public boolean isVisible() {
1054
		double scale = rootlayer.getScale();
1055
				return this.isVisible;
1055
		fishEyeLayer.setScale(1 / scale);
1056
			}
1056
		fishEyeLayer.translateToRelative(bounds);
1057
1057
		fishEyeLayer.translateFromParent(bounds);
1058
			public void run() {
1058
1059
				isVisible = Graph.this.isVisible();
1059
		fishEyeLayer.setConstraint(fishEyeFigure, bounds);
1060
			}
1060
1061
1061
		for (Iterator iterator = fisheyeListeners.iterator(); iterator
1062
		};
1062
				.hasNext();) {
1063
		Display.getDefault().syncExec(myRunnable);
1063
			FisheyeListener listener = (FisheyeListener) iterator.next();
1064
1064
			listener.fisheyeRemoved(this, regularFigure, fishEyeFigure);
1065
		if (myRunnable.isVisible()) {
1065
		}
1066
			revealListener.revealed(this);
1066
1067
		} else {
1067
		if (animate) {
1068
			revealListeners.add(revealListener);
1068
			Animation.run(FISHEYE_ANIMATION_TIME * 2);
1069
		}
1069
		}
1070
	}
1070
		this.getRootLayer().getUpdateManager().performUpdate();
1071
1071
		fishEyeLayer.removeAll();
1072
	private ScalableFigure createLayers() {
1072
1073
		rootlayer = new ScalableFreeformLayeredPane();
1073
	}
1074
		rootlayer.setLayoutManager(new FreeformLayout());
1074
1075
		zestRootLayer = new ZestRootLayer();
1075
	/**
1076
1076
	 * Replaces the old fisheye figure with a new one.
1077
		zestRootLayer.setLayoutManager(new FreeformLayout());
1077
	 * 
1078
1078
	 * @param oldFigure
1079
		fishEyeLayer = new ScalableFreeformLayeredPane();
1079
	 * @param newFigure
1080
		fishEyeLayer.setLayoutManager(new FreeformLayout());
1080
	 */
1081
1081
	boolean replaceFishFigure(IFigure oldFigure, IFigure newFigure) {
1082
		rootlayer.add(zestRootLayer);
1082
		if (this.fishEyeLayer.getChildren().contains(oldFigure)) {
1083
		rootlayer.add(fishEyeLayer);
1083
			Rectangle bounds = oldFigure.getBounds();
1084
1084
			newFigure.setBounds(bounds);
1085
		zestRootLayer.addLayoutListener(LayoutAnimator.getDefault());
1085
			this.fishEyeLayer.remove(oldFigure);
1086
		fishEyeLayer.addLayoutListener(LayoutAnimator.getDefault());
1086
			this.fishEyeLayer.add(newFigure);
1087
		return rootlayer;
1087
1088
	}
1088
			for (Iterator iterator = fisheyeListeners.iterator(); iterator
1089
1089
					.hasNext();) {
1090
	/**
1090
				FisheyeListener listener = (FisheyeListener) iterator.next();
1091
	 * This removes the fisheye from the graph. It uses an animation to make the fisheye
1091
				listener.fisheyeReplaced(this, oldFigure, newFigure);
1092
	 * shrink, and then it finally clears the fisheye layer.  This assumes that there
1092
			}
1093
	 * is ever only 1 node on the fisheye layer at any time.  
1093
1094
	 * 
1094
			return true;
1095
	 * @param fishEyeFigure The fisheye figure
1095
		}
1096
	 * @param regularFigure The regular figure (i.e. the non fisheye version)
1096
		return false;
1097
	 */
1097
	}
1098
	void removeFishEye(final IFigure fishEyeFigure, final IFigure regularFigure, boolean animate) {
1098
1099
1099
	/**
1100
		if (!fishEyeLayer.getChildren().contains(fishEyeFigure)) {
1100
	 * Add a fisheye version of the node. This works by animating the change
1101
			return;
1101
	 * from the original node to the fisheyed one, and then placing the fisheye
1102
		}
1102
	 * node on the fisheye layer.
1103
		if (animate) {
1103
	 * 
1104
			Animation.markBegin();
1104
	 * @param startFigure
1105
		}
1105
	 *            The original node
1106
1106
	 * @param endFigure
1107
		Rectangle bounds = regularFigure.getBounds().getCopy();
1107
	 *            The fisheye figure
1108
		regularFigure.translateToAbsolute(bounds);
1108
	 * @param newBounds
1109
1109
	 *            The final size of the fisheyed figure
1110
		double scale = rootlayer.getScale();
1110
	 */
1111
		fishEyeLayer.setScale(1 / scale);
1111
	void fishEye(IFigure startFigure, IFigure endFigure, Rectangle newBounds,
1112
		fishEyeLayer.translateToRelative(bounds);
1112
			boolean animate) {
1113
		fishEyeLayer.translateFromParent(bounds);
1113
1114
1114
		fishEyeLayer.removeAll();
1115
		fishEyeLayer.setConstraint(fishEyeFigure, bounds);
1115
1116
1116
		if (animate) {
1117
		if (animate) {
1117
			Animation.markBegin();
1118
			Animation.run(FISHEYE_ANIMATION_TIME * 2);
1118
		}
1119
		}
1119
1120
		this.getRootLayer().getUpdateManager().performUpdate();
1120
		double scale = rootlayer.getScale();
1121
		fishEyeLayer.removeAll();
1121
		fishEyeLayer.setScale(1 / scale);
1122
		this.fisheyedFigure = null;
1122
1123
1123
		fishEyeLayer.translateToRelative(newBounds);
1124
	}
1124
		fishEyeLayer.translateFromParent(newBounds);
1125
1125
1126
	/**
1126
		Rectangle bounds = startFigure.getBounds().getCopy();
1127
	 * Replaces the old fisheye figure with a new one.
1127
		startFigure.translateToAbsolute(bounds);
1128
	 * @param oldFigure
1128
		// startFigure.translateToRelative(bounds);
1129
	 * @param newFigure
1129
		fishEyeLayer.translateToRelative(bounds);
1130
	 */
1130
		fishEyeLayer.translateFromParent(bounds);
1131
	boolean replaceFishFigure(IFigure oldFigure, IFigure newFigure) {
1131
1132
		if (this.fishEyeLayer.getChildren().contains(oldFigure)) {
1132
		endFigure.setLocation(bounds.getLocation());
1133
			Rectangle bounds = oldFigure.getBounds();
1133
		endFigure.setSize(bounds.getSize());
1134
			newFigure.setBounds(bounds);
1134
		fishEyeLayer.add(endFigure);
1135
			//this.fishEyeLayer.getChildren().remove(oldFigure);
1135
		fishEyeLayer.setConstraint(endFigure, newBounds);
1136
			this.fishEyeLayer.remove(oldFigure);
1136
1137
			this.fishEyeLayer.add(newFigure);
1137
		for (Iterator iterator = fisheyeListeners.iterator(); iterator
1138
			//this.fishEyeLayer.getChildren().add(newFigure);
1138
				.hasNext();) {
1139
			//this.fishEyeLayer.invalidate();
1139
			FisheyeListener listener = (FisheyeListener) iterator.next();
1140
			//this.fishEyeLayer.repaint();
1140
			listener.fisheyeAdded(this, startFigure, endFigure);
1141
			this.fisheyedFigure = newFigure;
1141
		}
1142
			return true;
1142
1143
		}
1143
		if (animate) {
1144
		return false;
1144
			Animation.run(FISHEYE_ANIMATION_TIME);
1145
	}
1145
		}
1146
1146
		this.getRootLayer().getUpdateManager().performUpdate();
1147
	/**
1147
	}
1148
	 * Add a fisheye version of the node.  This works by animating the change from the original node
1148
1149
	 * to the fisheyed one, and then placing the fisheye node on the fisheye layer.
1149
	/**
1150
	 * @param startFigure The original node
1150
	 * Adds a listener that will be notified when fisheyed figures change in
1151
	 * @param endFigure The fisheye figure
1151
	 * this graph.
1152
	 * @param newBounds The final size of the fisheyed figure
1152
	 * 
1153
	 */
1153
	 * @param listener
1154
	void fishEye(IFigure startFigure, IFigure endFigure, Rectangle newBounds, boolean animate) {
1154
	 *            listener to add
1155
1155
	 * @since 2.0
1156
		fishEyeLayer.removeAll();
1156
	 */
1157
		fisheyedFigure = null;
1157
	public void addFisheyeListener(FisheyeListener listener) {
1158
		if (animate) {
1158
		fisheyeListeners.add(listener);
1159
			Animation.markBegin();
1159
	}
1160
		}
1160
1161
1161
	/**
1162
		double scale = rootlayer.getScale();
1162
	 * @since 2.0
1163
		fishEyeLayer.setScale(1 / scale);
1163
	 */
1164
1164
	public void removeFisheyeListener(FisheyeListener listener) {
1165
		fishEyeLayer.translateToRelative(newBounds);
1165
		fisheyeListeners.remove(listener);
1166
		fishEyeLayer.translateFromParent(newBounds);
1166
	}
1167
1167
1168
		Rectangle bounds = startFigure.getBounds().getCopy();
1168
	public int getItemType() {
1169
		startFigure.translateToAbsolute(bounds);
1169
		return GraphItem.GRAPH;
1170
		//startFigure.translateToRelative(bounds);
1170
	}
1171
		fishEyeLayer.translateToRelative(bounds);
1171
1172
		fishEyeLayer.translateFromParent(bounds);
1172
	GraphItem getGraphItem(IFigure figure) {
1173
1173
		return (GraphItem) figure2ItemMap.get(figure);
1174
		endFigure.setLocation(bounds.getLocation());
1174
	}
1175
		endFigure.setSize(bounds.getSize());
1175
1176
		fishEyeLayer.add(endFigure);
1176
	/**
1177
		fishEyeLayer.setConstraint(endFigure, newBounds);
1177
	 * @since 2.0
1178
1178
	 */
1179
		if (animate) {
1179
	public void setExpanded(GraphNode node, boolean expanded) {
1180
			Animation.run(FISHEYE_ANIMATION_TIME);
1180
		layoutContext.setExpanded(node.getLayout(), expanded);
1181
		}
1181
		rootlayer.invalidate();
1182
		this.getRootLayer().getUpdateManager().performUpdate();
1182
	}
1183
	}
1183
1184
1184
	/**
1185
	public Graph getGraph() {
1185
	 * @since 2.0
1186
		// @tag refactor : Is this method really needed
1186
	 */
1187
		return this.getGraphModel();
1187
	public boolean canExpand(GraphNode node) {
1188
	}
1188
		return layoutContext.canExpand(node.getLayout());
1189
1189
	}
1190
	public int getItemType() {
1190
1191
		return GraphItem.GRAPH;
1191
	/**
1192
	}
1192
	 * @since 2.0
1193
1193
	 */
1194
	GraphItem getGraphItem(IFigure figure) {
1194
	public boolean canCollapse(GraphNode node) {
1195
		return (GraphItem) figure2ItemMap.get(figure);
1195
		return layoutContext.canCollapse(node.getLayout());
1196
	}
1196
	}
1197
1197
1198
}
1198
	public Graph getGraph() {
1199
		return this;
1200
	}
1201
1202
	/**
1203
	 * @since 2.0
1204
	 */
1205
	public Widget getItem() {
1206
		return this;
1207
	}
1208
1209
	/**
1210
	 * @since 2.0
1211
	 */
1212
	public DisplayIndependentRectangle getLayoutBounds() {
1213
		Dimension preferredSize = this.getPreferredSize();
1214
		return new DisplayIndependentRectangle(0, 0, preferredSize.width,
1215
				preferredSize.height);
1216
	}
1217
}
(-)src/org/eclipse/zest/core/widgets/GraphConnection.java (-718 / +739 lines)
Lines 1-718 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * All rights reserved. This program and the accompanying materials are made
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria 
9
 ******************************************************************************/
9
 *               Mateusz Matela
10
package org.eclipse.zest.core.widgets;
10
 ******************************************************************************/
11
11
package org.eclipse.zest.core.widgets;
12
import org.eclipse.draw2d.ChopboxAnchor;
12
13
import org.eclipse.draw2d.ColorConstants;
13
import org.eclipse.draw2d.ChopboxAnchor;
14
import org.eclipse.draw2d.Connection;
14
import org.eclipse.draw2d.ColorConstants;
15
import org.eclipse.draw2d.Graphics;
15
import org.eclipse.draw2d.Connection;
16
import org.eclipse.draw2d.IFigure;
16
import org.eclipse.draw2d.Graphics;
17
import org.eclipse.draw2d.Label;
17
import org.eclipse.draw2d.IFigure;
18
import org.eclipse.draw2d.Locator;
18
import org.eclipse.draw2d.Label;
19
import org.eclipse.draw2d.MidpointLocator;
19
import org.eclipse.draw2d.Locator;
20
import org.eclipse.draw2d.PolygonDecoration;
20
import org.eclipse.draw2d.MidpointLocator;
21
import org.eclipse.draw2d.PolylineConnection;
21
import org.eclipse.draw2d.PolygonDecoration;
22
import org.eclipse.draw2d.Shape;
22
import org.eclipse.draw2d.PolylineConnection;
23
import org.eclipse.draw2d.geometry.Point;
23
import org.eclipse.draw2d.Shape;
24
import org.eclipse.swt.graphics.Color;
24
import org.eclipse.draw2d.geometry.Point;
25
import org.eclipse.swt.graphics.Font;
25
import org.eclipse.swt.graphics.Color;
26
import org.eclipse.swt.widgets.Display;
26
import org.eclipse.swt.graphics.Font;
27
import org.eclipse.zest.core.widgets.internal.LoopAnchor;
27
import org.eclipse.swt.widgets.Display;
28
import org.eclipse.zest.core.widgets.internal.PolylineArcConnection;
28
import org.eclipse.zest.core.widgets.internal.LoopAnchor;
29
import org.eclipse.zest.core.widgets.internal.RoundedChopboxAnchor;
29
import org.eclipse.zest.core.widgets.internal.PolylineArcConnection;
30
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
30
import org.eclipse.zest.core.widgets.internal.RoundedChopboxAnchor;
31
import org.eclipse.zest.layouts.LayoutBendPoint;
31
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
32
import org.eclipse.zest.layouts.LayoutEntity;
32
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
33
import org.eclipse.zest.layouts.LayoutRelationship;
33
import org.eclipse.zest.layouts.interfaces.NodeLayout;
34
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
34
35
35
/*
36
/*
36
 * This is the graph connection model which stores the source and destination
37
 * This is the graph connection model which stores the source and destination
37
 * nodes and the properties of this connection (color, line width etc).
38
 * nodes and the properties of this connection (color, line width etc).
38
 * 
39
 * 
39
 * @author Chris Callendar
40
 * @author Chris Callendar
40
 * 
41
 * 
41
 * @author Ian Bull
42
 * @author Ian Bull
42
 */
43
 */
43
public class GraphConnection extends GraphItem {
44
public class GraphConnection extends GraphItem {
44
45
45
	private Font font;
46
	private Font font;
46
	private GraphNode sourceNode;
47
	private GraphNode sourceNode;
47
	private GraphNode destinationNode;
48
	private GraphNode destinationNode;
48
49
49
	private double weight;
50
	private double weight;
50
	private Color color;
51
	private Color color;
51
	private Color highlightColor;
52
	private Color highlightColor;
52
	private Color foreground;
53
	private Color foreground;
53
	private int lineWidth;
54
	private int lineWidth;
54
	private int lineStyle;
55
	private int lineStyle;
55
	private final Graph graph;
56
	private final Graph graphModel;
56
57
57
	private int connectionStyle;
58
	private int connectionStyle;
58
	private int curveDepth;
59
	private int curveDepth;
59
	private boolean isDisposed = false;
60
	private boolean isDisposed = false;
60
61
61
	private Label connectionLabel = null;
62
	private Label connectionLabel = null;
62
	private Connection connectionFigure = null;
63
	private Connection connectionFigure = null;
63
	private Connection sourceContainerConnectionFigure = null;
64
	private Connection sourceContainerConnectionFigure = null;
64
	private Connection targetContainerConnectionFigure = null;
65
	private Connection targetContainerConnectionFigure = null;
65
66
66
	/**
67
	/**
67
	 * The state of visibility set by the user.
68
	 * The state of visibility set by the user.
68
	 */
69
	 */
69
	private boolean visible;
70
	private boolean visible;
70
71
71
	private IFigure tooltip;
72
	private IFigure tooltip;
72
73
73
	private boolean highlighted;
74
	private boolean highlighted;
74
	private boolean hasCustomTooltip;
75
	private GraphLayoutConnection layoutConnection = null;
75
76
	private boolean hasCustomTooltip;
76
	public GraphConnection(Graph graphModel, int style, GraphNode source,
77
77
			GraphNode destination) {
78
	public GraphConnection(Graph graphModel, int style, GraphNode source, GraphNode destination) {
78
		super(graphModel, style);
79
		super(graphModel, style);
79
80
80
		this.connectionStyle |= graphModel.getConnectionStyle();
81
		this.connectionStyle |= graphModel.getConnectionStyle();
81
		this.connectionStyle |= style;
82
		this.connectionStyle |= style;
82
		this.sourceNode = source;
83
		this.sourceNode = source;
83
		this.destinationNode = destination;
84
		this.destinationNode = destination;
84
		this.visible = true;
85
		this.visible = true;
85
		this.color = ColorConstants.lightGray;
86
		this.color = ColorConstants.lightGray;
86
		this.foreground = ColorConstants.lightGray;
87
		this.foreground = ColorConstants.lightGray;
87
		this.highlightColor = graphModel.DARK_BLUE;
88
		this.highlightColor = graphModel.DARK_BLUE;
88
		this.lineWidth = 1;
89
		this.lineWidth = 1;
89
		this.lineStyle = Graphics.LINE_SOLID;
90
		this.lineStyle = Graphics.LINE_SOLID;
90
		setWeight(1.0);
91
		setWeight(weight);
91
		this.graph = graphModel;
92
		this.graphModel = graphModel;
92
		this.curveDepth = 0;
93
		this.curveDepth = 0;
93
		this.font = Display.getDefault().getSystemFont();
94
		this.layoutConnection = new GraphLayoutConnection();
94
		registerConnection(source, destination);
95
		this.font = Display.getDefault().getSystemFont();
95
	}
96
		registerConnection(source, destination);
96
97
	}
97
	private void registerConnection(GraphNode source, GraphNode destination) {
98
98
		if (source.getSourceConnections().contains(this)) {
99
	private void registerConnection(GraphNode source, GraphNode destination) {
99
			source.removeSourceConnection(this);
100
		if (source.getSourceConnections().contains(this)) {
100
		}
101
			source.removeSourceConnection(this);
101
		if (destination.getTargetConnections().contains(this)) {
102
		}
102
			destination.removeTargetConnection(this);
103
		if (destination.getTargetConnections().contains(this)) {
103
		}
104
			destination.removeTargetConnection(this);
104
		(source).addSourceConnection(this);
105
		}
105
		(destination).addTargetConnection(this);
106
		(source).addSourceConnection(this);
106
107
		(destination).addTargetConnection(this);
107
		if (source.getParent().getItemType() == GraphItem.CONTAINER
108
108
				&& destination.getParent().getItemType() == GraphItem.CONTAINER
109
		if (source.getParent().getItemType() == GraphItem.CONTAINER && destination.getParent().getItemType() == GraphItem.CONTAINER && (source.getParent() == destination.getParent())) {
109
				&& (source.getParent() == destination.getParent())) {
110
			// 196189: Edges should not draw on the edge layer if both the src and dest are in the same container
110
			// 196189: Edges should not draw on the edge layer if both the src
111
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=196189
111
			// and dest are in the same container
112
			graphModel.addConnection(this, ZestRootLayer.EDGES_ON_TOP);
112
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=196189
113
		} else {
113
			graph.addConnection(this, false);
114
			graphModel.addConnection(this, true);
114
		} else {
115
		}
115
			graph.addConnection(this, true);
116
116
		}
117
		if ((source.getParent()).getItemType() == GraphItem.CONTAINER) {
117
118
			// If the container of the source is a container, we need to draw another
118
		if ((source.getParent()).getItemType() == GraphItem.CONTAINER) {
119
			// arc on that arc layer
119
			// If the container of the source is a container, we need to draw
120
			sourceContainerConnectionFigure = doCreateFigure();
120
			// another
121
			((GraphContainer) source.getParent()).addConnectionFigure((PolylineConnection) sourceContainerConnectionFigure);
121
			// arc on that arc layer
122
			this.setVisible(false);
122
			sourceContainerConnectionFigure = doCreateFigure();
123
		}
123
			((GraphContainer) source.getParent())
124
124
					.addConnectionFigure(sourceContainerConnectionFigure);
125
		if ((destination.getParent()).getItemType() == GraphItem.CONTAINER) { //&& src_destSameContainer == false) {
125
			this.setVisible(false);
126
			// If the container of the source is a container, we need to draw another
126
		}
127
			// arc on that arc layer
127
128
			targetContainerConnectionFigure = doCreateFigure();
128
		if ((destination.getParent()).getItemType() == GraphItem.CONTAINER) { // &&
129
			((GraphContainer) destination.getParent()).addConnectionFigure((PolylineConnection) targetContainerConnectionFigure);
129
																				// src_destSameContainer
130
			this.setVisible(false);
130
																				// ==
131
		}
131
																				// false)
132
		graphModel.getGraph().registerItem(this);
132
																				// {
133
	}
133
			// If the container of the source is a container, we need to draw
134
134
			// another
135
	void removeFigure() {
135
			// arc on that arc layer
136
		if (connectionFigure.getParent() != null) {
136
			targetContainerConnectionFigure = doCreateFigure();
137
			if (connectionFigure.getParent() instanceof ZestRootLayer) {
137
			((GraphContainer) destination.getParent())
138
				((ZestRootLayer) connectionFigure.getParent()).removeConnection(connectionFigure);
138
					.addConnectionFigure(targetContainerConnectionFigure);
139
			} else {
139
			this.setVisible(false);
140
				connectionFigure.getParent().remove(connectionFigure);
140
		}
141
			}
141
		graph.registerItem(this);
142
		}
142
	}
143
		connectionFigure = null;
143
144
		if (sourceContainerConnectionFigure != null) {
144
	void removeFigure() {
145
			sourceContainerConnectionFigure.getParent().remove(sourceContainerConnectionFigure);
145
		if (connectionFigure.getParent() != null) {
146
			sourceContainerConnectionFigure = null;
146
			connectionFigure.getParent().remove(connectionFigure);
147
		}
147
		}
148
		if (targetContainerConnectionFigure != null) {
148
		connectionFigure = null;
149
			targetContainerConnectionFigure.getParent().remove(targetContainerConnectionFigure);
149
		if (sourceContainerConnectionFigure != null) {
150
			targetContainerConnectionFigure = null;
150
			sourceContainerConnectionFigure.getParent().remove(
151
		}
151
					sourceContainerConnectionFigure);
152
152
			sourceContainerConnectionFigure = null;
153
	}
153
		}
154
154
		if (targetContainerConnectionFigure != null) {
155
	public void dispose() {
155
			targetContainerConnectionFigure.getParent().remove(
156
		super.dispose();
156
					targetContainerConnectionFigure);
157
		this.isDisposed = true;
157
			targetContainerConnectionFigure = null;
158
		(getSource()).removeSourceConnection(this);
158
		}
159
		(getDestination()).removeTargetConnection(this);
159
160
		graphModel.removeConnection(this);
160
	}
161
		if (sourceContainerConnectionFigure != null) {
161
162
			sourceContainerConnectionFigure.getParent().remove(sourceContainerConnectionFigure);
162
	public void dispose() {
163
		}
163
		super.dispose();
164
		if (targetContainerConnectionFigure != null) {
164
		this.isDisposed = true;
165
			targetContainerConnectionFigure.getParent().remove(targetContainerConnectionFigure);
165
		(getSource()).removeSourceConnection(this);
166
		}
166
		(getDestination()).removeTargetConnection(this);
167
	}
167
		graph.removeConnection(this);
168
168
		if (sourceContainerConnectionFigure != null) {
169
	public boolean isDisposed() {
169
			sourceContainerConnectionFigure.getParent().remove(
170
		return isDisposed;
170
					sourceContainerConnectionFigure);
171
	}
171
		}
172
172
		if (targetContainerConnectionFigure != null) {
173
	public Connection getConnectionFigure() {
173
			targetContainerConnectionFigure.getParent().remove(
174
		if (connectionFigure == null) {
174
					targetContainerConnectionFigure);
175
			connectionFigure = createFigure();
175
		}
176
		}
176
	}
177
		return connectionFigure;
177
178
	}
178
	public boolean isDisposed() {
179
179
		return isDisposed;
180
	/**
180
	}
181
	 * Gets a proxy to this connection that can be used with the Zest layout
181
182
	 * engine
182
	public Connection getConnectionFigure() {
183
	 * 
183
		if (connectionFigure == null) {
184
	 * @return
184
			connectionFigure = doCreateFigure();
185
	 */
185
		}
186
	public LayoutRelationship getLayoutRelationship() {
186
		return connectionFigure;
187
		return this.layoutConnection;
187
	}
188
	}
188
189
189
	/**
190
	/**
190
	 * Gets the external connection object.
191
	 * Gets the external connection object.
191
	 * 
192
	 * 
192
	 * @return Object
193
	 * @return Object
193
	 */
194
	 */
194
	public Object getExternalConnection() {
195
	public Object getExternalConnection() {
195
		return this.getData();
196
		return this.getData();
196
	}
197
	}
197
198
198
	/**
199
	/**
199
	 * Returns a string like 'source -> destination'
200
	 * Returns a string like 'source -> destination'
200
	 * 
201
	 * 
201
	 * @return String
202
	 * @return String
202
	 */
203
	 */
203
	public String toString() {
204
	public String toString() {
204
		StringBuffer buffer = new StringBuffer("GraphModelConnection: ");
205
		String arrow = (isBidirectionalInLayout() ? " <--> " : " --> ");
205
		buffer.append(sourceNode != null ? sourceNode.getText() : "null");
206
		String src = (sourceNode != null ? sourceNode.getText() : "null");
206
		buffer.append(isDirected() ? " --> " : " --- ");
207
		String dest = (destinationNode != null ? destinationNode.getText() : "null");
207
		buffer.append(destinationNode != null ? destinationNode.getText()
208
		String weight = "  (weight=" + getWeightInLayout() + ")";
208
				: "null");
209
		return ("GraphModelConnection: " + src + arrow + dest + weight);
209
		buffer.append("  (weight=").append(getWeightInLayout()).append(")");
210
	}
210
		return buffer.toString();
211
211
	}
212
	/**
212
213
	 * Returns the style of this connection. Valid styles are those that begin
213
	/**
214
	 * with CONNECTION in ZestStyles.
214
	 * Returns the style of this connection. Valid styles are those that begin
215
	 * 
215
	 * with CONNECTION in ZestStyles.
216
	 * @return the style of this connection.
216
	 * 
217
	 * @see #ZestStyles
217
	 * @return the style of this connection.
218
	 */
218
	 * @see #ZestStyles
219
	public int getConnectionStyle() {
219
	 */
220
		return connectionStyle;
220
	public int getConnectionStyle() {
221
	}
221
		return connectionStyle;
222
222
	}
223
	/**
223
224
	 * Returns the style of this connection. Valid styles are those that begin
224
	/**
225
	 * with CONNECTION in ZestStyles.
225
	 * Returns the style of this connection. Valid styles are those that begin
226
	 * 
226
	 * with CONNECTION in ZestStyles.
227
	 * @return the style of this connection.
227
	 * 
228
	 * @see #ZestStyles
228
	 * @return the style of this connection.
229
	 */
229
	 * @see #ZestStyles
230
	public void setConnectionStyle(int style) {
230
	 */
231
		this.connectionStyle = style;
231
	public void setConnectionStyle(int style) {
232
		updateFigure(this.connectionFigure);
232
		this.connectionStyle = style;
233
	}
233
		updateFigure(this.connectionFigure);
234
234
	}
235
	/**
235
236
	 * Gets the weight of this connection. The weight must be in {-1, [0-1]}. A
236
	/**
237
	 * weight of -1 means that there is no force/tension between the nodes. A
237
	 * Gets the weight of this connection. The weight must be in {-1, [0-1]}. A
238
	 * weight of 0 results in the maximum spring length being used (farthest
238
	 * weight of -1 means that there is no force/tension between the nodes. A
239
	 * apart). A weight of 1 results in the minimum spring length being used
239
	 * weight of 0 results in the maximum spring length being used (farthest
240
	 * (closest together).
240
	 * apart). A weight of 1 results in the minimum spring length being used
241
	 * 
241
	 * (closest together).
242
	 * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getWeightInLayout()
242
	 * 
243
	 * @return the weight: {-1, [0 - 1]}.
243
	 * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getWeightInLayout()
244
	 */
244
	 * @return the weight: {-1, [0 - 1]}.
245
	public double getWeightInLayout() {
245
	 */
246
		return weight;
246
	public double getWeightInLayout() {
247
	}
247
		return weight;
248
248
	}
249
	/**
249
250
	 * Gets the font for the label on this connection
250
	/**
251
	 * 
251
	 * Gets the font for the label on this connection
252
	 * @return
252
	 * 
253
	 */
253
	 * @return
254
	public Font getFont() {
254
	 */
255
		return this.font;
255
	public Font getFont() {
256
	}
256
		return this.font;
257
257
	}
258
	/**
258
259
	 * Sets the font for the label on this connection.
259
	/**
260
	 * 
260
	 * Sets the font for the label on this connection.
261
	 */
261
	 * 
262
	public void setFont(Font f) {
262
	 */
263
		this.font = f;
263
	public void setFont(Font f) {
264
	}
264
		this.font = f;
265
265
	}
266
	/**
266
267
	 * Sets the weight for this connection. The weight must be in {-1, [0-1]}. A
267
	/**
268
	 * weight of -1 means that there is no force/tension between the nodes. A
268
	 * Sets the weight for this connection. The weight must be in {-1, [0-1]}. A
269
	 * weight of 0 results in the maximum spring length being used (farthest
269
	 * weight of -1 means that there is no force/tension between the nodes. A
270
	 * apart). A weight of 1 results in the minimum spring length being used
270
	 * weight of 0 results in the maximum spring length being used (farthest
271
	 * (closest together).
271
	 * apart). A weight of 1 results in the minimum spring length being used
272
	 * 
272
	 * (closest together).
273
	 */
273
	 * 
274
	public void setWeight(double weight) {
274
	 */
275
		if (weight < 0) {
275
	public void setWeight(double weight) {
276
			this.weight = -1;
276
		if (weight < 0) {
277
		} else if (weight > 1) {
277
			this.weight = -1;
278
			this.weight = 1;
278
		} else if (weight > 1) {
279
		} else {
279
			this.weight = 1;
280
			this.weight = weight;
280
		} else {
281
		}
281
			this.weight = weight;
282
	}
282
		}
283
283
	}
284
	/**
284
285
	 * Returns the color of this connection.
285
	/**
286
	 * 
286
	 * Returns the color of this connection.
287
	 * @return Color
287
	 * 
288
	 */
288
	 * @return Color
289
	public Color getLineColor() {
289
	 */
290
		return color;
290
	public Color getLineColor() {
291
	}
291
		return color;
292
292
	}
293
	/**
293
294
	 * Sets the highlight color.
294
	/**
295
	 * 
295
	 * Sets the highlight color.
296
	 * @param color
296
	 * 
297
	 *            the color to use for highlighting.
297
	 * @param color
298
	 */
298
	 *            the color to use for highlighting.
299
	public void setHighlightColor(Color color) {
299
	 */
300
		this.highlightColor = color;
300
	public void setHighlightColor(Color color) {
301
	}
301
		this.highlightColor = color;
302
302
	}
303
	/**
303
304
	 * @return the highlight color
304
	/**
305
	 */
305
	 * @return the highlight color
306
	public Color getHighlightColor() {
306
	 */
307
		return highlightColor;
307
	public Color getHighlightColor() {
308
	}
308
		return highlightColor;
309
309
	}
310
	/**
310
311
	 * Perminently sets the color of this line to the given color. This will
311
	/**
312
	 * become the color of the line when it is not highlighted. If you would
312
	 * Perminently sets the color of this line to the given color. This will
313
	 * like to temporarily change the color of the line, use changeLineColor.
313
	 * become the color of the line when it is not highlighted. If you would
314
	 * 
314
	 * like to temporarily change the color of the line, use changeLineColor.
315
	 * @param color
315
	 * 
316
	 *            the color to be set.
316
	 * @param color
317
	 * @see changeLineColor(Color color)
317
	 *            the color to be set.
318
	 */
318
	 * @see changeLineColor(Color color)
319
	public void setLineColor(Color color) {
319
	 */
320
		this.foreground = color;
320
	public void setLineColor(Color color) {
321
		changeLineColor(foreground);
321
		this.foreground = color;
322
	}
322
		changeLineColor(foreground);
323
323
	}
324
	/**
324
325
	 * Sets the connection color.
325
	/**
326
	 * 
326
	 * Sets the connection color.
327
	 * @param color
327
	 * 
328
	 */
328
	 * @param color
329
	public void changeLineColor(Color color) {
329
	 */
330
		this.color = color;
330
	public void changeLineColor(Color color) {
331
		updateFigure(connectionFigure);
331
		this.color = color;
332
	}
332
		updateFigure(connectionFigure);
333
333
	}
334
	/**
334
335
	 * Sets the tooltip on this node. This tooltip will display if the mouse
335
	/**
336
	 * hovers over the node. Setting the tooltip has no effect if a custom
336
	 * Sets the tooltip on this node. This tooltip will display if the mouse
337
	 * figure has been set.
337
	 * hovers over the node. Setting the tooltip has no effect if a custom
338
	 */
338
	 * figure has been set.
339
	public void setTooltip(IFigure tooltip) {
339
	 */
340
		hasCustomTooltip = true;
340
	public void setTooltip(IFigure tooltip) {
341
		this.tooltip = tooltip;
341
		hasCustomTooltip = true;
342
		updateFigure(connectionFigure);
342
		this.tooltip = tooltip;
343
	}
343
		updateFigure(connectionFigure);
344
344
	}
345
	/**
345
346
	 * Gets the current tooltip for this node. The tooltip returned is
346
	/**
347
	 * meaningless if a custom figure has been set.
347
	 * Gets the current tooltip for this node. The tooltip returned is
348
	 */
348
	 * meaningless if a custom figure has been set.
349
	public IFigure getTooltip() {
349
	 */
350
		return this.tooltip;
350
	public IFigure getTooltip() {
351
	}
351
		return this.tooltip;
352
352
	}
353
	/**
353
354
	 * Returns the connection line width.
354
	/**
355
	 * 
355
	 * Returns the connection line width.
356
	 * @return int
356
	 * 
357
	 */
357
	 * @return int
358
	public int getLineWidth() {
358
	 */
359
		return lineWidth;
359
	public int getLineWidth() {
360
	}
360
		return lineWidth;
361
361
	}
362
	/**
362
363
	 * Sets the connection line width.
363
	/**
364
	 * 
364
	 * Sets the connection line width.
365
	 * @param lineWidth
365
	 * 
366
	 */
366
	 * @param lineWidth
367
	public void setLineWidth(int lineWidth) {
367
	 */
368
		this.lineWidth = lineWidth;
368
	public void setLineWidth(int lineWidth) {
369
		updateFigure(connectionFigure);
369
		this.lineWidth = lineWidth;
370
	}
370
		updateFigure(connectionFigure);
371
371
	}
372
	/**
372
373
	 * Returns the connection line style.
373
	/**
374
	 * 
374
	 * Returns the connection line style.
375
	 * @return int
375
	 * 
376
	 */
376
	 * @return int
377
	public int getLineStyle() {
377
	 */
378
		return lineStyle;
378
	public int getLineStyle() {
379
	}
379
		return lineStyle;
380
380
	}
381
	/**
381
382
	 * Sets the connection line style.
382
	/**
383
	 * 
383
	 * Sets the connection line style.
384
	 * @param lineStyle
384
	 * 
385
	 */
385
	 * @param lineStyle
386
	public void setLineStyle(int lineStyle) {
386
	 */
387
		this.lineStyle = lineStyle;
387
	public void setLineStyle(int lineStyle) {
388
		updateFigure(connectionFigure);
388
		this.lineStyle = lineStyle;
389
	}
389
		updateFigure(connectionFigure);
390
390
	}
391
	/**
391
392
	 * Gets the source node for this relationship
392
	/**
393
	 * 
393
	 * Gets the source node for this relationship
394
	 * @return GraphModelNode
394
	 * 
395
	 */
395
	 * @return GraphModelNode
396
	public GraphNode getSource() {
396
	 */
397
		return this.sourceNode;
397
	public GraphNode getSource() {
398
	}
398
		return this.sourceNode;
399
399
	}
400
	/**
400
401
	 * Gets the target node for this relationship
401
	/**
402
	 * 
402
	 * Gets the target node for this relationship
403
	 * @return GraphModelNode
403
	 * 
404
	 */
404
	 * @return GraphModelNode
405
	public GraphNode getDestination() {
405
	 */
406
		return this.destinationNode;
406
	public GraphNode getDestination() {
407
	}
407
		return this.destinationNode;
408
408
	}
409
	/**
409
410
	 * Highlights this node. Uses the default highlight color.
410
	/**
411
	 */
411
	 * Highlights this node. Uses the default highlight color.
412
	public void highlight() {
412
	 */
413
		if (highlighted) {
413
	public void highlight() {
414
			return;
414
		if (highlighted) {
415
		}
415
			return;
416
		highlighted = true;
416
		}
417
		updateFigure(connectionFigure);
417
		IFigure parentFigure = connectionFigure.getParent();
418
		graphModel.highlightEdge(this);
418
		if (parentFigure instanceof ZestRootLayer) {
419
	}
419
			((ZestRootLayer) parentFigure)
420
420
					.highlightConnection(connectionFigure);
421
	/**
421
		}
422
	 * Unhighlights this node. Uses the default color.
422
		highlighted = true;
423
	 */
423
		updateFigure(connectionFigure);
424
	public void unhighlight() {
424
	}
425
		if (!highlighted) {
425
426
			return;
426
	/**
427
		}
427
	 * Unhighlights this node. Uses the default color.
428
		highlighted = false;
428
	 */
429
		updateFigure(connectionFigure);
429
	public void unhighlight() {
430
		graphModel.unhighlightEdge(this);
430
		if (!highlighted) {
431
	}
431
			return;
432
432
		}
433
	/**
433
		IFigure parentFigure = connectionFigure.getParent();
434
	 * Returns true if this connection is highlighted, false otherwise
434
		if (parentFigure instanceof ZestRootLayer) {
435
	 * 
435
			((ZestRootLayer) parentFigure)
436
	 * @return
436
					.unHighlightConnection(connectionFigure);
437
	 */
437
		}
438
	public boolean isHighlighted() {
438
		highlighted = false;
439
		return highlighted;
439
		updateFigure(connectionFigure);
440
	}
440
	}
441
441
442
	/**
442
	/**
443
	 * Gets the graph model that this connection is in
443
	 * Returns true if this connection is highlighted, false otherwise
444
	 * 
444
	 * 
445
	 * @return The graph model that this connection is contained in
445
	 * @return
446
	 */
446
	 */
447
	public Graph getGraphModel() {
447
	public boolean isHighlighted() {
448
		return this.graphModel;
448
		return highlighted;
449
	}
449
	}
450
450
451
	/**
451
	/**
452
	 * Sets the curve depth of the arc.  The curve depth is defined as 
452
	 * Gets the graph model that this connection is in
453
	 * the maximum distance from any point on the chord (i.e. a vector
453
	 * 
454
	 * normal to the chord with magnitude d).
454
	 * @return The graph model that this connection is contained in
455
	 * 
455
	 */
456
	 * If 0 is set, a Polyline Connection will be used, otherwise a 
456
	public Graph getGraphModel() {
457
	 * PolylineArcConnectoin will be used.  Negative depths are also supported.
457
		return this.graph;
458
	 * @param depth The depth of the curve
458
	}
459
	 */
459
460
	public void setCurveDepth(int depth) {
460
	/**
461
		if (this.curveDepth == 0 && depth != 0 || this.curveDepth != 0 && depth == 0) {
461
	 * Sets the curve depth of the arc. The curve depth is defined as the
462
			// There is currently no curve, so we have to create
462
	 * maximum distance from any point on the chord (i.e. a vector normal to the
463
			// a curved connection
463
	 * chord with magnitude d).
464
			graphModel.removeConnection(this);
464
	 * 
465
			this.curveDepth = depth;
465
	 * If 0 is set, a Polyline Connection will be used, otherwise a
466
			this.connectionFigure = createFigure();
466
	 * PolylineArcConnectoin will be used. Negative depths are also supported.
467
			registerConnection(sourceNode, destinationNode);
467
	 * 
468
			updateFigure(this.connectionFigure);
468
	 * @param depth
469
		} else {
469
	 *            The depth of the curve
470
			this.curveDepth = depth;
470
	 */
471
			updateFigure(this.connectionFigure);
471
	public void setCurveDepth(int depth) {
472
		}
472
		if (this.curveDepth == 0 && depth != 0 || this.curveDepth != 0
473
	}
473
				&& depth == 0) {
474
474
			// There is currently no curve, so we have to create
475
	/*
475
			// a curved connection
476
	 * (non-Javadoc)
476
			graph.removeConnection(this);
477
	 * 
477
			this.curveDepth = depth;
478
	 * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType()
478
			this.connectionFigure = doCreateFigure();
479
	 */
479
			registerConnection(sourceNode, destinationNode);
480
	public int getItemType() {
480
			updateFigure(this.connectionFigure);
481
		return CONNECTION;
481
		} else {
482
	}
482
			this.curveDepth = depth;
483
483
			updateFigure(this.connectionFigure);
484
	/*
484
		}
485
	 * (non-Javadoc)
485
	}
486
	 * 
486
487
	 * @see org.eclipse.mylar.zest.core.internal.graphmodel.GraphItem#setVisible(boolean)
487
	/*
488
	 */
488
	 * (non-Javadoc)
489
	public void setVisible(boolean visible) {
489
	 * 
490
		//graphModel.addRemoveFigure(this, visible);
490
	 * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType()
491
		if (getSource().isVisible() && getDestination().isVisible() && visible) {
491
	 */
492
			this.getFigure().setVisible(visible);
492
	public int getItemType() {
493
			if (sourceContainerConnectionFigure != null) {
493
		return CONNECTION;
494
				sourceContainerConnectionFigure.setVisible(visible);
494
	}
495
			}
495
496
			if (targetContainerConnectionFigure != null) {
496
	/*
497
				targetContainerConnectionFigure.setVisible(visible);
497
	 * (non-Javadoc)
498
			}
498
	 * 
499
			this.visible = visible;
499
	 * @see
500
		} else {
500
	 * org.eclipse.mylar.zest.core.internal.graphmodel.GraphItem#setVisible(
501
			this.getFigure().setVisible(false);
501
	 * boolean)
502
			if (sourceContainerConnectionFigure != null) {
502
	 */
503
				sourceContainerConnectionFigure.setVisible(false);
503
	public void setVisible(boolean visible) {
504
			}
504
		if (getSource().isVisible() && getDestination().isVisible() && visible) {
505
			if (targetContainerConnectionFigure != null) {
505
			this.getFigure().setVisible(visible);
506
				targetContainerConnectionFigure.setVisible(false);
506
			if (sourceContainerConnectionFigure != null) {
507
			}
507
				sourceContainerConnectionFigure.setVisible(visible);
508
			this.visible = false;
508
			}
509
		}
509
			if (targetContainerConnectionFigure != null) {
510
510
				targetContainerConnectionFigure.setVisible(visible);
511
	}
511
			}
512
512
			this.visible = visible;
513
	/*
513
		} else {
514
	 * (non-Javadoc)
514
			this.getFigure().setVisible(false);
515
	 * 
515
			if (sourceContainerConnectionFigure != null) {
516
	 * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#isVisible()
516
				sourceContainerConnectionFigure.setVisible(false);
517
	 */
517
			}
518
	public boolean isVisible() {
518
			if (targetContainerConnectionFigure != null) {
519
		return visible;
519
				targetContainerConnectionFigure.setVisible(false);
520
	}
520
			}
521
521
			this.visible = false;
522
	/*
522
		}
523
	 * (non-Javadoc)
523
524
	 * 
524
	}
525
	 * @see org.eclipse.swt.widgets.Item#setText(java.lang.String)
525
526
	 */
526
	/*
527
	public void setText(String string) {
527
	 * (non-Javadoc)
528
		super.setText(string);
528
	 * 
529
529
	 * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#isVisible()
530
		if (this.connectionFigure != null) {
530
	 */
531
			updateFigure(this.connectionFigure);
531
	public boolean isVisible() {
532
		}
532
		return visible;
533
	}
533
	}
534
534
535
	PolylineConnection getSourceContainerConnectionFigure() {
535
	/*
536
		return (PolylineConnection) sourceContainerConnectionFigure;
536
	 * (non-Javadoc)
537
	}
537
	 * 
538
538
	 * @see org.eclipse.swt.widgets.Item#setText(java.lang.String)
539
	PolylineConnection getTargetContainerConnectionFigure() {
539
	 */
540
		return (PolylineConnection) targetContainerConnectionFigure;
540
	public void setText(String string) {
541
	}
541
		super.setText(string);
542
542
543
	private void updateFigure(Connection connection) {
543
		if (this.connectionFigure != null) {
544
		if (sourceContainerConnectionFigure != null) {
544
			updateFigure(this.connectionFigure);
545
			doUpdateFigure(sourceContainerConnectionFigure);
545
		}
546
		}
546
	}
547
		if (targetContainerConnectionFigure != null) {
547
548
			doUpdateFigure(targetContainerConnectionFigure);
548
	/**
549
		}
549
	 * @since 2.0
550
		doUpdateFigure(connection);
550
	 */
551
	}
551
	public boolean isDirected() {
552
552
		return ZestStyles.checkStyle(connectionStyle,
553
	private void doUpdateFigure(Connection connection) {
553
				ZestStyles.CONNECTIONS_DIRECTED);
554
		if (connection == null) {
554
	}
555
			return;
555
556
		}
556
	/**
557
		Shape connectionShape = (Shape) connection;
557
	 * @since 2.0
558
558
	 */
559
		connectionShape.setLineStyle(getLineStyle());
559
	public void setDirected(boolean directed) {
560
560
		if (directed) {
561
		if (this.getText() != null || this.getImage() != null) {
561
			setConnectionStyle(connectionStyle
562
			//Label l = new Label(this.getText(), this.getImage());
562
					| ZestStyles.CONNECTIONS_DIRECTED);
563
			if (this.getImage() != null) {
563
		} else {
564
				this.connectionLabel.setIcon(this.getImage());
564
			setConnectionStyle(connectionStyle
565
			}
565
					& (-1 - ZestStyles.CONNECTIONS_DIRECTED));
566
			if (this.getText() != null) {
566
		}
567
				this.connectionLabel.setText(this.getText());
567
	}
568
			}
568
569
			this.connectionLabel.setFont(this.getFont());
569
	PolylineConnection getSourceContainerConnectionFigure() {
570
		}
570
		return (PolylineConnection) sourceContainerConnectionFigure;
571
571
	}
572
		if (highlighted) {
572
573
			connectionShape.setForegroundColor(getHighlightColor());
573
	PolylineConnection getTargetContainerConnectionFigure() {
574
			connectionShape.setLineWidth(getLineWidth() * 2);
574
		return (PolylineConnection) targetContainerConnectionFigure;
575
		} else {
575
	}
576
			connectionShape.setForegroundColor(getLineColor());
576
577
			connectionShape.setLineWidth(getLineWidth());
577
	private void updateFigure(Connection connection) {
578
		}
578
		if (sourceContainerConnectionFigure != null) {
579
579
			doUpdateFigure(sourceContainerConnectionFigure);
580
		if (connection instanceof PolylineArcConnection) {
580
		}
581
			PolylineArcConnection arcConnection = (PolylineArcConnection) connection;
581
		if (targetContainerConnectionFigure != null) {
582
			arcConnection.setDepth(curveDepth);
582
			doUpdateFigure(targetContainerConnectionFigure);
583
		}
583
		}
584
		if ((connectionStyle & ZestStyles.CONNECTIONS_DIRECTED) > 0) {
584
		doUpdateFigure(connection);
585
			PolygonDecoration decoration = new PolygonDecoration();
585
	}
586
			if (getLineWidth() < 3) {
586
587
				decoration.setScale(9, 3);
587
	private void doUpdateFigure(Connection connection) {
588
			} else {
588
		if (connection == null) {
589
				double logLineWith = getLineWidth() / 2.0;
589
			return;
590
				decoration.setScale(7 * logLineWith, 3 * logLineWith);
590
		}
591
			}
591
		Shape connectionShape = (Shape) connection;
592
			((PolylineConnection) connection).setTargetDecoration(decoration);
592
593
		}
593
		connectionShape.setLineStyle(getLineStyle());
594
594
595
		IFigure toolTip;
595
		if (this.getText() != null || this.getImage() != null) {
596
		if (this.getTooltip() == null && getText() != null && getText().length() > 0 && hasCustomTooltip == false) {
596
			if (this.getImage() != null) {
597
			toolTip = new Label();
597
				this.connectionLabel.setIcon(this.getImage());
598
			((Label) toolTip).setText(getText());
598
			}
599
		} else {
599
			if (this.getText() != null) {
600
			toolTip = this.getTooltip();
600
				this.connectionLabel.setText(this.getText());
601
		}
601
			}
602
		connection.setToolTip(toolTip);
602
			this.connectionLabel.setFont(this.getFont());
603
	}
603
		}
604
604
605
	private Connection createFigure() {
605
		if (highlighted) {
606
		/*
606
			connectionShape.setForegroundColor(getHighlightColor());
607
		if ((sourceNode.getParent()).getItemType() == GraphItem.CONTAINER) {
607
			connectionShape.setLineWidth(getLineWidth() * 2);
608
			GraphContainer container = (GraphContainer) sourceNode.getParent();
608
		} else {
609
			sourceContainerConnectionFigure = doCreateFigure();
609
			connectionShape.setForegroundColor(getLineColor());
610
			container.addConnectionFigure((PolylineConnection) sourceContainerConnectionFigure);
610
			connectionShape.setLineWidth(getLineWidth());
611
		}
611
		}
612
		if ((destinationNode.getParent()).getItemType() == GraphItem.CONTAINER) {
612
613
			GraphContainer container = (GraphContainer) destinationNode.getParent();
613
		if (connection instanceof PolylineArcConnection) {
614
			targetContainerConnectionFigure = doCreateFigure();
614
			PolylineArcConnection arcConnection = (PolylineArcConnection) connection;
615
			container.addConnectionFigure((PolylineConnection) targetContainerConnectionFigure);
615
			arcConnection.setDepth(curveDepth);
616
		}
616
		}
617
		*/
617
		if ((connectionStyle & ZestStyles.CONNECTIONS_DIRECTED) > 0) {
618
618
			PolygonDecoration decoration = new PolygonDecoration();
619
		return doCreateFigure();
619
			if (getLineWidth() < 3) {
620
620
				decoration.setScale(9, 3);
621
	}
621
			} else {
622
622
				double logLineWith = getLineWidth() / 2.0;
623
	private Connection doCreateFigure() {
623
				decoration.setScale(7 * logLineWith, 3 * logLineWith);
624
		Connection connectionFigure = null;
624
			}
625
		ChopboxAnchor sourceAnchor = null;
625
			((PolylineConnection) connection).setTargetDecoration(decoration);
626
		ChopboxAnchor targetAnchor = null;
626
		}
627
		this.connectionLabel = new Label();
627
628
		Locator labelLocator = null;
628
		IFigure toolTip;
629
629
		if (this.getTooltip() == null && getText() != null
630
		if (getSource() == getDestination()) {
630
				&& getText().length() > 0 && hasCustomTooltip == false) {
631
			// If this is a self loop, create a looped arc and put the locator at the top
631
			toolTip = new Label();
632
			// of the connection
632
			((Label) toolTip).setText(getText());
633
			connectionFigure = new PolylineArcConnection();
633
		} else {
634
			sourceAnchor = new LoopAnchor(getSource().getNodeFigure());
634
			toolTip = this.getTooltip();
635
			targetAnchor = new LoopAnchor(getDestination().getNodeFigure());
635
		}
636
			labelLocator = new MidpointLocator(connectionFigure, 0) {
636
		connection.setToolTip(toolTip);
637
				protected Point getReferencePoint() {
637
	}
638
					Point p = Point.SINGLETON;
638
639
					p.x = getConnection().getPoints().getPoint(getIndex()).x;
639
	private Connection doCreateFigure() {
640
					p.y = (int) (getConnection().getPoints().getPoint(getIndex()).y - (curveDepth * 1.5));
640
		Connection connectionFigure = null;
641
					return p;
641
		ChopboxAnchor sourceAnchor = null;
642
				}
642
		ChopboxAnchor targetAnchor = null;
643
			};
643
		this.connectionLabel = new Label();
644
		} else {
644
		Locator labelLocator = null;
645
			if (curveDepth != 0) {
645
646
				connectionFigure = new PolylineArcConnection();
646
		if (getSource() == getDestination()) {
647
				((PolylineArcConnection) connectionFigure).setDepth(this.curveDepth);
647
			// If this is a self loop, create a looped arc and put the locator
648
			} else {
648
			// at the top
649
				connectionFigure = new PolylineConnection();
649
			// of the connection
650
			}
650
			connectionFigure = new PolylineArcConnection();
651
			sourceAnchor = new RoundedChopboxAnchor(getSource().getNodeFigure(), 8);
651
			sourceAnchor = new LoopAnchor(getSource().getNodeFigure());
652
			targetAnchor = new RoundedChopboxAnchor(getDestination().getNodeFigure(), 8);
652
			targetAnchor = new LoopAnchor(getDestination().getNodeFigure());
653
			labelLocator = new MidpointLocator(connectionFigure, 0);
653
			labelLocator = new MidpointLocator(connectionFigure, 0) {
654
		}
654
				protected Point getReferencePoint() {
655
655
					Point p = Point.SINGLETON;
656
		connectionFigure.setSourceAnchor(sourceAnchor);
656
					p.x = getConnection().getPoints().getPoint(getIndex()).x;
657
		connectionFigure.setTargetAnchor(targetAnchor);
657
					p.y = (int) (getConnection().getPoints().getPoint(
658
		connectionFigure.add(this.connectionLabel, labelLocator);
658
							getIndex()).y - (curveDepth * 1.5));
659
659
					return p;
660
		doUpdateFigure(connectionFigure);
660
				}
661
		return connectionFigure;
661
			};
662
	}
662
		} else {
663
663
			if (curveDepth != 0) {
664
	/*
664
				connectionFigure = new PolylineArcConnection();
665
	 * (non-Javadoc)
665
				((PolylineArcConnection) connectionFigure)
666
	 * 
666
						.setDepth(this.curveDepth);
667
	 * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#isBidirectionalInLayout()
667
			} else {
668
	 */
668
				connectionFigure = new PolylineConnection();
669
	private boolean isBidirectionalInLayout() {
669
			}
670
		return !ZestStyles.checkStyle(connectionStyle, ZestStyles.CONNECTIONS_DIRECTED);
670
			sourceAnchor = new RoundedChopboxAnchor(
671
	}
671
					getSource().getNodeFigure(), 8);
672
672
			targetAnchor = new RoundedChopboxAnchor(getDestination()
673
	class GraphLayoutConnection implements LayoutRelationship {
673
					.getNodeFigure(), 8);
674
674
			labelLocator = new MidpointLocator(connectionFigure, 0);
675
		Object layoutInformation = null;
675
		}
676
676
677
		public void clearBendPoints() {
677
		connectionFigure.setSourceAnchor(sourceAnchor);
678
			// @tag TODO : add bendpoints
678
		connectionFigure.setTargetAnchor(targetAnchor);
679
		}
679
		connectionFigure.add(this.connectionLabel, labelLocator);
680
680
681
		public LayoutEntity getDestinationInLayout() {
681
		doUpdateFigure(connectionFigure);
682
			return getDestination().getLayoutEntity();
682
		return connectionFigure;
683
		}
683
	}
684
684
685
		public Object getLayoutInformation() {
685
	IFigure getFigure() {
686
			return layoutInformation;
686
		return this.getConnectionFigure();
687
		}
687
	}
688
688
689
		public LayoutEntity getSourceInLayout() {
689
	private InternalConnectionLayout layout;
690
			return getSource().getLayoutEntity();
690
691
		}
691
	InternalConnectionLayout getLayout() {
692
692
		if (layout == null) {
693
		public void populateLayoutConstraint(LayoutConstraint constraint) {
693
			layout = new InternalConnectionLayout();
694
			graphModel.invokeConstraintAdapters(GraphConnection.this, constraint);
694
		}
695
		}
695
		return layout;
696
696
	}
697
		public void setBendPoints(LayoutBendPoint[] bendPoints) {
697
698
			// @tag TODO : add bendpoints
698
	class InternalConnectionLayout implements ConnectionLayout {
699
		}
699
		private boolean visible = GraphConnection.this.isVisible();
700
700
701
		public void setLayoutInformation(Object layoutInformation) {
701
		public NodeLayout getSource() {
702
			this.layoutInformation = layoutInformation;
702
			return sourceNode.getLayout();
703
		}
703
		}
704
704
705
		public Object getGraphData() {
705
		public NodeLayout getTarget() {
706
			return GraphConnection.this;
706
			return destinationNode.getLayout();
707
		}
707
		}
708
708
709
		public void setGraphData(Object o) {
709
		public double getWeight() {
710
710
			return GraphConnection.this.getWeightInLayout();
711
		}
711
		}
712
712
713
	}
713
		public boolean isDirected() {
714
714
			return !ZestStyles.checkStyle(getConnectionStyle(),
715
	IFigure getFigure() {
715
					ZestStyles.CONNECTIONS_DIRECTED);
716
		return this.getConnectionFigure();
716
		}
717
	}
717
718
}
718
		public boolean isVisible() {
719
			return visible;
720
		}
721
722
		public void setVisible(boolean visible) {
723
			graph.getLayoutContext().checkChangesAllowed();
724
			this.visible = visible;
725
		}
726
727
		void applyLayout() {
728
			if (GraphConnection.this.isVisible() != this.visible) {
729
				GraphConnection.this.setVisible(this.visible);
730
			}
731
		}
732
	}
733
734
	void applyLayoutChanges() {
735
		if (layout != null) {
736
			layout.applyLayout();
737
		}
738
	}
739
}
(-)src/org/eclipse/zest/core/widgets/GraphContainer.java (-835 / +947 lines)
Lines 1-835 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria 
9
 ******************************************************************************/
9
 *               Mateusz Matela
10
package org.eclipse.zest.core.widgets;
10
 ******************************************************************************/
11
11
package org.eclipse.zest.core.widgets;
12
import java.util.ArrayList;
12
13
import java.util.Iterator;
13
import java.util.ArrayList;
14
import java.util.LinkedList;
14
import java.util.Iterator;
15
import java.util.List;
15
import java.util.LinkedList;
16
16
import java.util.List;
17
import org.eclipse.draw2d.Animation;
17
18
import org.eclipse.draw2d.ColorConstants;
18
import org.eclipse.draw2d.ActionEvent;
19
import org.eclipse.draw2d.FreeformLayout;
19
import org.eclipse.draw2d.ActionListener;
20
import org.eclipse.draw2d.FreeformViewport;
20
import org.eclipse.draw2d.Animation;
21
import org.eclipse.draw2d.IFigure;
21
import org.eclipse.draw2d.Clickable;
22
import org.eclipse.draw2d.LayoutAnimator;
22
import org.eclipse.draw2d.ColorConstants;
23
import org.eclipse.draw2d.LineBorder;
23
import org.eclipse.draw2d.Figure;
24
import org.eclipse.draw2d.PolylineConnection;
24
import org.eclipse.draw2d.FreeformLayout;
25
import org.eclipse.draw2d.ScrollPane;
25
import org.eclipse.draw2d.FreeformViewport;
26
import org.eclipse.draw2d.Viewport;
26
import org.eclipse.draw2d.Graphics;
27
import org.eclipse.draw2d.geometry.Dimension;
27
import org.eclipse.draw2d.IFigure;
28
import org.eclipse.draw2d.geometry.Point;
28
import org.eclipse.draw2d.Label;
29
import org.eclipse.draw2d.geometry.Rectangle;
29
import org.eclipse.draw2d.LayoutAnimator;
30
import org.eclipse.swt.graphics.Image;
30
import org.eclipse.draw2d.LineBorder;
31
import org.eclipse.zest.core.widgets.internal.AspectRatioFreeformLayer;
31
import org.eclipse.draw2d.ScrollPane;
32
import org.eclipse.zest.core.widgets.internal.ContainerFigure;
32
import org.eclipse.draw2d.ToolbarLayout;
33
import org.eclipse.zest.core.widgets.internal.ExpandGraphLabel;
33
import org.eclipse.draw2d.Triangle;
34
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
34
import org.eclipse.draw2d.Viewport;
35
import org.eclipse.zest.layouts.InvalidLayoutConfiguration;
35
import org.eclipse.draw2d.geometry.Dimension;
36
import org.eclipse.zest.layouts.LayoutAlgorithm;
36
import org.eclipse.draw2d.geometry.Point;
37
import org.eclipse.zest.layouts.LayoutEntity;
37
import org.eclipse.draw2d.geometry.Rectangle;
38
import org.eclipse.zest.layouts.LayoutRelationship;
38
import org.eclipse.swt.events.SelectionEvent;
39
import org.eclipse.zest.layouts.LayoutStyles;
39
import org.eclipse.swt.events.SelectionListener;
40
import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
40
import org.eclipse.swt.graphics.Color;
41
41
import org.eclipse.swt.graphics.Image;
42
/*
42
import org.eclipse.swt.graphics.RGB;
43
 * A Container than can be added to a Graph. Nodes can be added to this
43
import org.eclipse.swt.widgets.Display;
44
 * container. The container supports collapsing and expanding and has the same
44
import org.eclipse.swt.widgets.Widget;
45
 * properties as the nodes. Containers cannot have custom figures.
45
import org.eclipse.zest.core.widgets.internal.AspectRatioFreeformLayer;
46
 * 
46
import org.eclipse.zest.core.widgets.internal.ContainerFigure;
47
 * @author Ian Bull
47
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
48
 */
48
import org.eclipse.zest.layouts.LayoutAlgorithm;
49
public class GraphContainer extends GraphNode implements IContainer {
49
import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
50
50
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
51
	//private static final double CONTAINER_SCALE = 0.75;
51
52
	private static final double scaledWidth = 300;
52
/**
53
	private static final double scaledHeight = 200;
53
 * A Container that can be added to a Graph. Nodes can be added to this
54
	private static final int CONTAINER_HEIGHT = 200;
54
 * container. The container supports collapsing and expanding and has the same
55
	private static final int MIN_WIDTH = 250;
55
 * properties as the nodes. Containers cannot have custom figures.
56
	private static final int ANIMATION_TIME = 100;
56
 * 
57
	private static final int SUBLAYER_OFFSET = 2;
57
 * @author Ian Bull
58
58
 */
59
	private ExpandGraphLabel expandGraphLabel;
59
public class GraphContainer extends GraphNode implements IContainer {
60
60
61
	//private FreeformLayer container;
61
	static class ExpandGraphLabel extends Figure implements ActionListener {
62
	//private FreeformLayer edgeLayer;
62
63
	private List childNodes = null;
63
		private boolean isExpanded;
64
	private int childAreaHeight = CONTAINER_HEIGHT;
64
		private Expander expander = new Expander();
65
65
		private Color darkerBackground;
66
	public ZestRootLayer zestLayer;
66
67
	private ScrollPane scrollPane;
67
		class Expander extends Clickable {
68
	private LayoutAlgorithm layoutAlgorithm;
68
			private Triangle triangle;
69
	private boolean isExpanded = false;
69
70
	//private ScalableFreeformLayeredPane scalledLayer;
70
			public Expander() {
71
	private AspectRatioFreeformLayer scalledLayer;
71
				setStyle(Clickable.STYLE_TOGGLE);
72
72
				triangle = new Triangle();
73
	/**
73
				triangle.setSize(10, 10);
74
	 * Creates a new GraphContainer.  A GraphContainer may contain nodes,
74
				triangle.setBackgroundColor(ColorConstants.black);
75
	 * and has many of the same properties as a graph node.
75
				triangle.setForegroundColor(ColorConstants.black);
76
	 * @param graph The graph that the container is being added to
76
				triangle.setFill(true);
77
	 * @param style 
77
				triangle.setDirection(Triangle.EAST);
78
	 */
78
				triangle.setLocation(new Point(5, 3));
79
	public GraphContainer(IContainer graph, int style) {
79
				this.setLayoutManager(new FreeformLayout());
80
		this(graph, style, "");
80
				this.add(triangle);
81
81
				this.setPreferredSize(15, 15);
82
	}
82
				this.addActionListener(ExpandGraphLabel.this);
83
83
			}
84
	public GraphContainer(IContainer graph, int style, String text) {
84
85
		this(graph, style, text, null);
85
			public void open() {
86
86
				triangle.setDirection(Triangle.SOUTH);
87
	}
87
			}
88
88
89
	public GraphContainer(IContainer graph, int style, String text, Image image) {
89
			public void close() {
90
		super(graph, style, text, image);
90
				triangle.setDirection(Triangle.EAST);
91
		initModel(graph, text, image);
91
			}
92
		close(false);
92
93
		childNodes = new ArrayList();
93
		}
94
	}
94
95
95
		/**
96
	/**
96
		 * Sets the expander state (the little triangle) to
97
	 * Custom figures cannot be set on a GraphContainer.
97
		 * ExpanderGraphLabel.OPEN or ExpanderGraphLabel.CLOSED
98
	 */
98
		 * 
99
	public void setCustomFigure(IFigure nodeFigure) {
99
		 * @param state
100
		throw new RuntimeException("Operation not supported:  Containers cannot have custom figures");
100
		 */
101
	}
101
		public void setExpandedState(boolean expanded) {
102
102
			if (expanded) {
103
	/*
103
				expander.open();
104
	 * (non-Javadoc)
104
			} else {
105
	 * @see org.eclipse.mylar.zest.core.widgets.GraphItem#getItemType()
105
				expander.close();
106
	public int getItemType() {
106
			}
107
		return GraphItem.CONTAINER;
107
			this.isExpanded = expanded;
108
	}
108
		}
109
109
110
	/**
110
		/*
111
	 * Gets the figure for this container.
111
		 * (non-Javadoc)
112
	 */
112
		 * 
113
	public IFigure getNodeFigure() {
113
		 * @see
114
		return this.nodeFigure;
114
		 * org.eclipse.draw2d.ActionListener#actionPerformed(org.eclipse.draw2d
115
	}
115
		 * .ActionEvent)
116
116
		 */
117
	/**
117
		public void actionPerformed(ActionEvent event) {
118
	 * Close this node.
118
			if (isExpanded) {
119
	 * @param animate
119
				container.close(true);
120
	 */
120
			} else {
121
	public void close(boolean animate) {
121
				container.open(true);
122
		if (animate) {
122
			}
123
			Animation.markBegin();
123
		}
124
		}
124
125
		isExpanded = false;
125
		private final int arcWidth = 8;
126
126
		private final Label label;
127
		expandGraphLabel.setExpandedState(ExpandGraphLabel.CLOSED);
127
		private final GraphContainer container;
128
		Rectangle newBounds = scrollPane.getBounds().getCopy();
128
		private final ToolbarLayout layout;
129
		newBounds.height = 0;
129
130
130
		public ExpandGraphLabel(GraphContainer container, String text,
131
		//this.nodeFigure.setConstraint(scrollPane, newBounds);
131
				Image image, boolean cacheLabel) {
132
		//this.nodeFigure.revalidate();
132
			this.label = new Label(text) {
133
		scrollPane.setSize(scrollPane.getSize().width, 0);
133
134
		updateFigureForModel(this.zestLayer);
134
				/**
135
		scrollPane.setVisible(false);
135
				 * <b>This method is overwritten so that the text is not
136
		//setSize(expandGraphLabel.getSize().width, expandGraphLabel.getSize().height);
136
				 * truncated.</b><br>
137
		List children = this.zestLayer.getChildren();
137
				 * 
138
		for (Iterator iterator = children.iterator(); iterator.hasNext();) {
138
				 * {@inheritDoc}
139
			IFigure child = (IFigure) iterator.next();
139
				 * 
140
			GraphItem item = getGraph().getGraphItem(child);
140
				 */
141
			item.setVisible(false);
141
				protected void paintFigure(Graphics graphics) {
142
		}
142
					if (isOpaque()) {
143
		Rectangle containerBounds = new Rectangle(this.getLocation(), new Dimension(this.getSize().width, CONTAINER_HEIGHT + this.expandGraphLabel.getSize().height));
143
						super.paintFigure(graphics);
144
		moveNodesUp(containerBounds, this);
144
					}
145
		if (animate) {
145
					Rectangle bounds = getBounds();
146
			Animation.run(ANIMATION_TIME);
146
					graphics.translate(bounds.x, bounds.y);
147
		}
147
					if (getIcon() != null) {
148
		//this.nodeFigure.getUpdateManager().performUpdate();
148
						graphics.drawImage(getIcon(), getIconLocation());
149
		updateFigureForModel(nodeFigure);
149
					}
150
	}
150
					if (!isEnabled()) {
151
151
						graphics.translate(1, 1);
152
	private static void addNodeToOrderedList(List orderedNodeList, GraphNode node) {
152
						graphics.setForegroundColor(ColorConstants.buttonLightest);
153
		Iterator orderedNodeIterator = orderedNodeList.iterator();
153
						graphics.drawText(getSubStringText(), getTextLocation());
154
		int counter = 0;
154
						graphics.translate(-1, -1);
155
		while (orderedNodeIterator.hasNext()) {
155
						graphics.setForegroundColor(ColorConstants.buttonDarker);
156
			// Look through the list of nodes below and find the right spot for this
156
					}
157
			GraphNode nextOrderedNode = (GraphNode) orderedNodeIterator.next();
157
					graphics.drawText(getText(), getTextLocation());
158
			if (nextOrderedNode.getLocation().y + nextOrderedNode.getBounds().height > node.getLocation().y + node.getBounds().height) {
158
					graphics.translate(-bounds.x, -bounds.y);
159
				break;
159
				}
160
			}
160
			};
161
			counter++;
161
			this.setText(text);
162
		}
162
			this.setImage(image);
163
		// Place this in the right location
163
			this.container = container;
164
		orderedNodeList.add(counter, node);
164
			this.setFont(Display.getDefault().getSystemFont());
165
	}
165
			layout = new ToolbarLayout(true);
166
166
			layout.setSpacing(5);
167
	/**
167
			layout.setMinorAlignment(ToolbarLayout.ALIGN_CENTER);
168
	 * Gets all the nodes below the yValue.  The nodes are returned in order.
168
			this.setLayoutManager(layout);
169
	 * @param nodes
169
			this.add(this.expander);
170
	 * @param yValue
170
			this.add(this.label);
171
	 * @return
171
		}
172
	 */
172
173
	private static List getOrderedNodesBelowY(List nodes, int yValue, GraphNode yValueNode) {
173
		private Color getDarkerBackgroundColor() {
174
		Iterator iterator = nodes.iterator();
174
			if (darkerBackground == null) {
175
		LinkedList orderedNode = new LinkedList();
175
				Color baseColor = getBackgroundColor();
176
		while (iterator.hasNext()) {
176
				int blue = (int) (baseColor.getBlue() * 0.8 + 0.5);
177
			GraphNode node = (GraphNode) iterator.next();
177
				int red = (int) (baseColor.getRed() * 0.8 + 0.5);
178
			if (node == yValueNode) {
178
				int green = (int) (baseColor.getGreen() * 0.8 + 0.5);
179
				continue;
179
				darkerBackground = new Color(Display.getCurrent(), new RGB(red,
180
			}
180
						green, blue));
181
			if (node.getLocation().y + node.getBounds().height > yValue) {
181
			}
182
				// This node is below the container
182
			return darkerBackground;
183
				addNodeToOrderedList(orderedNode, node);
183
		}
184
			}
184
185
		}
185
		/*
186
		// Convert this to an arrayList for faster access
186
		 * (non-Javadoc)
187
		List arrayList = new ArrayList();
187
		 * 
188
		iterator = orderedNode.iterator();
188
		 * @see
189
		while (iterator.hasNext()) {
189
		 * org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
190
			arrayList.add(iterator.next());
190
		 */
191
		}
191
		public void paint(Graphics graphics) {
192
		return arrayList;
192
193
	}
193
			graphics.setForegroundColor(getDarkerBackgroundColor());
194
194
			graphics.setBackgroundColor(getBackgroundColor());
195
	/**
195
196
	 * Checks if the node intersects the stripe between left and right
196
			graphics.pushState();
197
	 * @param left
197
198
	 * @param right
198
			// fill in the background
199
	 * @param node
199
			Rectangle bounds = getBounds().getCopy();
200
	 * @return
200
			Rectangle r = bounds.getCopy();
201
	 */
201
			r.y += arcWidth / 2;
202
	private static boolean nodeInStripe(int left, int right, GraphNode node) {
202
			r.height -= arcWidth;
203
		return (node.getBounds().x < right && node.getBounds().x + node.getBounds().width > left);
203
204
	}
204
			Rectangle top = bounds.getCopy();
205
205
			top.height /= 2;
206
	void pack(Graph g) {
206
			graphics.setForegroundColor(getBackgroundColor());
207
		GraphNode highestNode = getHighestNode(g);
207
			graphics.setBackgroundColor(getBackgroundColor());
208
		moveNodesUp(highestNode.getBounds(), highestNode);
208
			graphics.fillRoundRectangle(top, arcWidth, arcWidth);
209
	}
209
210
210
			top.y = top.y + top.height;
211
	/**
211
			graphics.setForegroundColor(darkerBackground);
212
	 * 
212
			graphics.setBackgroundColor(darkerBackground);
213
	 * @param g
213
			graphics.fillRoundRectangle(top, arcWidth, arcWidth);
214
	 * @return
214
215
	 */
215
			graphics.setBackgroundColor(darkerBackground);
216
	static GraphNode getHighestNode(Graph g) {
216
			graphics.setForegroundColor(getBackgroundColor());
217
		Iterator iterator = g.getNodes().iterator();
217
			graphics.fillGradient(r, true);
218
		GraphNode lowest /* highest on the screen */= null;
218
219
219
			super.paint(graphics);
220
		while (iterator.hasNext()) {
220
			graphics.popState();
221
			GraphNode node = (GraphNode) iterator.next();
221
			graphics.setForegroundColor(darkerBackground);
222
			if (lowest == null || lowest.getBounds().y > node.getBounds().y) {
222
			graphics.setBackgroundColor(darkerBackground);
223
				lowest = node;
223
			// paint the border
224
			}
224
			bounds.setSize(bounds.width - 1, bounds.height - 1);
225
		}
225
			graphics.drawRoundRectangle(bounds, arcWidth, arcWidth);
226
		return lowest;
226
		}
227
227
228
	}
228
		public void setBackgroundColor(Color bg) {
229
229
			super.setBackgroundColor(bg);
230
	/**
230
			if (darkerBackground != null) {
231
	 * Move the nodes below this node up
231
				darkerBackground.dispose();
232
	 * @param containerBounds
232
			}
233
	 * @param graphContainer
233
			darkerBackground = null;
234
	 */
234
		}
235
	private void moveNodesUp(Rectangle containerBounds, GraphNode graphContainer) {
235
236
236
		public void setTextT(String string) {
237
		// Get all nodes below this container, in order
237
			this.setPreferredSize(null);
238
		List orderedNodesBelowY = getOrderedNodesBelowY(parent.getNodes(), containerBounds.y, graphContainer);
238
			this.label.setText(string);
239
		int leftSide = containerBounds.x;
239
			this.add(label);
240
		int rightSide = containerBounds.x + containerBounds.width;
240
			this.layout.layout(this);
241
		List nodesToConsider = new LinkedList();
241
			this.invalidate();
242
		for (int i = 0; i < orderedNodesBelowY.size(); i++) {
242
			this.revalidate();
243
			nodesToConsider.add(orderedNodesBelowY.get(i));
243
			this.validate();
244
		}
244
		}
245
		addNodeToOrderedList(orderedNodesBelowY, graphContainer);
245
246
246
		public void setText(String string) {
247
		while (nodesToConsider.size() > 0) {
247
			this.label.setText(string);
248
			GraphNode node = (GraphNode) nodesToConsider.get(0);
248
		}
249
			if (nodeInStripe(leftSide, rightSide, node)) {
249
250
				leftSide = Math.min(leftSide, node.getBounds().x);
250
		public void setImage(Image image) {
251
				rightSide = Math.max(rightSide, node.getBounds().x + node.getBounds().width);
251
			this.label.setIcon(image);
252
				// If this node is in the stripe, move it up 
252
		}
253
				// the previous node
253
254
				GraphNode previousNode = null;
254
		public void setFocus() {
255
				int i = 0;
255
			expander.requestFocus();
256
				for (; i < orderedNodesBelowY.size(); i++) {
256
		}
257
					if (orderedNodesBelowY.get(i) == node) {
257
258
						break;
258
	}
259
					}
259
260
				}
260
	static final double SCALED_WIDTH = 300;
261
				int j = i - 1;
261
	static final double SCALED_HEIGHT = 200;
262
				while (j >= 0) {
262
	private static final int CONTAINER_HEIGHT = 200;
263
					GraphNode pastNode = (GraphNode) orderedNodesBelowY.get(j);
263
	private static final int MIN_WIDTH = 250;
264
					//if (nodeInStripe(leftSide, rightSide, pastNode)) {
264
	private static final int MIN_HEIGHT = 30;
265
					if (nodeInStripe(node.getBounds().x, node.getBounds().x + node.getBounds().width, pastNode)) {
265
	private static final int ANIMATION_TIME = 100;
266
						previousNode = pastNode;
266
	private static final int SUBLAYER_OFFSET = 2;
267
						break;
267
268
					}
268
	private static SelectionListener selectionListener;
269
					j--;
269
270
				}
270
	private ExpandGraphLabel expandGraphLabel;
271
				if (previousNode == null) {
271
272
					previousNode = graphContainer;
272
	private List childNodes = null;
273
				}
273
	private int childAreaHeight = CONTAINER_HEIGHT;
274
				int previousLocation = previousNode.getBounds().y + previousNode.getBounds().height + 2;
274
275
275
	private ZestRootLayer zestLayer;
276
				orderedNodesBelowY.remove(i);
276
	private ScrollPane scrollPane;
277
				node.setLocation(node.getLocation().x, previousLocation);
277
	private LayoutAlgorithm layoutAlgorithm;
278
				addNodeToOrderedList(orderedNodesBelowY, node);
278
	private boolean isExpanded = false;
279
279
	private AspectRatioFreeformLayer scalledLayer;
280
			}
280
	private InternalLayoutContext layoutContext;
281
			nodesToConsider.remove(node);
281
282
		}
282
	/**
283
	}
283
	 * Creates a new GraphContainer. A GraphContainer may contain nodes, and has
284
284
	 * many of the same properties as a graph node.
285
	/**
285
	 * 
286
	 * Open the container.  This opens the graph container to 
286
	 * @param graph
287
	 * show the nodes within and update the twistie
287
	 *            The graph that the container is being added to
288
	 */
288
	 * @param style
289
	public void open(boolean animate) {
289
	 * 
290
		if (animate) {
290
	 * @since 2.0
291
			Animation.markBegin();
291
	 */
292
		}
292
	public GraphContainer(Graph graph, int style) {
293
		isExpanded = true;
293
		super(graph, style, "");
294
294
		initModel(graph, "", null);
295
		expandGraphLabel.setExpandedState(ExpandGraphLabel.OPEN);
295
		close(false);
296
296
		childNodes = new ArrayList();
297
		scrollPane.setSize(computeChildArea());
297
		registerToParent(graph);
298
		scrollPane.setVisible(true);
298
	}
299
		//setSize(expandGraphLabel.getSize().width, expandGraphLabel.getSize().height + expandedHeight - SUBLAYER_OFFSET);
299
300
300
	/**
301
		List children = this.zestLayer.getChildren();
301
	 * Custom figures cannot be set on a GraphContainer.
302
		for (Iterator iterator = children.iterator(); iterator.hasNext();) {
302
	 */
303
			IFigure child = (IFigure) iterator.next();
303
	public void setCustomFigure(IFigure nodeFigure) {
304
			GraphItem item = getGraph().getGraphItem(child);
304
		throw new RuntimeException(
305
			item.setVisible(true);
305
				"Operation not supported:  Containers cannot have custom figures");
306
		}
306
	}
307
307
308
		updateFigureForModel(nodeFigure);
308
	/**
309
309
	 * Close this node.
310
		Rectangle containerBounds = new Rectangle(this.getLocation(), new Dimension(this.getSize().width, CONTAINER_HEIGHT + this.expandGraphLabel.getSize().height));
310
	 * 
311
		//moveIntersectedNodes(containerBounds, this);
311
	 * @param animate
312
		moveNodesDown(containerBounds, this);
312
	 */
313
		moveNodesUp(containerBounds, this);
313
	public void close(boolean animate) {
314
		//pack(graph);
314
		if (animate) {
315
		if (animate) {
315
			Animation.markBegin();
316
			Animation.run(ANIMATION_TIME);
316
		}
317
		}
317
		isExpanded = false;
318
		this.getFigure().getUpdateManager().performValidation();
318
319
		//this.nodeFigure.getUpdateManager().performUpdate();
319
		expandGraphLabel.setExpandedState(false);
320
320
		Rectangle newBounds = scrollPane.getBounds().getCopy();
321
	}
321
		newBounds.height = 0;
322
322
323
	/**
323
		scrollPane.setSize(scrollPane.getSize().width, 0);
324
	 * 
324
		updateFigureForModel(this.zestLayer);
325
	 * @param containerBounds
325
		scrollPane.setVisible(false);
326
	 * @param graphContainer
326
		List children = this.zestLayer.getChildren();
327
	 */
327
		for (Iterator iterator = children.iterator(); iterator.hasNext();) {
328
	private void moveNodesDown(Rectangle containerBounds, GraphContainer graphContainer) {
328
			IFigure child = (IFigure) iterator.next();
329
329
			GraphItem item = getGraph().getGraphItem(child);
330
		// Find all nodes below here
330
			item.setVisible(false);
331
		List nodesBelowHere = getOrderedNodesBelowY(parent.getNodes(), containerBounds.y, graphContainer);
331
		}
332
		Iterator nodesBelowHereIterator = nodesBelowHere.iterator();
332
		Rectangle containerBounds = new Rectangle(this.getLocation(),
333
		List nodesToMove = new LinkedList();
333
				new Dimension(this.getSize().width, CONTAINER_HEIGHT
334
		int left = containerBounds.x;
334
						+ this.expandGraphLabel.getSize().height));
335
		int right = containerBounds.x + containerBounds.width;
335
		moveNodesUp(containerBounds, this);
336
		while (nodesBelowHereIterator.hasNext()) {
336
		if (animate) {
337
			GraphNode node = (GraphNode) nodesBelowHereIterator.next();
337
			Animation.run(ANIMATION_TIME);
338
			if (nodeInStripe(left, right, node)) {
338
		}
339
				nodesToMove.add(node);
339
		updateFigureForModel(nodeFigure);
340
				left = Math.min(left, node.getBounds().x);
340
	}
341
				right = Math.max(right, node.getBounds().x + node.getBounds().width);
341
342
			}
342
	private static void addNodeToOrderedList(List orderedNodeList,
343
		}
343
			GraphNode node) {
344
		List intersectingNodes = intersectingNodes(containerBounds, nodesToMove, graphContainer);
344
		Iterator orderedNodeIterator = orderedNodeList.iterator();
345
		int delta = getMaxMovement(containerBounds, intersectingNodes);
345
		int counter = 0;
346
		if (delta > 0) {
346
		while (orderedNodeIterator.hasNext()) {
347
			shiftNodesDown(nodesToMove, delta);
347
			// Look through the list of nodes below and find the right spot for
348
		}
348
			// this
349
349
			GraphNode nextOrderedNode = (GraphNode) orderedNodeIterator.next();
350
	}
350
			if (nextOrderedNode.getLocation().y
351
351
					+ nextOrderedNode.getBounds().height > node.getLocation().y
352
	void highlightNode(GraphNode node) {
352
					+ node.getBounds().height) {
353
353
				break;
354
	}
354
			}
355
355
			counter++;
356
	void highlightEdge(GraphConnection connection) {
356
		}
357
	}
357
		// Place this in the right location
358
358
		orderedNodeList.add(counter, node);
359
	void highlightNode(GraphContainer container) {
359
	}
360
360
361
	}
361
	/**
362
362
	 * Gets all the nodes below the yValue. The nodes are returned in order.
363
	void unhighlightNode(GraphNode node) {
363
	 * 
364
364
	 * @param nodes
365
	}
365
	 * @param yValue
366
366
	 * @return
367
	void unhighlightNode(GraphContainer container) {
367
	 */
368
368
	private static List getOrderedNodesBelowY(List nodes, int yValue,
369
	}
369
			GraphNode yValueNode) {
370
370
		Iterator iterator = nodes.iterator();
371
//	/**
371
		LinkedList orderedNode = new LinkedList();
372
//	 * Gets a list of nodes below the given node
372
		while (iterator.hasNext()) {
373
//	 * @param node
373
			GraphNode node = (GraphNode) iterator.next();
374
//	 * @return
374
			if (node == yValueNode) {
375
//	 */
375
				continue;
376
//	private List getNodesBelow(int y, List nodes) {
376
			}
377
//		Iterator allNodes = nodes.iterator();
377
			if (node.getLocation().y + node.getBounds().height > yValue) {
378
//		LinkedList result = new LinkedList();
378
				// This node is below the container
379
//		while (allNodes.hasNext()) {
379
				addNodeToOrderedList(orderedNode, node);
380
//			GraphNode nextNode = (GraphNode) allNodes.next();
380
			}
381
//			int top = nextNode.getLocation().y;
381
		}
382
//			if (top > y) {
382
		// Convert this to an arrayList for faster access
383
//				result.add(nextNode);
383
		List arrayList = new ArrayList();
384
//			}
384
		iterator = orderedNode.iterator();
385
//		}
385
		while (iterator.hasNext()) {
386
//		return result;
386
			arrayList.add(iterator.next());
387
//	}
387
		}
388
388
		return arrayList;
389
	/**
389
	}
390
	 * Checks all the nodes in the list of nodesToCheck to see if they intersect with the bounds set
390
391
	 * @param node
391
	/**
392
	 * @param nodesToCheck
392
	 * Checks if the node intersects the stripe between left and right
393
	 * @return
393
	 * 
394
	 */
394
	 * @param left
395
	private List intersectingNodes(Rectangle bounds, List nodesToCheck, GraphNode node) {
395
	 * @param right
396
		List result = new LinkedList();
396
	 * @param node
397
		Iterator nodes = nodesToCheck.iterator();
397
	 * @return
398
		while (nodes.hasNext()) {
398
	 */
399
			GraphNode nodeToCheck = (GraphNode) nodes.next();
399
	private static boolean nodeInStripe(int left, int right, GraphNode node) {
400
			if (node == nodeToCheck) {
400
		return (node.getBounds().x < right && node.getBounds().x
401
				continue;
401
				+ node.getBounds().width > left);
402
			}
402
	}
403
			if (bounds.intersects(nodeToCheck.getBounds())) {
403
404
				result.add(nodeToCheck);
404
	void pack(Graph g) {
405
			}
405
		GraphNode highestNode = getHighestNode(g);
406
		}
406
		moveNodesUp(highestNode.getBounds(), highestNode);
407
		return result;
407
	}
408
	}
408
409
409
	/**
410
	/**
410
	 * 
411
	 * Gets the max distance the intersecting nodes need to be shifted to make room for the expanding node
411
	 * @param g
412
	 * @param bounds
412
	 * @return
413
	 * @param nodesToMove
413
	 */
414
	 * @return
414
	static GraphNode getHighestNode(Graph g) {
415
	 */
415
		Iterator iterator = g.getNodes().iterator();
416
	private int getMaxMovement(Rectangle bounds, List nodesToMove) {
416
		GraphNode lowest /* highest on the screen */= null;
417
		Iterator iterator = nodesToMove.iterator();
417
418
		int maxMovement = 0;
418
		while (iterator.hasNext()) {
419
		while (iterator.hasNext()) {
419
			GraphNode node = (GraphNode) iterator.next();
420
			GraphNode node = (GraphNode) iterator.next();
420
			if (lowest == null || lowest.getBounds().y > node.getBounds().y) {
421
			int yValue = node.getLocation().y;
421
				lowest = node;
422
			int distanceFromBottom = (bounds.y + bounds.height) - yValue;
422
			}
423
			maxMovement = Math.max(maxMovement, distanceFromBottom);
423
		}
424
		}
424
		return lowest;
425
		return maxMovement + 3;
425
426
	}
426
	}
427
427
428
	/**
428
	/**
429
	 * Shifts a collection of nodes down.
429
	 * Move the nodes below this node up
430
	 * @param nodesToShift
430
	 * 
431
	 * @param amount
431
	 * @param containerBounds
432
	 */
432
	 * @param graphContainer
433
	private void shiftNodesDown(List nodesToShift, int amount) {
433
	 */
434
		Iterator iterator = nodesToShift.iterator();
434
	private void moveNodesUp(Rectangle containerBounds, GraphNode graphContainer) {
435
		while (iterator.hasNext()) {
435
436
			GraphNode node = (GraphNode) iterator.next();
436
		// Get all nodes below this container, in order
437
437
		List orderedNodesBelowY = getOrderedNodesBelowY(parent.getGraph()
438
			node.setLocation(node.getLocation().x, node.getLocation().y + amount);
438
				.getNodes(), containerBounds.y, graphContainer);
439
		}
439
		int leftSide = containerBounds.x;
440
	}
440
		int rightSide = containerBounds.x + containerBounds.width;
441
441
		List nodesToConsider = new LinkedList();
442
//	/**
442
		for (int i = 0; i < orderedNodesBelowY.size(); i++) {
443
//	 * This finds the highest Y Value of a set of nodes.
443
			nodesToConsider.add(orderedNodesBelowY.get(i));
444
//	 * @param nodes
444
		}
445
//	 * @return
445
		addNodeToOrderedList(orderedNodesBelowY, graphContainer);
446
//	 */
446
447
//	private int findSmallestYValue(List nodes) {
447
		while (nodesToConsider.size() > 0) {
448
//		Iterator iterator = nodes.iterator();
448
			GraphNode node = (GraphNode) nodesToConsider.get(0);
449
//		int lowestNode /*highest on the screen*/= Integer.MAX_VALUE - 100; // Subtract 100 so we don't overflow
449
			if (nodeInStripe(leftSide, rightSide, node)) {
450
//		while (iterator.hasNext()) {
450
				leftSide = Math.min(leftSide, node.getBounds().x);
451
//			GraphNode node = (GraphNode) iterator.next();
451
				rightSide = Math.max(rightSide, node.getBounds().x
452
//			int y = node.getLocation().y;
452
						+ node.getBounds().width);
453
//			lowestNode = Math.min(lowestNode, y);
453
				// If this node is in the stripe, move it up
454
//		}
454
				// the previous node
455
//		return lowestNode;
455
				GraphNode previousNode = null;
456
//	}
456
				int i = 0;
457
457
				for (; i < orderedNodesBelowY.size(); i++) {
458
//	/**
458
					if (orderedNodesBelowY.get(i) == node) {
459
//	 * Clears the nodes that the container intersects as it expands
459
						break;
460
//	 * @param containerBounds
460
					}
461
//	 * @param graphContainer
461
				}
462
//	 */
462
				int j = i - 1;
463
//	private void moveIntersectedNodes(Rectangle containerBounds, GraphNode graphContainer) {
463
				while (j >= 0) {
464
//
464
					GraphNode pastNode = (GraphNode) orderedNodesBelowY.get(j);
465
//		List nodesBelowHere = getNodesBelow(this.getLocation().y, graphContainer.getGraphModel().getNodes());
465
					// if (nodeInStripe(leftSide, rightSide, pastNode)) {
466
//		List intersectingNodes = intersectingNodes(containerBounds, nodesBelowHere, graphContainer);
466
					if (nodeInStripe(node.getBounds().x, node.getBounds().x
467
//		int delta = getMaxMovement(containerBounds, intersectingNodes);
467
							+ node.getBounds().width, pastNode)) {
468
//		shiftNodesDown(intersectingNodes, delta);
468
						previousNode = pastNode;
469
//
469
						break;
470
//		int lowestNode /*highest on the screen*/= findSmallestYValue(intersectingNodes);
470
					}
471
//		nodesBelowHere = getNodesBelow(lowestNode, nodesBelowHere);
471
					j--;
472
//
472
				}
473
//		while (nodesBelowHere.size() > 0) {
473
				if (previousNode == null) {
474
//			Iterator intersectingNodeIterator = intersectingNodes.iterator();
474
					previousNode = graphContainer;
475
//			List nodesMovedInLastIteration = new LinkedList();
475
				}
476
//			while (intersectingNodeIterator.hasNext()) {
476
				int previousLocation = previousNode.getBounds().y
477
//				GraphNode node = (GraphNode) intersectingNodeIterator.next();
477
						+ previousNode.getBounds().height + 2;
478
//				intersectingNodes = intersectingNodes(node.getBounds(), nodesBelowHere, node);
478
479
//				delta = getMaxMovement(node.getBounds(), intersectingNodes);
479
				orderedNodesBelowY.remove(i);
480
//				if (delta > 0) {
480
				node.setLocation(node.getLocation().x, previousLocation);
481
//					shiftNodesDown(intersectingNodes, delta);
481
				addNodeToOrderedList(orderedNodesBelowY, node);
482
//					nodesMovedInLastIteration.addAll(intersectingNodes);
482
483
//				}
483
			}
484
//			}
484
			nodesToConsider.remove(node);
485
//			lowestNode /*highest on the screen*/= findSmallestYValue(nodesMovedInLastIteration);
485
		}
486
//			nodesBelowHere = getNodesBelow(lowestNode, nodesBelowHere);
486
	}
487
//			intersectingNodes = nodesMovedInLastIteration;
487
488
//		}
488
	/**
489
//	}
489
	 * Open the container. This opens the graph container to show the nodes
490
490
	 * within and update the twistie
491
	/**
491
	 */
492
	 * Gets the graph that this container has been added to.
492
	public void open(boolean animate) {
493
	 */
493
		if (animate) {
494
	public Graph getGraph() {
494
			Animation.markBegin();
495
		return this.graph.getGraph();
495
		}
496
	}
496
		isExpanded = true;
497
497
498
	public int getItemType() {
498
		expandGraphLabel.setExpandedState(true);
499
		return CONTAINER;
499
500
	}
500
		scrollPane.setSize(computeChildArea());
501
501
		scrollPane.setVisible(true);
502
	/**
502
503
	 * 
503
		List children = this.zestLayer.getChildren();
504
	 */
504
		for (Iterator iterator = children.iterator(); iterator.hasNext();) {
505
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean applyLayout) {
505
			IFigure child = (IFigure) iterator.next();
506
		this.layoutAlgorithm = algorithm;
506
			GraphItem item = getGraph().getGraphItem(child);
507
		if (applyLayout) {
507
			item.setVisible(true);
508
			applyLayout();
508
		}
509
		}
509
510
510
		updateFigureForModel(nodeFigure);
511
	}
511
512
512
		Rectangle containerBounds = new Rectangle(this.getLocation(),
513
	public void applyLayout() {
513
				new Dimension(this.getSize().width, CONTAINER_HEIGHT
514
		if ((this.getNodes().size() == 0)) {
514
						+ this.expandGraphLabel.getSize().height));
515
			return;
515
		moveNodesDown(containerBounds, this);
516
		}
516
		moveNodesUp(containerBounds, this);
517
517
		if (animate) {
518
		int layoutStyle = 0;
518
			Animation.run(ANIMATION_TIME);
519
519
		}
520
		if (checkStyle(ZestStyles.NODES_NO_LAYOUT_RESIZE)) {
520
		this.getFigure().getUpdateManager().performValidation();
521
			layoutStyle = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
521
	}
522
		}
522
523
523
	/**
524
		if (layoutAlgorithm == null) {
524
	 * 
525
			layoutAlgorithm = new TreeLayoutAlgorithm(layoutStyle);
525
	 * @param containerBounds
526
		}
526
	 * @param graphContainer
527
527
	 */
528
		layoutAlgorithm.setStyle(layoutAlgorithm.getStyle() | layoutStyle);
528
	private void moveNodesDown(Rectangle containerBounds,
529
529
			GraphContainer graphContainer) {
530
		// calculate the size for the layout algorithm
530
531
		//Dimension d = this.scalledLayer.getSize();
531
		// Find all nodes below here
532
		Dimension d = new Dimension();
532
		List nodesBelowHere = getOrderedNodesBelowY(parent.getGraph()
533
		d.width = (int) scaledWidth;
533
				.getNodes(), containerBounds.y, graphContainer);
534
		d.height = (int) scaledHeight;
534
		Iterator nodesBelowHereIterator = nodesBelowHere.iterator();
535
535
		List nodesToMove = new LinkedList();
536
		d.width = d.width - 10;
536
		int left = containerBounds.x;
537
		d.height = d.height - 10;
537
		int right = containerBounds.x + containerBounds.width;
538
		//if (d.height <= 0) {
538
		while (nodesBelowHereIterator.hasNext()) {
539
		//d.height = (CONTAINER_HEIGHT);
539
			GraphNode node = (GraphNode) nodesBelowHereIterator.next();
540
		//}
540
			if (nodeInStripe(left, right, node)) {
541
		//d.scale(1 / this.scalledLayer.getScale());
541
				nodesToMove.add(node);
542
542
				left = Math.min(left, node.getBounds().x);
543
		if (d.isEmpty()) {
543
				right = Math.max(right, node.getBounds().x
544
			return;
544
						+ node.getBounds().width);
545
		}
545
			}
546
		LayoutRelationship[] connectionsToLayout = getGraph().getConnectionsToLayout(getNodes());
546
		}
547
		LayoutEntity[] nodesToLayout = getGraph().getNodesToLayout(getNodes());
547
		List intersectingNodes = intersectingNodes(containerBounds,
548
548
				nodesToMove, graphContainer);
549
		try {
549
		int delta = getMaxMovement(containerBounds, intersectingNodes);
550
			Animation.markBegin();
550
		if (delta > 0) {
551
			layoutAlgorithm.applyLayout(nodesToLayout, connectionsToLayout, 25, 25, d.width - 50, d.height - 50, false, false);
551
			shiftNodesDown(nodesToMove, delta);
552
			Animation.run(ANIMATION_TIME);
552
		}
553
			getFigure().getUpdateManager().performUpdate();
553
554
554
	}
555
		} catch (InvalidLayoutConfiguration e) {
555
556
			e.printStackTrace();
556
	/**
557
		}
557
	 * Checks all the nodes in the list of nodesToCheck to see if they intersect
558
558
	 * with the bounds set
559
	}
559
	 * 
560
560
	 * @param node
561
	/**
561
	 * @param nodesToCheck
562
	 * Get the scale for this container. This is the scale applied to the children contained within
562
	 * @return
563
	 * @return
563
	 */
564
	 */
564
	private List intersectingNodes(Rectangle bounds, List nodesToCheck,
565
	public double getScale() {
565
			GraphNode node) {
566
		return this.scalledLayer.getScale();
566
		List result = new LinkedList();
567
	}
567
		Iterator nodes = nodesToCheck.iterator();
568
568
		while (nodes.hasNext()) {
569
	/**
569
			GraphNode nodeToCheck = (GraphNode) nodes.next();
570
	 * Set the scale for this container. This is the scale applied to the children contained within.
570
			if (node == nodeToCheck) {
571
	 * @param scale
571
				continue;
572
	 */
572
			}
573
	public void setScale(double scale) {
573
			if (bounds.intersects(nodeToCheck.getBounds())) {
574
		this.scalledLayer.setScale(scale);
574
				result.add(nodeToCheck);
575
	}
575
			}
576
576
		}
577
	/***************************************************************************
577
		return result;
578
	 * NON API MEMBERS
578
	}
579
	 **************************************************************************/
579
580
	protected void initFigure() {
580
	/**
581
		nodeFigure = createContainerFigure();
581
	 * Gets the max distance the intersecting nodes need to be shifted to make
582
	}
582
	 * room for the expanding node
583
583
	 * 
584
	/**
584
	 * @param bounds
585
	 * This is a small class to help represent the size of the container.  It should only be
585
	 * @param nodesToMove
586
	 * used in the computeContainerSize method.
586
	 * @return
587
	 */
587
	 */
588
	class ContainerDimension {
588
	private int getMaxMovement(Rectangle bounds, List nodesToMove) {
589
		int width;
589
		Iterator iterator = nodesToMove.iterator();
590
		int labelHeight;
590
		int maxMovement = 0;
591
		int expandedHeight;
591
		while (iterator.hasNext()) {
592
	}
592
			GraphNode node = (GraphNode) iterator.next();
593
593
			int yValue = node.getLocation().y;
594
	/**
594
			int distanceFromBottom = (bounds.y + bounds.height) - yValue;
595
	 * Computes size of the scroll pane that the child nodes will be placed in.
595
			maxMovement = Math.max(maxMovement, distanceFromBottom);
596
	 * @return
596
		}
597
	 */
597
		return maxMovement + 3;
598
	private Dimension computeChildArea() {
598
	}
599
		ContainerDimension containerDimension = computeContainerSize();
599
600
		Dimension dimension = new Dimension();
600
	/**
601
		dimension.width = containerDimension.width;
601
	 * Shifts a collection of nodes down.
602
		dimension.height = containerDimension.expandedHeight - containerDimension.labelHeight + SUBLAYER_OFFSET;
602
	 * 
603
		return dimension;
603
	 * @param nodesToShift
604
	}
604
	 * @param amount
605
605
	 */
606
	/**
606
	private void shiftNodesDown(List nodesToShift, int amount) {
607
	 * Computes the desired size of the container.  This method uses the 
607
		Iterator iterator = nodesToShift.iterator();
608
	 * minimum size, label size and setSize to compute the size.
608
		while (iterator.hasNext()) {
609
	 * @return
609
			GraphNode node = (GraphNode) iterator.next();
610
	 */
610
611
	private ContainerDimension computeContainerSize() {
611
			node.setLocation(node.getLocation().x, node.getLocation().y
612
		ContainerDimension dimension = new ContainerDimension();
612
					+ amount);
613
		int labelHeight = expandGraphLabel.getPreferredSize().height;
613
		}
614
		int labelWidth = expandGraphLabel.getPreferredSize().width;
614
	}
615
		if (labelWidth < MIN_WIDTH) {
615
616
			labelWidth = MIN_WIDTH;
616
	/**
617
			expandGraphLabel.setPreferredSize(labelWidth, labelHeight);
617
	 * Gets the graph that this container has been added to.
618
		}
618
	 */
619
		if (labelHeight < 30) {
619
	public Graph getGraph() {
620
			labelHeight = 30;
620
		return this.graph;
621
		}
621
	}
622
622
623
		dimension.labelHeight = labelHeight;
623
	/**
624
		dimension.width = labelWidth;
624
	 * @since 2.0
625
		dimension.width = Math.max(dimension.width, this.size.width);
625
	 */
626
		dimension.expandedHeight = dimension.labelHeight + childAreaHeight - SUBLAYER_OFFSET;
626
	public Widget getItem() {
627
		dimension.expandedHeight = Math.max(dimension.expandedHeight, this.size.height);
627
		return this;
628
628
	}
629
		return dimension;
629
630
	}
630
	public int getItemType() {
631
631
		return CONTAINER;
632
	/*
632
	}
633
	private double computeChildScale() {
633
634
		Dimension childArea = computeChildArea();
634
	/**
635
		double widthScale = childArea.width / scaledWidth;
635
	 * @since 2.0
636
		double heightScale = childArea.height / scaledHeight;
636
	 */
637
		return Math.min(widthScale, heightScale);
637
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm,
638
	}
638
			boolean applyLayout) {
639
	*/
639
		if (this.layoutAlgorithm != null) {
640
	private double computeHeightScale() {
640
			this.layoutAlgorithm.setLayoutContext(null);
641
		Dimension childArea = computeChildArea();
641
		}
642
		double heightScale = childArea.height / scaledHeight;
642
643
		return heightScale;
643
		this.layoutAlgorithm = algorithm;
644
	}
644
		this.layoutAlgorithm.setLayoutContext(getLayoutContext());
645
645
		if (applyLayout) {
646
	private double computeWidthScale() {
646
			applyLayout();
647
		Dimension childArea = computeChildArea();
647
		}
648
		double widthScale = childArea.width / scaledWidth;
648
	}
649
		return widthScale;
649
650
	}
650
	/**
651
651
	 * @noreference This method is not intended to be referenced by clients.
652
	private IFigure createContainerFigure() {
652
	 */
653
		GraphContainer node = this;
653
	public InternalLayoutContext getLayoutContext() {
654
		IFigure containerFigure = new ContainerFigure();
654
		if (layoutContext == null) {
655
		containerFigure.setOpaque(true);
655
			layoutContext = new InternalLayoutContext(this);
656
656
		}
657
		containerFigure.addLayoutListener(LayoutAnimator.getDefault());
657
		return layoutContext;
658
658
	}
659
		containerFigure.setLayoutManager(new FreeformLayout());
659
660
		expandGraphLabel = new ExpandGraphLabel(this, node.getText(), node.getImage(), false);
660
	/**
661
		expandGraphLabel.setText(getText());
661
	 * @since 2.0
662
		expandGraphLabel.setImage(getImage());
662
	 */
663
		ContainerDimension containerDimension = computeContainerSize();
663
	public DisplayIndependentRectangle getLayoutBounds() {
664
664
		double width = GraphContainer.SCALED_WIDTH - 10;
665
		scrollPane = new ScrollPane();
665
		double height = GraphContainer.SCALED_HEIGHT - 10;
666
		scrollPane.addLayoutListener(LayoutAnimator.getDefault());
666
		return new DisplayIndependentRectangle(25, 25, width - 50, height - 50);
667
667
	}
668
		Viewport viewport = new FreeformViewport();
668
669
		/*
669
	public void applyLayout() {
670
		 * This is the code that helps remove the scroll bars moving when the nodes
670
		if (layoutAlgorithm == null) {
671
		 * are dragged.  
671
			setLayoutAlgorithm(new TreeLayoutAlgorithm(), false);
672
		 *
672
		}
673
		viewport.setHorizontalRangeModel(new DefaultRangeModel() {
673
		Animation.markBegin();
674
			public void setAll(int min, int ext, int max) {
674
		layoutAlgorithm.applyLayout(true);
675
				System.out.println("Max: " + max + " : current Max:  " + getMaximum());
675
		layoutContext.flushChanges(false);
676
				if (max < getMaximum()) {
676
		Animation.run(ANIMATION_TIME);
677
					max = getMaximum();
677
		getFigure().getUpdateManager().performUpdate();
678
				}
678
	}
679
				super.setAll(min, ext, max);
679
680
			}
680
	/**
681
681
	 * Get the scale for this container. This is the scale applied to the
682
			public void setMaximum(int maximum) {
682
	 * children contained within
683
				// TODO Auto-generated method stub
683
	 * 
684
				System.out.println("Max: " + maximum + " : current Max:  " + getMaximum());
684
	 * @return
685
				if (maximum < getMaximum()) {
685
	 */
686
					return;
686
	public double getScale() {
687
				}
687
		return this.scalledLayer.getScale();
688
				super.setMaximum(maximum);
688
	}
689
			}
689
690
		});
690
	/**
691
		*/
691
	 * Set the scale for this container. This is the scale applied to the
692
692
	 * children contained within.
693
		scrollPane.setViewport(viewport);
693
	 * 
694
		viewport.addLayoutListener(LayoutAnimator.getDefault());
694
	 * @param scale
695
		scrollPane.setScrollBarVisibility(ScrollPane.AUTOMATIC);
695
	 */
696
696
	public void setScale(double scale) {
697
		//scalledLayer = new ScalableFreeformLayeredPane();
697
		this.scalledLayer.setScale(scale);
698
		scalledLayer = new AspectRatioFreeformLayer("debug label");
698
	}
699
		scalledLayer.addLayoutListener(LayoutAnimator.getDefault());
699
700
		//scalledLayer.setScale(computeChildScale());
700
	/***************************************************************************
701
		scalledLayer.setScale(computeWidthScale(), computeHeightScale());
701
	 * NON API MEMBERS
702
		//container = new FreeformLayer();
702
	 **************************************************************************/
703
		//edgeLayer = new FreeformLayer();
703
	protected void initFigure() {
704
		zestLayer = new ZestRootLayer();
704
		nodeFigure = createContainerFigure();
705
		zestLayer.addLayoutListener(LayoutAnimator.getDefault());
705
	}
706
		//container.addLayoutListener(LayoutAnimator.getDefault());
706
707
		//edgeLayer.addLayoutListener(LayoutAnimator.getDefault());
707
	/**
708
		//scalledLayer.add(edgeLayer);
708
	 * This is a small class to help represent the size of the container. It
709
		//scalledLayer.add(container);
709
	 * should only be used in the computeContainerSize method.
710
		scalledLayer.add(zestLayer);
710
	 */
711
711
	class ContainerDimension {
712
		//container.setLayoutManager(new FreeformLayout());
712
		int width;
713
		zestLayer.setLayoutManager(new FreeformLayout());
713
		int labelHeight;
714
		scrollPane.setSize(computeChildArea());
714
		int expandedHeight;
715
		scrollPane.setLocation(new Point(0, containerDimension.labelHeight - SUBLAYER_OFFSET));
715
	}
716
		scrollPane.setForegroundColor(ColorConstants.gray);
716
717
717
	/**
718
		expandGraphLabel.setBackgroundColor(getBackgroundColor());
718
	 * Computes size of the scroll pane that the child nodes will be placed in.
719
		expandGraphLabel.setForegroundColor(getForegroundColor());
719
	 * 
720
		expandGraphLabel.setLocation(new Point(0, 0));
720
	 * @return
721
721
	 */
722
		containerFigure.add(scrollPane);
722
	private Dimension computeChildArea() {
723
		containerFigure.add(expandGraphLabel);
723
		ContainerDimension containerDimension = computeContainerSize();
724
724
		Dimension dimension = new Dimension();
725
		scrollPane.getViewport().setContents(scalledLayer);
725
		dimension.width = containerDimension.width;
726
		scrollPane.setBorder(new LineBorder());
726
		dimension.height = containerDimension.expandedHeight
727
727
				- containerDimension.labelHeight + SUBLAYER_OFFSET;
728
		return containerFigure;
728
		return dimension;
729
	}
729
	}
730
730
731
	protected void updateFigureForModel(IFigure currentFigure) {
731
	/**
732
732
	 * Computes the desired size of the container. This method uses the minimum
733
		expandGraphLabel.setTextT(getText());
733
	 * size, label size and setSize to compute the size.
734
		expandGraphLabel.setImage(getImage());
734
	 * 
735
		expandGraphLabel.setFont(getFont());
735
	 * @return
736
736
	 */
737
		if (highlighted == HIGHLIGHT_ON) {
737
	private ContainerDimension computeContainerSize() {
738
			expandGraphLabel.setForegroundColor(getForegroundColor());
738
		ContainerDimension dimension = new ContainerDimension();
739
			expandGraphLabel.setBackgroundColor(getHighlightColor());
739
		int labelHeight = expandGraphLabel.getPreferredSize().height;
740
		}
740
		int labelWidth = expandGraphLabel.getPreferredSize().width;
741
		// @tag ADJACENT : Removed highlight adjacent
741
		if (labelWidth < MIN_WIDTH) {
742
		/*
742
			labelWidth = MIN_WIDTH;
743
		else if (highlighted == HIGHLIGHT_ADJACENT) {
743
			expandGraphLabel.setPreferredSize(labelWidth, labelHeight);
744
			expandGraphLabel.setForegroundColor(getForegroundColor());
744
		}
745
			expandGraphLabel.setBackgroundColor(getHighlightAdjacentColor());
745
746
		}
746
		dimension.labelHeight = Math.max(labelHeight, MIN_HEIGHT);
747
		*/
747
		dimension.width = Math.max(labelWidth, this.size.width);
748
		else {
748
		dimension.expandedHeight = Math.max(dimension.labelHeight
749
			expandGraphLabel.setForegroundColor(getForegroundColor());
749
				+ childAreaHeight - SUBLAYER_OFFSET, this.size.height);
750
			expandGraphLabel.setBackgroundColor(getBackgroundColor());
750
751
		}
751
		return dimension;
752
752
	}
753
		ContainerDimension containerDimension = computeContainerSize();
753
754
754
	private double computeHeightScale() {
755
		expandGraphLabel.setSize(containerDimension.width, containerDimension.labelHeight);
755
		Dimension childArea = computeChildArea();
756
		if (isExpanded) {
756
		double heightScale = childArea.height / SCALED_HEIGHT;
757
			//setSize(expandGraphLabel.getSize().width, expandGraphLabel.getSize().height + expandedHeight - SUBLAYER_OFFSET);
757
		return heightScale;
758
			setSize(containerDimension.width, containerDimension.expandedHeight);
758
	}
759
		} else {
759
760
			setSize(containerDimension.width, containerDimension.labelHeight);
760
	private double computeWidthScale() {
761
		}
761
		Dimension childArea = computeChildArea();
762
		scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x, expandGraphLabel.getLocation().y + containerDimension.labelHeight - SUBLAYER_OFFSET));
762
		double widthScale = childArea.width / SCALED_WIDTH;
763
		//scrollPane.setLocation(new Point(0, labelHeight - SUBLAYER_OFFSET));
763
		return widthScale;
764
		//Rectangle bounds = expandGraphLabel.getBounds().getCopy();
764
	}
765
		//Rectangle newBounds = new Rectangle(new Point(bounds.x, bounds.y + labelHeight - SUBLAYER_OFFSET), scrollPane.getSize());
765
766
		//figure.setConstraint(scrollPane, newBounds);
766
	private IFigure createContainerFigure() {
767
		/*
767
		GraphContainer node = this;
768
		size.width = labelWidth;
768
		IFigure containerFigure = new ContainerFigure();
769
		if (scrollPane.getSize().height > 0) {
769
		containerFigure.setOpaque(true);
770
			size.height = labelHeight + scrollPane.getSize().height - SUBLAYER_OFFSET;
770
771
		} else {
771
		containerFigure.addLayoutListener(LayoutAnimator.getDefault());
772
			size.height = labelHeight;
772
773
		}
773
		containerFigure.setLayoutManager(new FreeformLayout());
774
		refreshLocation();
774
		expandGraphLabel = new ExpandGraphLabel(this, node.getText(), node
775
		figure.getUpdateManager().performValidation();
775
				.getImage(), false);
776
		*/
776
		expandGraphLabel.setText(getText());
777
777
		expandGraphLabel.setImage(getImage());
778
	}
778
		ContainerDimension containerDimension = computeContainerSize();
779
779
780
	protected void refreshLocation() {
780
		scrollPane = new ScrollPane();
781
		if (nodeFigure == null || nodeFigure.getParent() == null) {
781
		scrollPane.addLayoutListener(LayoutAnimator.getDefault());
782
			return; // node figure has not been created yet
782
783
		}
783
		Viewport viewport = new FreeformViewport();
784
		GraphNode node = this;
784
785
		Point loc = node.getLocation();
785
		scrollPane.setViewport(viewport);
786
786
		viewport.addLayoutListener(LayoutAnimator.getDefault());
787
		ContainerDimension containerDimension = computeContainerSize();
787
		scrollPane.setScrollBarVisibility(ScrollPane.AUTOMATIC);
788
		Dimension size = new Dimension();
788
789
789
		scalledLayer = new AspectRatioFreeformLayer("debug label");
790
		expandGraphLabel.setSize(containerDimension.width, containerDimension.labelHeight);
790
		scalledLayer.addLayoutListener(LayoutAnimator.getDefault());
791
		this.childAreaHeight = computeChildArea().height;
791
		scalledLayer.setScale(computeWidthScale(), computeHeightScale());
792
		if (isExpanded) {
792
		zestLayer = new ZestRootLayer();
793
			size.width = containerDimension.width;
793
		zestLayer.addLayoutListener(LayoutAnimator.getDefault());
794
			size.height = containerDimension.expandedHeight;
794
		scalledLayer.add(zestLayer);
795
		} else {
795
796
			size.width = containerDimension.width;
796
		zestLayer.setLayoutManager(new FreeformLayout());
797
			size.height = containerDimension.labelHeight;
797
		scrollPane.setSize(computeChildArea());
798
		}
798
		scrollPane.setLocation(new Point(0, containerDimension.labelHeight
799
		Rectangle bounds = new Rectangle(loc, size);
799
				- SUBLAYER_OFFSET));
800
		nodeFigure.getParent().setConstraint(nodeFigure, bounds);
800
		scrollPane.setForegroundColor(ColorConstants.gray);
801
		scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x, expandGraphLabel.getLocation().y + containerDimension.labelHeight - SUBLAYER_OFFSET));
801
802
		scrollPane.setSize(computeChildArea());
802
		expandGraphLabel.setBackgroundColor(getBackgroundColor());
803
		scalledLayer.setScale(computeWidthScale(), computeHeightScale());
803
		expandGraphLabel.setForegroundColor(getForegroundColor());
804
	}
804
		expandGraphLabel.setLocation(new Point(0, 0));
805
805
806
	void addConnectionFigure(PolylineConnection connection) {
806
		containerFigure.add(scrollPane);
807
		nodeFigure.add(connection);
807
		containerFigure.add(expandGraphLabel);
808
		//zestLayer.addConnection(connection);
808
809
	}
809
		scrollPane.getViewport().setContents(scalledLayer);
810
810
		scrollPane.setBorder(new LineBorder());
811
	void addNode(GraphNode node) {
811
812
		zestLayer.addNode(node.getNodeFigure());
812
		return containerFigure;
813
		this.childNodes.add(node);
813
	}
814
		//container.add(node.getNodeFigure());
814
815
		//graph.registerItem(node);
815
	private void registerToParent(IContainer parent) {
816
	}
816
		if (parent.getItemType() == GRAPH) {
817
817
			createSelectionListener();
818
	void addNode(GraphContainer container) {
818
			parent.getGraph().addSelectionListener(selectionListener);
819
		// Containers cannot be added to other containers (yet)
819
		}
820
	}
820
	}
821
821
822
	public List getNodes() {
822
	private void createSelectionListener() {
823
		return this.childNodes;
823
		if (selectionListener == null) {
824
	}
824
			selectionListener = new SelectionListener() {
825
825
				public void widgetSelected(SelectionEvent e) {
826
	void paint() {
826
					if (e.item instanceof GraphContainer) {
827
		Iterator iterator = getNodes().iterator();
827
						// set focus to expand label so that pressing space
828
828
						// opens/closes
829
		while (iterator.hasNext()) {
829
						// the last selected container
830
			GraphNode node = (GraphNode) iterator.next();
830
						((GraphContainer) e.item).expandGraphLabel.setFocus();
831
			node.paint();
831
					}
832
		}
832
				}
833
	}
833
834
834
				public void widgetDefaultSelected(SelectionEvent e) {
835
}
835
					// ignore
836
				}
837
			};
838
839
		}
840
	}
841
842
	protected void updateFigureForModel(IFigure currentFigure) {
843
844
		if (expandGraphLabel == null) {
845
			initFigure();
846
		}
847
		expandGraphLabel.setTextT(getText());
848
		expandGraphLabel.setImage(getImage());
849
		expandGraphLabel.setFont(getFont());
850
851
		if (highlighted == HIGHLIGHT_ON) {
852
			expandGraphLabel.setForegroundColor(getForegroundColor());
853
			expandGraphLabel.setBackgroundColor(getHighlightColor());
854
		} else {
855
			expandGraphLabel.setForegroundColor(getForegroundColor());
856
			expandGraphLabel.setBackgroundColor(getBackgroundColor());
857
		}
858
859
		ContainerDimension containerDimension = computeContainerSize();
860
861
		expandGraphLabel.setSize(containerDimension.width,
862
				containerDimension.labelHeight);
863
		if (isExpanded) {
864
			setSize(containerDimension.width, containerDimension.expandedHeight);
865
		} else {
866
			setSize(containerDimension.width, containerDimension.labelHeight);
867
		}
868
		scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x,
869
				expandGraphLabel.getLocation().y
870
						+ containerDimension.labelHeight - SUBLAYER_OFFSET));
871
872
	}
873
874
	void refreshBounds() {
875
		if (nodeFigure == null || nodeFigure.getParent() == null) {
876
			return; // node figure has not been created yet
877
		}
878
		GraphNode node = this;
879
		Point loc = node.getLocation();
880
881
		ContainerDimension containerDimension = computeContainerSize();
882
		Dimension size = new Dimension();
883
884
		expandGraphLabel.setSize(containerDimension.width,
885
				containerDimension.labelHeight);
886
		this.childAreaHeight = computeChildArea().height;
887
		if (isExpanded) {
888
			size.width = containerDimension.width;
889
			size.height = containerDimension.expandedHeight;
890
		} else {
891
			size.width = containerDimension.width;
892
			size.height = containerDimension.labelHeight;
893
		}
894
		Rectangle bounds = new Rectangle(loc, size);
895
		nodeFigure.getParent().setConstraint(nodeFigure, bounds);
896
		scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x,
897
				expandGraphLabel.getLocation().y
898
						+ containerDimension.labelHeight - SUBLAYER_OFFSET));
899
		scrollPane.setSize(computeChildArea());
900
		scalledLayer.setScale(computeWidthScale(), computeHeightScale());
901
	}
902
903
	/**
904
	 * @noreference This method is not intended to be referenced by clients.
905
	 */
906
	public void addSubgraphFigure(IFigure figure) {
907
		zestLayer.addSubgraph(figure);
908
		graph.subgraphFigures.add(figure);
909
	}
910
911
	void addConnectionFigure(IFigure figure) {
912
		nodeFigure.add(figure);
913
	}
914
915
	/**
916
	 * @noreference This method is not intended to be referenced by clients.
917
	 */
918
	public void addNode(GraphNode node) {
919
		zestLayer.addNode(node.getNodeFigure());
920
		this.childNodes.add(node);
921
		node.setVisible(isExpanded);
922
	}
923
924
	public List getNodes() {
925
		return this.childNodes;
926
	}
927
928
	/**
929
	 * @since 2.0
930
	 */
931
	public List getConnections() {
932
		return filterConnections(getGraph().getConnections());
933
934
	}
935
936
	private List filterConnections(List connections) {
937
		List result = new ArrayList();
938
		for (Iterator iterator = connections.iterator(); iterator.hasNext();) {
939
			GraphConnection connection = (GraphConnection) iterator.next();
940
			if (connection.getSource().getParent() == this
941
					&& connection.getDestination().getParent() == this) {
942
				result.add(connection);
943
			}
944
		}
945
		return result;
946
	}
947
}
(-)src/org/eclipse/zest/core/widgets/GraphItem.java (-114 / +117 lines)
Lines 1-114 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * All rights reserved. This program and the accompanying materials are made
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria 
9
 ******************************************************************************/
9
 *               Mateusz Matela
10
package org.eclipse.zest.core.widgets;
10
 ******************************************************************************/
11
11
package org.eclipse.zest.core.widgets;
12
import org.eclipse.draw2d.IFigure;
12
13
import org.eclipse.swt.SWT;
13
import org.eclipse.draw2d.IFigure;
14
import org.eclipse.swt.widgets.Item;
14
import org.eclipse.swt.SWT;
15
import org.eclipse.swt.widgets.Widget;
15
import org.eclipse.swt.widgets.Item;
16
16
import org.eclipse.swt.widgets.Widget;
17
/**
17
18
 * Provides support for property changes. All model elements extend this class.
18
/**
19
 * Also extends the Item (Widget) class to be used inside a StructuredViewer.
19
 * Provides support for property changes. All model elements extend this class.
20
 * 
20
 * Also extends the Item (Widget) class to be used inside a StructuredViewer.
21
 * @author Chris Callendar
21
 * 
22
 */
22
 * @author Chris Callendar
23
public abstract class GraphItem extends Item {
23
 */
24
24
public abstract class GraphItem extends Item {
25
	public static final int GRAPH = 0;
25
26
	public static final int NODE = 1;
26
	public static final int GRAPH = 0;
27
	public static final int CONNECTION = 2;
27
	public static final int NODE = 1;
28
	public static final int CONTAINER = 3;
28
	public static final int CONNECTION = 2;
29
29
	public static final int CONTAINER = 3;
30
	/**
30
31
	 * @param parent
31
	/**
32
	 * @param style
32
	 * @param parent
33
	 */
33
	 * @param style
34
	public GraphItem(Widget parent, int style) {
34
	 */
35
		this(parent, style | SWT.NO_BACKGROUND, null);
35
	public GraphItem(Widget parent, int style) {
36
	}
36
		this(parent, style | SWT.NO_BACKGROUND, null);
37
37
	}
38
	/**
38
39
	 * @param parent
39
	/**
40
	 * @param style
40
	 * @param parent
41
	 */
41
	 * @param style
42
	public GraphItem(Widget parent, int style, Object data) {
42
	 */
43
		super(parent, style | SWT.NO_BACKGROUND);
43
	public GraphItem(Widget parent, int style, Object data) {
44
		if (data != null) {
44
		super(parent, style | SWT.NO_BACKGROUND);
45
			this.setData(data);
45
		if (data != null) {
46
		}
46
			this.setData(data);
47
	}
47
		}
48
48
	}
49
	/*
49
50
	 * (non-Javadoc)
50
	/*
51
	 * 
51
	 * (non-Javadoc)
52
	 * @see org.eclipse.swt.widgets.Widget#dispose()
52
	 * 
53
	 */
53
	 * @see org.eclipse.swt.widgets.Widget#dispose()
54
	public void dispose() {
54
	 */
55
		// @tag zest.bug.167132-ListenerDispose : remove all listeners.
55
	public void dispose() {
56
		// pcsDelegate = new PropertyChangeSupport(this);
56
		// @tag zest.bug.167132-ListenerDispose : remove all listeners.
57
		super.dispose();
57
		// pcsDelegate = new PropertyChangeSupport(this);
58
	}
58
		super.dispose();
59
59
	}
60
	/**
60
61
	 * Gets the graph item type. The item type is one of: GRAPH, NODE or
61
	/**
62
	 * CONNECTION
62
	 * Gets the graph item type. The item type is one of: GRAPH, NODE or
63
	 * 
63
	 * CONNECTION
64
	 * @return
64
	 * 
65
	 */
65
	 * @return
66
	public abstract int getItemType();
66
	 */
67
67
	public abstract int getItemType();
68
	/**
68
69
	 * Set the visibility of this item.
69
	/**
70
	 * 
70
	 * Set the visibility of this item.
71
	 * @param visible
71
	 * 
72
	 *            whether or not this item is visible.
72
	 * @param visible
73
	 */
73
	 *            whether or not this item is visible.
74
	public abstract void setVisible(boolean visible);
74
	 */
75
75
	public abstract void setVisible(boolean visible);
76
	/**
76
77
	 * Get the visibility of this item.
77
	/**
78
	 * 
78
	 * Get the visibility of this item.
79
	 * @return the visibility of this item.
79
	 * 
80
	 */
80
	 * @return the visibility of this item.
81
	public abstract boolean isVisible();
81
	 */
82
82
	public abstract boolean isVisible();
83
	/**
83
84
	 * Gets the graph that this item is rooted on. If this item is itself a
84
	/**
85
	 * graph, then this is returned.
85
	 * Gets the graph that this item is rooted on. If this item is itself a
86
	 * 
86
	 * graph, then this is returned.
87
	 * @return the parent graph.
87
	 * 
88
	 */
88
	 * @return the parent graph.
89
	public abstract Graph getGraphModel();
89
	 */
90
90
	public abstract Graph getGraphModel();
91
	/**
91
92
	 * Highlights the current GraphItem.  A graph item is either a graph node or 
92
	/**
93
	 * graph connection, and highlighting them will set the appropriate highlight
93
	 * Highlights the current GraphItem. A graph item is either a graph node or
94
	 * color.
94
	 * graph connection, and highlighting them will set the appropriate
95
	 */
95
	 * highlight color.
96
	public abstract void highlight();
96
	 */
97
97
	public abstract void highlight();
98
	/**
98
99
	 * Unhighlight sets the graphItem (either a graphNode or graphConnection) back
99
	/**
100
	 * to the unhighlight figure or color.
100
	 * Unhighlight sets the graphItem (either a graphNode or graphConnection)
101
	 */
101
	 * back to the unhighlight figure or color.
102
	public abstract void unhighlight();
102
	 */
103
103
	public abstract void unhighlight();
104
	abstract IFigure getFigure();
104
105
105
	abstract IFigure getFigure();
106
	/**
106
107
	 * Checks a style to see if it is set on the given graph item
107
	/**
108
	 * @param styleToCheck The style to check
108
	 * Checks a style to see if it is set on the given graph item
109
	 * @return
109
	 * 
110
	 */
110
	 * @param styleToCheck
111
	protected boolean checkStyle(int styleToCheck) {
111
	 *            The style to check
112
		return ((getStyle() & styleToCheck) > 0);
112
	 * @return
113
	}
113
	 */
114
}
114
	protected boolean checkStyle(int styleToCheck) {
115
		return ((getStyle() & styleToCheck) > 0);
116
	}
117
}
(-)src/org/eclipse/zest/core/widgets/GraphNode.java (-955 / +789 lines)
Lines 1-955 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * All rights reserved. This program and the accompanying materials are made
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria 
9
 ******************************************************************************/
9
 *               Mateusz Matela
10
package org.eclipse.zest.core.widgets;
10
 ******************************************************************************/
11
11
package org.eclipse.zest.core.widgets;
12
import java.util.ArrayList;
12
13
import java.util.Iterator;
13
import java.util.ArrayList;
14
import java.util.List;
14
import java.util.Iterator;
15
15
import java.util.List;
16
import org.eclipse.draw2d.ColorConstants;
16
17
import org.eclipse.draw2d.IFigure;
17
import org.eclipse.draw2d.Animation;
18
import org.eclipse.draw2d.Label;
18
import org.eclipse.draw2d.ColorConstants;
19
import org.eclipse.draw2d.geometry.Dimension;
19
import org.eclipse.draw2d.FigureListener;
20
import org.eclipse.draw2d.geometry.Insets;
20
import org.eclipse.draw2d.IFigure;
21
import org.eclipse.draw2d.geometry.Point;
21
import org.eclipse.draw2d.Label;
22
import org.eclipse.draw2d.geometry.PrecisionPoint;
22
import org.eclipse.draw2d.geometry.Dimension;
23
import org.eclipse.draw2d.geometry.Rectangle;
23
import org.eclipse.draw2d.geometry.Insets;
24
import org.eclipse.swt.SWT;
24
import org.eclipse.draw2d.geometry.Point;
25
import org.eclipse.swt.graphics.Color;
25
import org.eclipse.draw2d.geometry.PrecisionPoint;
26
import org.eclipse.swt.graphics.Font;
26
import org.eclipse.draw2d.geometry.Rectangle;
27
import org.eclipse.swt.graphics.FontData;
27
import org.eclipse.swt.graphics.Color;
28
import org.eclipse.swt.graphics.Image;
28
import org.eclipse.swt.graphics.Font;
29
import org.eclipse.swt.widgets.Display;
29
import org.eclipse.swt.graphics.Image;
30
import org.eclipse.zest.core.widgets.internal.GraphLabel;
30
import org.eclipse.swt.widgets.Display;
31
import org.eclipse.zest.layouts.LayoutEntity;
31
import org.eclipse.zest.core.widgets.internal.GraphLabel;
32
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
32
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
33
33
34
/*
34
/**
35
 * Simple node class which has the following properties: color, size, location,
35
 * Simple node class which has the following properties: color, size, location,
36
 * and a label. It also has a list of connections and anchors.
36
 * and a label. It also has a list of connections and anchors.
37
 * 
37
 * 
38
 * @author Chris Callendar
38
 * @author Chris Callendar
39
 * 
39
 * 
40
 * @author Del Myers
40
 * @author Del Myers
41
 * 
41
 * 
42
 * @author Ian Bull
42
 * @author Ian Bull
43
 */
43
 */
44
public class GraphNode extends GraphItem {
44
public class GraphNode extends GraphItem {
45
	public static final int HIGHLIGHT_NONE = 0;
45
	public static final int HIGHLIGHT_NONE = 0;
46
	public static final int HIGHLIGHT_ON = 1;
46
	public static final int HIGHLIGHT_ON = 1;
47
	// @tag ADJACENT : Removed highlight adjacent
47
48
	//public static final int HIGHLIGHT_ADJACENT = 2;
48
	private int nodeStyle;
49
49
50
	private int nodeStyle;
50
	private List /* IGraphModelConnection */sourceConnections;
51
51
	private List /* IGraphModelConnection */targetConnections;
52
	private List /* IGraphModelConnection */sourceConnections;
52
53
	private List /* IGraphModelConnection */targetConnections;
53
	private Color foreColor;
54
54
	private Color backColor;
55
	private Color foreColor;
55
	private Color highlightColor;
56
	private Color backColor;
56
	private Color borderColor;
57
	private Color highlightColor;
57
	private Color borderHighlightColor;
58
	// @tag ADJACENT : Removed highlight adjacent
58
	private int borderWidth;
59
	//private Color highlightAdjacentColor;
59
	private PrecisionPoint currentLocation;
60
	private Color borderColor;
60
	protected Dimension size;
61
	private Color borderHighlightColor;
61
	private Font font;
62
	private int borderWidth;
62
	private boolean cacheLabel;
63
	private Point currentLocation;
63
	private boolean visible = true;
64
	protected Dimension size;
64
65
	private Font font;
65
	protected Graph graph;
66
	private boolean cacheLabel;
66
	protected IContainer parent;
67
	private boolean visible = true;
67
68
	private LayoutEntity layoutEntity;
68
	/** The internal node. */
69
69
	protected Object internalNode;
70
	protected Graph graph;
70
	private boolean selected;
71
	protected IContainer parent;
71
	protected int highlighted = HIGHLIGHT_NONE;
72
72
	private IFigure tooltip;
73
	/** The internal node. */
73
	protected IFigure nodeFigure;
74
	protected Object internalNode;
74
75
	private boolean selected;
75
	private boolean isDisposed = false;
76
	protected int highlighted = HIGHLIGHT_NONE;
76
	private boolean hasCustomTooltip;
77
	private IFigure tooltip;
77
78
	protected IFigure nodeFigure;
78
	public GraphNode(IContainer graphModel, int style) {
79
79
		this(graphModel, style, null);
80
	private boolean isDisposed = false;
80
	}
81
	private boolean hasCustomTooltip;
81
82
82
	public GraphNode(IContainer graphModel, int style, String text) {
83
	public GraphNode(IContainer graphModel, int style) {
83
		this(graphModel, style, text, null, null);
84
		this(graphModel, style, null);
84
	}
85
	}
85
86
86
	public GraphNode(IContainer graphModel, int style, Object data) {
87
	public GraphNode(IContainer graphModel, int style, Object data) {
87
		this(graphModel, style, "" /* text */, null /* image */, data);
88
		this(graphModel.getGraph(), style, "" /*text*/, null /*image*/, data);
88
	}
89
	}
89
90
90
	private GraphNode(IContainer graphModel, int style, String text,
91
	public GraphNode(IContainer graphModel, int style, String text) {
91
			Image image, Object data) {
92
		this(graphModel, style, text, null);
92
		super(graphModel.getGraph(), style, data);
93
	}
93
		initModel(graphModel, text, image);
94
94
		if (nodeFigure == null) {
95
	public GraphNode(IContainer graphModel, int style, String text, Object data) {
95
			initFigure();
96
		this(graphModel.getGraph(), style, text, null /*image*/, data);
96
		}
97
	}
97
98
98
		this.parent.addNode(this);
99
	public GraphNode(IContainer graphModel, int style, String text, Image image) {
99
		this.parent.getGraph().registerItem(this);
100
		this(graphModel, style, text, image, null);
100
	}
101
	}
101
102
102
	protected void initFigure() {
103
	public GraphNode(IContainer graphModel, int style, String text, Image image, Object data) {
103
		nodeFigure = createFigureForModel();
104
		super(graphModel.getGraph(), style, data);
104
	}
105
		initModel(graphModel, text, image);
105
106
		if (nodeFigure == null) {
106
	static int count = 0;
107
			initFigure();
107
108
		}
108
	protected void initModel(IContainer graphModel, String text, Image image) {
109
109
		this.nodeStyle |= graphModel.getGraph().getNodeStyle();
110
		// This is a hack because JAVA sucks!
110
		this.parent = graphModel;
111
		// I don't want to expose addNode so I can't put it in the
111
		this.sourceConnections = new ArrayList();
112
		// IContainer interface.  
112
		this.targetConnections = new ArrayList();
113
		if (this.parent.getItemType() == GRAPH) {
113
		this.foreColor = graphModel.getGraph().DARK_BLUE;
114
			((Graph) this.parent).addNode(this);
114
		this.backColor = graphModel.getGraph().LIGHT_BLUE;
115
		} else if (this.parent.getItemType() == CONTAINER) {
115
		this.highlightColor = graphModel.getGraph().HIGHLIGHT_COLOR;
116
			((GraphContainer) this.parent).addNode(this);
116
		this.borderColor = ColorConstants.lightGray;
117
		}
117
		this.borderHighlightColor = ColorConstants.blue;
118
		this.parent.getGraph().registerItem(this);
118
		this.borderWidth = 1;
119
	}
119
		this.currentLocation = new PrecisionPoint(0, 0);
120
120
		this.size = new Dimension(-1, -1);
121
	protected void initFigure() {
121
		this.font = Display.getDefault().getSystemFont();
122
		nodeFigure = createFigureForModel();
122
		this.graph = graphModel.getGraph();
123
	}
123
		this.cacheLabel = false;
124
124
		this.setText(text);
125
	static int count = 0;
125
		if (image != null) {
126
126
			this.setImage(image);
127
	protected void initModel(IContainer parent, String text, Image image) {
127
		}
128
		this.nodeStyle |= parent.getGraph().getNodeStyle();
128
129
		this.parent = parent;
129
		if (font == null) {
130
		this.sourceConnections = new ArrayList();
130
			font = Display.getDefault().getSystemFont();
131
		this.targetConnections = new ArrayList();
131
		}
132
		this.foreColor = parent.getGraph().DARK_BLUE;
132
133
		this.backColor = parent.getGraph().LIGHT_BLUE;
133
	}
134
		this.highlightColor = parent.getGraph().HIGHLIGHT_COLOR;
134
135
		// @tag ADJACENT : Removed highlight adjacent
135
	/**
136
		//this.highlightAdjacentColor = ColorConstants.orange;
136
	 * A simple toString that we can use for debugging
137
		this.nodeStyle = SWT.NONE;
137
	 */
138
		this.borderColor = ColorConstants.lightGray;
138
	public String toString() {
139
		this.borderHighlightColor = ColorConstants.blue;
139
		return "GraphModelNode: " + getText();
140
		this.borderWidth = 1;
140
	}
141
		this.currentLocation = new PrecisionPoint(0, 0);
141
142
		this.size = new Dimension(-1, -1);
142
	/*
143
		this.font = Display.getDefault().getSystemFont();
143
	 * (non-Javadoc)
144
		this.graph = parent.getGraph();
144
	 * 
145
		this.cacheLabel = false;
145
	 * @see org.eclipse.mylar.zest.core.widgets.GraphItem#dispose()
146
		this.setText(text);
146
	 */
147
		this.layoutEntity = new LayoutGraphNode();
147
	public void dispose() {
148
		if (image != null) {
148
		if (isFisheyeEnabled) {
149
			this.setImage(image);
149
			this.fishEye(false, false);
150
		}
150
		}
151
151
		super.dispose();
152
		if (font == null) {
152
		this.isDisposed = true;
153
			font = Display.getDefault().getSystemFont();
153
		while (getSourceConnections().size() > 0) {
154
		}
154
			GraphConnection connection = (GraphConnection) getSourceConnections()
155
155
					.get(0);
156
	}
156
			if (!connection.isDisposed()) {
157
157
				connection.dispose();
158
	/**
158
			} else {
159
	 * A simple toString that we can use for debugging
159
				removeSourceConnection(connection);
160
	 */
160
			}
161
	public String toString() {
161
		}
162
		return "GraphModelNode: " + getText();
162
		while (getTargetConnections().size() > 0) {
163
	}
163
			GraphConnection connection = (GraphConnection) getTargetConnections()
164
164
					.get(0);
165
	public LayoutEntity getLayoutEntity() {
165
			if (!connection.isDisposed()) {
166
		return layoutEntity;
166
				connection.dispose();
167
	}
167
			} else {
168
168
				removeTargetConnection(connection);
169
	/*
169
			}
170
	 * (non-Javadoc)
170
		}
171
	 * 
171
		graph.removeNode(this);
172
	 * @see org.eclipse.mylar.zest.core.widgets.GraphItem#dispose()
172
	}
173
	 */
173
174
	public void dispose() {
174
	/*
175
		if (isFisheyeEnabled) {
175
	 * (non-Javadoc)
176
			this.fishEye(false, false);
176
	 * 
177
		}
177
	 * @see org.eclipse.swt.widgets.Widget#isDisposed()
178
		super.dispose();
178
	 */
179
		this.isDisposed = true;
179
	public boolean isDisposed() {
180
		while (getSourceConnections().size() > 0) {
180
		return isDisposed;
181
			GraphConnection connection = (GraphConnection) getSourceConnections().get(0);
181
	}
182
			if (!connection.isDisposed()) {
182
183
				connection.dispose();
183
	/**
184
			} else {
184
	 * Determines if this node has a fixed size or if it is packed to the size
185
				removeSourceConnection(connection);
185
	 * of its contents. To set a node to pack, set its size (-1, -1)
186
			}
186
	 * 
187
		}
187
	 * @return
188
		while (getTargetConnections().size() > 0) {
188
	 */
189
			GraphConnection connection = (GraphConnection) getTargetConnections().get(0);
189
	public boolean isSizeFixed() {
190
			if (!connection.isDisposed()) {
190
		return !(this.size.width < 0 && this.size.height < 0);
191
				connection.dispose();
191
	}
192
			} else {
192
193
				removeTargetConnection(connection);
193
	/**
194
			}
194
	 * Returns a new list of the source connections (GraphModelConnection
195
		}
195
	 * objects).
196
		graph.removeNode(this);
196
	 * 
197
	}
197
	 * @return List a new list of GraphModelConnect objects
198
198
	 */
199
	/*
199
	public List getSourceConnections() {
200
	 * (non-Javadoc)
200
		return new ArrayList(sourceConnections);
201
	 * 
201
	}
202
	 * @see org.eclipse.swt.widgets.Widget#isDisposed()
202
203
	 */
203
	/**
204
	public boolean isDisposed() {
204
	 * Returns a new list of the target connections (GraphModelConnection
205
		return isDisposed;
205
	 * objects).
206
	}
206
	 * 
207
207
	 * @return List a new list of GraphModelConnect objects
208
	/**
208
	 */
209
	 * Determines if this node has a fixed size or if it is packed to the size of its contents.
209
	public List getTargetConnections() {
210
	 * To set a node to pack, set its size (-1, -1)  
210
		return new ArrayList(targetConnections);
211
	 * @return
211
	}
212
	 */
212
213
	public boolean isSizeFixed() {
213
	/**
214
		return !(this.size.width < 0 && this.size.height < 0);
214
	 * Returns the bounds of this node. It is just the combination of the
215
	}
215
	 * location and the size.
216
216
	 * 
217
	/**
217
	 * @return Rectangle
218
	 * Returns a new list of the source connections (GraphModelConnection
218
	 */
219
	 * objects).
219
	Rectangle getBounds() {
220
	 * 
220
		return new Rectangle(getLocation(), getSize());
221
	 * @return List a new list of GraphModelConnect objects
221
	}
222
	 */
222
223
	public List getSourceConnections() {
223
	/**
224
		return new ArrayList(sourceConnections);
224
	 * Returns a copy of the node's location.
225
	}
225
	 * 
226
226
	 * @return Point
227
	/**
227
	 */
228
	 * Returns a new list of the target connections (GraphModelConnection
228
	public Point getLocation() {
229
	 * objects).
229
		return currentLocation;
230
	 * 
230
	}
231
	 * @return List a new list of GraphModelConnect objects
231
232
	 */
232
	/*
233
	public List getTargetConnections() {
233
	 * (non-Javadoc)
234
		return new ArrayList(targetConnections);
234
	 * 
235
	}
235
	 * @see
236
236
	 * org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#isSelected
237
	/**
237
	 * ()
238
	 * Returns the bounds of this node. It is just the combination of the
238
	 */
239
	 * location and the size.
239
	boolean isSelected() {
240
	 * 
240
		return selected;
241
	 * @return Rectangle
241
	}
242
	 */
242
243
	Rectangle getBounds() {
243
	/**
244
		return new Rectangle(getLocation(), getSize());
244
	 * Sets the current location for this node.
245
	}
245
	 */
246
246
	public void setLocation(double x, double y) {
247
	/**
247
		if (currentLocation.preciseX != x || currentLocation.preciseY != y) {
248
	 * Returns a copy of the node's location.
248
			currentLocation.preciseX = x;
249
	 * 
249
			currentLocation.preciseY = y;
250
	 * @return Point
250
			currentLocation.updateInts();
251
	 */
251
			refreshBounds();
252
	public Point getLocation() {
252
			parent.getLayoutContext().fireNodeMovedEvent(this.getLayout());
253
		return currentLocation;
253
		}
254
	}
254
	}
255
255
256
	/*
256
	/**
257
	 * (non-Javadoc)
257
	 * Returns a copy of the node's size.
258
	 * 
258
	 * 
259
	 * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#isSelected()
259
	 * @return Dimension
260
	 */
260
	 */
261
	public boolean isSelected() {
261
	public Dimension getSize() {
262
		return selected;
262
		if (size.height < 0 && size.width < 0 && nodeFigure != null) {
263
	}
263
			return nodeFigure.getSize().getCopy();
264
264
		}
265
	/**
265
		return size.getCopy();
266
	 * Sets the current location for this node.
266
	}
267
	 */
267
268
	public void setLocation(double x, double y) {
268
	/**
269
		currentLocation.x = (int) x;
269
	 * Get the foreground colour for this node
270
		currentLocation.y = (int) y;
270
	 */
271
		refreshLocation();
271
	public Color getForegroundColor() {
272
	}
272
		return foreColor;
273
273
	}
274
	/**
274
275
	 * Returns a copy of the node's size.
275
	/**
276
	 * 
276
	 * Set the foreground colour for this node
277
	 * @return Dimension
277
	 */
278
	 */
278
	public void setForegroundColor(Color c) {
279
	public Dimension getSize() {
279
		this.foreColor = c;
280
		if (size.height < 0 && size.width < 0 && nodeFigure != null) {
280
		updateFigureForModel(nodeFigure);
281
			return nodeFigure.getSize().getCopy();
281
	}
282
		}
282
283
		return size.getCopy();
283
	/**
284
	}
284
	 * Get the background colour for this node. This is the color the node will
285
285
	 * be if it is not currently highlighted. This color is meaningless if a
286
	/**
286
	 * custom figure has been set.
287
	 * Get the foreground colour for this node
287
	 */
288
	 */
288
	public Color getBackgroundColor() {
289
	public Color getForegroundColor() {
289
		return backColor;
290
		return foreColor;
290
	}
291
	}
291
292
292
	/**
293
	/**
293
	 * Permanently sets the background color (unhighlighted). This color has no
294
	 * Set the foreground colour for this node
294
	 * effect if a custom figure has been set.
295
	 */
295
	 * 
296
	public void setForegroundColor(Color c) {
296
	 * @param c
297
		this.foreColor = c;
297
	 */
298
		updateFigureForModel(nodeFigure);
298
	public void setBackgroundColor(Color c) {
299
	}
299
		backColor = c;
300
300
		updateFigureForModel(nodeFigure);
301
	/**
301
	}
302
	 * Get the background colour for this node. This is the color the node will
302
303
	 * be if it is not currently highlighted. This color is meaningless if a
303
	/**
304
	 * custom figure has been set.
304
	 * Sets the tooltip on this node. This tooltip will display if the mouse
305
	 */
305
	 * hovers over the node. Setting the tooltip has no effect if a custom
306
	public Color getBackgroundColor() {
306
	 * figure has been set.
307
		return backColor;
307
	 */
308
	}
308
	public void setTooltip(IFigure tooltip) {
309
309
		hasCustomTooltip = true;
310
	/**
310
		this.tooltip = tooltip;
311
	 * Permanently sets the background color (unhighlighted). This color has no
311
		updateFigureForModel(nodeFigure);
312
	 * effect if a custom figure has been set.
312
	}
313
	 * 
313
314
	 * @param c
314
	/**
315
	 */
315
	 * Gets the current tooltip for this node. The tooltip returned is
316
	public void setBackgroundColor(Color c) {
316
	 * meaningless if a custom figure has been set.
317
		backColor = c;
317
	 */
318
		updateFigureForModel(nodeFigure);
318
	public IFigure getTooltip() {
319
	}
319
		return this.tooltip;
320
320
	}
321
	/**
321
322
	 * Sets the tooltip on this node. This tooltip will display if the mouse
322
	/**
323
	 * hovers over the node. Setting the tooltip has no effect if a custom
323
	 * Sets the border color.
324
	 * figure has been set.
324
	 * 
325
	 */
325
	 * @param c
326
	public void setTooltip(IFigure tooltip) {
326
	 *            the border color.
327
		hasCustomTooltip = true;
327
	 */
328
		this.tooltip = tooltip;
328
	public void setBorderColor(Color c) {
329
		updateFigureForModel(nodeFigure);
329
		borderColor = c;
330
	}
330
		updateFigureForModel(nodeFigure);
331
331
	}
332
	/**
332
333
	 * Gets the current tooltip for this node. The tooltip returned is
333
	/**
334
	 * meaningless if a custom figure has been set.
334
	 * Sets the highlighted border color.
335
	 */
335
	 * 
336
	public IFigure getTooltip() {
336
	 * @param c
337
		return this.tooltip;
337
	 *            the highlighted border color.
338
	}
338
	 */
339
339
	public void setBorderHighlightColor(Color c) {
340
	/**
340
		this.borderHighlightColor = c;
341
	 * Sets the border color.
341
		updateFigureForModel(nodeFigure);
342
	 * 
342
	}
343
	 * @param c
343
344
	 *            the border color.
344
	/**
345
	 */
345
	 * Get the highlight colour for this node
346
	public void setBorderColor(Color c) {
346
	 */
347
		borderColor = c;
347
	public Color getHighlightColor() {
348
		updateFigureForModel(nodeFigure);
348
		return highlightColor;
349
	}
349
	}
350
350
351
	/**
351
	/**
352
	 * Sets the highlighted border color.
352
	 * Set the highlight colour for this node
353
	 * 
353
	 */
354
	 * @param c
354
	public void setHighlightColor(Color c) {
355
	 *            the highlighted border color.
355
		this.highlightColor = c;
356
	 */
356
	}
357
	public void setBorderHighlightColor(Color c) {
357
358
		this.borderHighlightColor = c;
358
	/**
359
		updateFigureForModel(nodeFigure);
359
	 * Highlights the node changing the background color and border color. The
360
	}
360
	 * source and destination connections are also highlighted, and the adjacent
361
361
	 * nodes are highlighted too in a different color.
362
	/**
362
	 */
363
	 * Get the highlight colour for this node
363
	public void highlight() {
364
	 */
364
		if (highlighted == HIGHLIGHT_ON) {
365
	public Color getHighlightColor() {
365
			return;
366
		return highlightColor;
366
		}
367
	}
367
		IFigure parentFigure = nodeFigure.getParent();
368
368
		if (parentFigure instanceof ZestRootLayer) {
369
	/**
369
			((ZestRootLayer) parentFigure).highlightNode(nodeFigure);
370
	 * Set the highlight colour for this node
370
		}
371
	 */
371
		highlighted = HIGHLIGHT_ON;
372
	public void setHighlightColor(Color c) {
372
		updateFigureForModel(getNodeFigure());
373
		this.highlightColor = c;
373
	}
374
	}
374
375
375
	/**
376
	/**
376
	 * Restores the nodes original background color and border width.
377
	 * Get the highlight adjacent colour for this node. This is the colour that
377
	 */
378
	 * adjacent nodes will get
378
	public void unhighlight() {
379
	 */
379
380
	// @tag ADJACENT : Removed highlight adjacent
380
		if (highlighted == HIGHLIGHT_NONE) {
381
	/*
381
			return;
382
	public Color getHighlightAdjacentColor() {
382
		}
383
		return highlightAdjacentColor;
383
384
	}
384
		IFigure parentFigure = nodeFigure.getParent();
385
	*/
385
		if (parentFigure instanceof ZestRootLayer) {
386
386
			((ZestRootLayer) parentFigure).unHighlightNode(nodeFigure);
387
	/**
387
		}
388
	 * Set the highlight adjacent colour for this node. This is the colour that
388
389
	 * adjacent node will get.
389
		highlighted = HIGHLIGHT_NONE;
390
	 */
390
		updateFigureForModel(nodeFigure);
391
	// @tag ADJACENT : Removed highlight adjacent
391
392
	/*
392
	}
393
	public void setHighlightAdjacentColor(Color c) {
393
394
		this.highlightAdjacentColor = c;
394
	void refreshBounds() {
395
	}
395
		Point loc = this.getLocation();
396
	*/
396
		Dimension size = this.getSize();
397
397
		Rectangle bounds = new Rectangle(loc, size);
398
	/**
398
399
	 * Highlights the node changing the background color and border color. The
399
		if (nodeFigure == null || nodeFigure.getParent() == null) {
400
	 * source and destination connections are also highlighted, and the adjacent
400
			return; // node figure has not been created yet
401
	 * nodes are highlighted too in a different color.
401
		}
402
	 */
402
		nodeFigure.getParent().setConstraint(nodeFigure, bounds);
403
	public void highlight() {
403
404
		if (highlighted == HIGHLIGHT_ON) {
404
		if (isFisheyeEnabled) {
405
			return;
405
			Rectangle fishEyeBounds = calculateFishEyeBounds();
406
		}
406
			if (fishEyeBounds != null) {
407
		// @tag ADJACENT : Removed highlight adjacent
407
				fishEyeFigure.getParent().translateToRelative(fishEyeBounds);
408
		/*
408
				fishEyeFigure.getParent().translateFromParent(fishEyeBounds);
409
		if (ZestStyles.checkStyle(getNodeStyle(), ZestStyles.NODES_HIGHLIGHT_ADJACENT)) {
409
				fishEyeFigure.getParent().setConstraint(fishEyeFigure,
410
			for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) {
410
						fishEyeBounds);
411
				GraphConnection conn = (GraphConnection) iter.next();
411
			}
412
				conn.highlight();
412
		}
413
				conn.getDestination().highlightAdjacent();
413
	}
414
			}
414
415
			for (Iterator iter = targetConnections.iterator(); iter.hasNext();) {
415
	public Color getBorderColor() {
416
				GraphConnection conn = (GraphConnection) iter.next();
416
		return borderColor;
417
				conn.highlight();
417
	}
418
				conn.getSource().highlightAdjacent();
418
419
			}
419
	public int getBorderWidth() {
420
		}
420
		return borderWidth;
421
		*/
421
	}
422
		if (parent.getItemType() == GraphItem.CONTAINER) {
422
423
			((GraphContainer) parent).highlightNode(this);
423
	public void setBorderWidth(int width) {
424
		} else {
424
		this.borderWidth = width;
425
			((Graph) parent).highlightNode(this);
425
		updateFigureForModel(nodeFigure);
426
		}
426
	}
427
		highlighted = HIGHLIGHT_ON;
427
428
		updateFigureForModel(getNodeFigure());
428
	public Font getFont() {
429
	}
429
		return font;
430
430
	}
431
	/**
431
432
	 * Restores the nodes original background color and border width.
432
	public void setFont(Font font) {
433
	 */
433
		this.font = font;
434
	public void unhighlight() {
434
		updateFigureForModel(nodeFigure);
435
435
	}
436
		// @tag ADJACENT : Removed highlight adjacent
436
437
		//boolean highlightedAdjacently = (highlighted == HIGHLIGHT_ADJACENT);
437
	/*
438
		if (highlighted == HIGHLIGHT_NONE) {
438
	 * (non-Javadoc)
439
			return;
439
	 * 
440
		}
440
	 * @see org.eclipse.swt.widgets.Item#setText(java.lang.String)
441
		// @tag ADJACENT : Removed highlight adjacent
441
	 */
442
		/*
442
	public void setText(String string) {
443
		if (!highlightedAdjacently) {
443
		if (string == null) {
444
			// IF we are highlighted as an adjacent node, we don't need to deal
444
			string = "";
445
			// with our connections.
445
		}
446
			if (ZestStyles.checkStyle(getNodeStyle(), ZestStyles.NODES_HIGHLIGHT_ADJACENT)) {
446
		super.setText(string);
447
				// unhighlight the adjacent edges
447
448
				for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) {
448
		updateFigureForModel(this.nodeFigure);
449
					GraphConnection conn = (GraphConnection) iter.next();
449
	}
450
					conn.unhighlight();
450
451
					if (conn.getDestination() != this) {
451
	/*
452
						conn.getDestination().unhighlight();
452
	 * (non-Javadoc)
453
					}
453
	 * 
454
				}
454
	 * @see
455
				for (Iterator iter = targetConnections.iterator(); iter.hasNext();) {
455
	 * org.eclipse.swt.widgets.Item#setImage(org.eclipse.swt.graphics.Image)
456
					GraphConnection conn = (GraphConnection) iter.next();
456
	 */
457
					conn.unhighlight();
457
	public void setImage(Image image) {
458
					if (conn.getSource() != this) {
458
		super.setImage(image);
459
						conn.getSource().unhighlight();
459
		updateFigureForModel(nodeFigure);
460
					}
460
	}
461
				}
461
462
			}
462
	/**
463
		}
463
	 * Gets the graphModel that this node is contained in
464
		*/
464
	 * 
465
		if (parent.getItemType() == GraphItem.CONTAINER) {
465
	 * @return The graph model that this node is contained in
466
			((GraphContainer) parent).unhighlightNode(this);
466
	 */
467
		} else {
467
	public Graph getGraphModel() {
468
			((Graph) parent).unhighlightNode(this);
468
		return this.graph;
469
		}
469
	}
470
		highlighted = HIGHLIGHT_NONE;
470
471
		updateFigureForModel(nodeFigure);
471
	/**
472
472
	 * @return the nodeStyle
473
	}
473
	 */
474
474
	public int getNodeStyle() {
475
	protected void refreshLocation() {
475
		return nodeStyle;
476
		Point loc = this.getLocation();
476
	}
477
		Dimension size = this.getSize();
477
478
		Rectangle bounds = new Rectangle(loc, size);
478
	/**
479
479
	 * @param nodeStyle
480
		if (nodeFigure == null || nodeFigure.getParent() == null) {
480
	 *            the nodeStyle to set
481
			return; // node figure has not been created yet
481
	 */
482
		}
482
	public void setNodeStyle(int nodeStyle) {
483
		//nodeFigure.setBounds(bounds);
483
		this.nodeStyle = nodeStyle;
484
		nodeFigure.getParent().setConstraint(nodeFigure, bounds);
484
		this.cacheLabel = ((this.nodeStyle & ZestStyles.NODES_CACHE_LABEL) > 0) ? true
485
	}
485
				: false;
486
486
	}
487
	/**
487
488
	 * Highlights this node using the adjacent highlight color. This only does
488
	public void setSize(double width, double height) {
489
	 * something if highlighAdjacentNodes is set to true and if the node isn't
489
		if ((width != size.width) || (height != size.height)) {
490
	 * already highlighted.
490
			size.width = (int) width;
491
	 * 
491
			size.height = (int) height;
492
	 * @see #setHighlightAdjacentNodes(boolean)
492
			refreshBounds();
493
	 */
493
		}
494
	// @tag ADJACENT : removed highlight adjacent
494
	}
495
	/*
495
496
	public void highlightAdjacent() {
496
	public Color getBorderHighlightColor() {
497
		if (highlighted > 0) {
497
		return borderHighlightColor;
498
			return;
498
	}
499
		}
499
500
		highlighted = HIGHLIGHT_ADJACENT;
500
	public boolean cacheLabel() {
501
		updateFigureForModel(nodeFigure);
501
		return this.cacheLabel;
502
		if (parent.getItemType() == GraphItem.CONTAINER) {
502
	}
503
			((GraphContainer) parent).highlightNode(this);
503
504
		} else {
504
	public void setCacheLabel(boolean cacheLabel) {
505
			((Graph) parent).highlightNode(this);
505
		this.cacheLabel = cacheLabel;
506
		}
506
	}
507
	}
507
508
	*/
508
	IFigure getNodeFigure() {
509
509
		return this.nodeFigure;
510
	/**
510
	}
511
	 * Returns if the nodes adjacent to this node will be highlighted when this
511
512
	 * node is selected.
512
	public void setVisible(boolean visible) {
513
	 * 
513
		this.visible = visible;
514
	 * @return GraphModelNode
514
		this.getFigure().setVisible(visible);
515
	 */
515
		for (Iterator iterator2 = sourceConnections.iterator(); iterator2
516
	// @tag ADJACENT : Removed highlight adjacent
516
				.hasNext();) {
517
	/*
517
			GraphConnection connection = (GraphConnection) iterator2.next();
518
	public boolean isHighlightAdjacentNodes() {
518
			connection.setVisible(visible);
519
		return ZestStyles.checkStyle(nodeStyle, ZestStyles.NODES_HIGHLIGHT_ADJACENT);
519
		}
520
	}
520
521
	*/
521
		for (Iterator iterator2 = targetConnections.iterator(); iterator2
522
522
				.hasNext();) {
523
	/**
523
			GraphConnection connection = (GraphConnection) iterator2.next();
524
	 * Sets if the adjacent nodes to this one should be highlighted when this
524
			connection.setVisible(visible);
525
	 * node is selected.
525
		}
526
	 * 
526
	}
527
	 * @param highlightAdjacentNodes
527
528
	 *            The highlightAdjacentNodes to set.
528
	public boolean isVisible() {
529
	 */
529
		return visible;
530
	// @tag ADJACENT : Removed highlight adjacent
530
	}
531
	/*
531
532
	public void setHighlightAdjacentNodes(boolean highlightAdjacentNodes) {
532
	public int getStyle() {
533
		if (!highlightAdjacentNodes) {
533
		return super.getStyle() | this.getNodeStyle();
534
			this.nodeStyle |= ZestStyles.NODES_HIGHLIGHT_ADJACENT;
534
	}
535
			this.nodeStyle ^= ZestStyles.NODES_HIGHLIGHT_ADJACENT;
535
536
			return;
536
	/***************************************************************************
537
		}
537
	 * PRIVATE MEMBERS
538
		this.nodeStyle |= ZestStyles.NODES_HIGHLIGHT_ADJACENT;
538
	 **************************************************************************/
539
	}
539
540
	*/
540
	private IFigure fishEyeFigure = null;
541
541
	private boolean isFisheyeEnabled;
542
	public Color getBorderColor() {
542
543
		return borderColor;
543
	protected IFigure fishEye(boolean enable, boolean animate) {
544
	}
544
		if (isDisposed) {
545
545
			// If a fisheyed figure is still left on the canvas, we could get
546
	public int getBorderWidth() {
546
			// called once more after the dispose is called. Since we cleaned
547
		return borderWidth;
547
			// up everything on dispose, we can just return null here.
548
	}
548
			return null;
549
549
		}
550
	public void setBorderWidth(int width) {
550
		if (!checkStyle(ZestStyles.NODES_FISHEYE)) {
551
		this.borderWidth = width;
551
			return null;
552
		updateFigureForModel(nodeFigure);
552
		}
553
	}
553
		if (enable) {
554
554
			// Create the fish eye label
555
	public Font getFont() {
555
			fishEyeFigure = createFishEyeFigure();
556
		return font;
556
557
	}
557
			Rectangle rectangle = calculateFishEyeBounds();
558
558
559
	public void setFont(Font font) {
559
			if (rectangle == null) {
560
		this.font = font;
560
				return null;
561
		updateFigureForModel(nodeFigure);
561
			}
562
	}
562
563
563
			// Add the fisheye
564
	/*
564
			this.getGraphModel().fishEye(nodeFigure, fishEyeFigure, rectangle,
565
	 * (non-Javadoc)
565
					true);
566
	 * 
566
			if (fishEyeFigure != null) {
567
	 * @see org.eclipse.swt.widgets.Item#setText(java.lang.String)
567
				isFisheyeEnabled = true;
568
	 */
568
			}
569
	public void setText(String string) {
569
			return fishEyeFigure;
570
		if (string == null) {
570
571
			string = "";
571
		} else {
572
		}
572
			isFisheyeEnabled = false;
573
		super.setText(string);
573
			this.getGraphModel().removeFishEye(fishEyeFigure, nodeFigure,
574
574
					animate);
575
		if (nodeFigure != null) {
575
			return null;
576
			updateFigureForModel(this.nodeFigure);
576
		}
577
		}
577
	}
578
	}
578
579
579
	IContainer getParent() {
580
	/*
580
		return parent;
581
	 * (non-Javadoc)
581
	}
582
	 * 
582
583
	 * @see org.eclipse.swt.widgets.Item#setImage(org.eclipse.swt.graphics.Image)
583
	boolean isHighlighted() {
584
	 */
584
		return highlighted > 0;
585
	public void setImage(Image image) {
585
	}
586
		super.setImage(image);
586
587
		if (nodeFigure != null) {
587
	protected void updateFigureForModel(IFigure currentFigure) {
588
			updateFigureForModel(nodeFigure);
588
		if (currentFigure == null) {
589
		}
589
			return;
590
	}
590
		}
591
591
592
	/**
592
		if (!(currentFigure instanceof GraphLabel)) {
593
	 * Gets the graphModel that this node is contained in
593
			return;
594
	 * 
594
		}
595
	 * @return The graph model that this node is contained in
595
		GraphLabel figure = (GraphLabel) currentFigure;
596
	 */
596
		IFigure toolTip;
597
	public Graph getGraphModel() {
597
598
		return this.graph;
598
		if (!checkStyle(ZestStyles.NODES_HIDE_TEXT)
599
	}
599
				&& !figure.getText().equals(this.getText())) {
600
600
			figure.setText(this.getText());
601
	/**
601
		}
602
	 * @return the nodeStyle
602
		if (figure.getIcon() != getImage()) {
603
	 */
603
			figure.setIcon(getImage());
604
	public int getNodeStyle() {
604
		}
605
		return nodeStyle;
605
606
	}
606
		if (highlighted == HIGHLIGHT_ON) {
607
607
			figure.setForegroundColor(getForegroundColor());
608
	/**
608
			figure.setBackgroundColor(getHighlightColor());
609
	 * @param nodeStyle
609
			figure.setBorderColor(getBorderHighlightColor());
610
	 *            the nodeStyle to set
610
		} else {
611
	 */
611
			figure.setForegroundColor(getForegroundColor());
612
	public void setNodeStyle(int nodeStyle) {
612
			figure.setBackgroundColor(getBackgroundColor());
613
		this.nodeStyle = nodeStyle;
613
			figure.setBorderColor(getBorderColor());
614
		this.cacheLabel = ((this.nodeStyle & ZestStyles.NODES_CACHE_LABEL) > 0) ? true : false;
614
		}
615
	}
615
616
616
		figure.setBorderWidth(getBorderWidth());
617
	/*
617
618
	 * (non-Javadoc)
618
		if (figure.getFont() != getFont()) {
619
	 * 
619
			figure.setFont(getFont());
620
	 * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setSize(double,
620
		}
621
	 *      double)
621
622
	 */
622
		if (this.getTooltip() == null && hasCustomTooltip == false) {
623
	public void setSize(double width, double height) {
623
			// if we have a custom tooltip, don't try and create our own.
624
		if ((width != size.width) || (height != size.height)) {
624
			toolTip = new Label();
625
			size.width = (int) width;
625
			((Label) toolTip).setText(getText());
626
			size.height = (int) height;
626
		} else {
627
			refreshLocation();
627
			toolTip = this.getTooltip();
628
		}
628
		}
629
	}
629
		figure.setToolTip(toolTip);
630
630
631
	/*
631
		if (isFisheyeEnabled) {
632
	 * (non-Javadoc)
632
			IFigure newFisheyeFigure = createFishEyeFigure();
633
	 * 
633
			if (graph.replaceFishFigure(this.fishEyeFigure, newFisheyeFigure)) {
634
	 * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderHighlightColor()
634
				this.fishEyeFigure = newFisheyeFigure;
635
	 */
635
			}
636
	public Color getBorderHighlightColor() {
636
		}
637
		return borderHighlightColor;
637
		refreshBounds();
638
	}
638
	}
639
639
640
	public boolean cacheLabel() {
640
	protected IFigure createFigureForModel() {
641
		return this.cacheLabel;
641
		GraphNode node = this;
642
	}
642
		boolean cacheLabel = (this).cacheLabel();
643
643
		final GraphLabel label = new GraphLabel(node.getText(),
644
	public void setCacheLabel(boolean cacheLabel) {
644
				node.getImage(), cacheLabel);
645
		this.cacheLabel = cacheLabel;
645
		label.setFont(this.font);
646
	}
646
		if (checkStyle(ZestStyles.NODES_HIDE_TEXT)) {
647
647
			label.setText("");
648
	public IFigure getNodeFigure() {
648
		}
649
		return this.nodeFigure;
649
		updateFigureForModel(label);
650
	}
650
		label.addFigureListener(new FigureListener() {
651
651
			private Dimension previousSize = label.getBounds().getSize();
652
	public void setVisible(boolean visible) {
652
653
		// graph.addRemoveFigure(this, visible);
653
			public void figureMoved(IFigure source) {
654
		this.visible = visible;
654
				if (Animation.isAnimating() || getLayout().isMinimized()) {
655
		this.getFigure().setVisible(visible);
655
					return;
656
		List sConnections = (this).getSourceConnections();
656
				}
657
		List tConnections = (this).getTargetConnections();
657
				Rectangle newBounds = nodeFigure.getBounds();
658
		for (Iterator iterator2 = sConnections.iterator(); iterator2.hasNext();) {
658
				if (!newBounds.getSize().equals(previousSize)) {
659
			GraphConnection connection = (GraphConnection) iterator2.next();
659
					previousSize = newBounds.getSize();
660
			connection.setVisible(visible);
660
					if (size.width >= 0 && size.height >= 0) {
661
		}
661
						size = newBounds.getSize();
662
662
					}
663
		for (Iterator iterator2 = tConnections.iterator(); iterator2.hasNext();) {
663
					currentLocation = new PrecisionPoint(nodeFigure.getBounds()
664
			GraphConnection connection = (GraphConnection) iterator2.next();
664
							.getTopLeft());
665
			connection.setVisible(visible);
665
					parent.getLayoutContext().fireNodeResizedEvent(getLayout());
666
		}
666
				} else if (currentLocation.x != newBounds.x
667
	}
667
						|| currentLocation.y != newBounds.y) {
668
668
					currentLocation = new PrecisionPoint(nodeFigure.getBounds()
669
	public int getStyle() {
669
							.getTopLeft());
670
		return super.getStyle() | this.getNodeStyle();
670
					parent.getLayoutContext().fireNodeMovedEvent(getLayout());
671
	}
671
				}
672
672
			}
673
	/***************************************************************************
673
		});
674
	 * PRIVATE MEMBERS
674
		return label;
675
	 **************************************************************************/
675
	}
676
676
677
	private IFigure fishEyeFigure = null;
677
	private IFigure createFishEyeFigure() {
678
	private Font fishEyeFont = null;
678
		GraphNode node = this;
679
	private boolean isFisheyeEnabled;
679
		boolean cacheLabel = this.cacheLabel();
680
680
		GraphLabel label = new GraphLabel(node.getText(), node.getImage(),
681
	protected IFigure fishEye(boolean enable, boolean animate) {
681
				cacheLabel);
682
		if (isDisposed) {
682
683
			// If a fisheyed figure is still left on the canvas, we could get
683
		if (highlighted == HIGHLIGHT_ON) {
684
			// called once more after the dispose is called.  Since we cleaned
684
			label.setForegroundColor(getForegroundColor());
685
			// up everything on dispose, we can just return null here.
685
			label.setBackgroundColor(getHighlightColor());
686
			return null;
686
			label.setBorderColor(getBorderHighlightColor());
687
		}
687
		} else {
688
		if (!checkStyle(ZestStyles.NODES_FISHEYE)) {
688
			label.setForegroundColor(getForegroundColor());
689
			return null;
689
			label.setBackgroundColor(getBackgroundColor());
690
		}
690
			label.setBorderColor(getBorderColor());
691
		if (enable) {
691
		}
692
			// Create the fish eye label
692
693
			fishEyeFigure = createFishEyeFigure();
693
		label.setBorderWidth(getBorderWidth());
694
694
		label.setFont(getFont());
695
			// Get the current Bounds
695
696
			Rectangle rectangle = nodeFigure.getBounds().getCopy();
696
		return label;
697
697
	}
698
			// Calculate how much we have to expand the current bounds to get to the new bounds
698
699
			Dimension newSize = fishEyeFigure.getPreferredSize();
699
	private Rectangle calculateFishEyeBounds() {
700
			Rectangle currentSize = rectangle.getCopy();
700
		// Get the current Bounds
701
			nodeFigure.translateToAbsolute(currentSize);
701
		Rectangle rectangle = nodeFigure.getBounds().getCopy();
702
			int expandedH = (newSize.height - currentSize.height) / 2 + 1;
702
703
			int expandedW = (newSize.width - currentSize.width) / 2 + 1;
703
		// Calculate how much we have to expand the current bounds to get to the
704
			Dimension expandAmount = new Dimension(expandedW, expandedH);
704
		// new bounds
705
			nodeFigure.translateToAbsolute(rectangle);
705
		Dimension newSize = fishEyeFigure.getPreferredSize();
706
			rectangle.expand(new Insets(expandAmount.height, expandAmount.width, expandAmount.height, expandAmount.width));
706
		Rectangle currentSize = rectangle.getCopy();
707
			if (expandedH <= 0 && expandedW <= 0) {
707
		nodeFigure.translateToAbsolute(currentSize);
708
				return null;
708
		int expandedH = Math.max((newSize.height - currentSize.height) / 2 + 1,
709
			}
709
				0);
710
710
		int expandedW = Math
711
			FontData fontData = Display.getCurrent().getSystemFont().getFontData()[0];
711
				.max((newSize.width - currentSize.width) / 2 + 1, 0);
712
			fontData.height = 12;
712
		Dimension expandAmount = new Dimension(expandedW, expandedH);
713
			fishEyeFont = new Font(Display.getCurrent(), fontData);
713
		nodeFigure.translateToAbsolute(rectangle);
714
			fishEyeFigure.setFont(fishEyeFont);
714
		rectangle.expand(new Insets(expandAmount.height, expandAmount.width,
715
715
				expandAmount.height, expandAmount.width));
716
			//Add the fisheye
716
		if (expandedH <= 0 && expandedW <= 0) {
717
			this.getGraphModel().fishEye(nodeFigure, fishEyeFigure, rectangle, true);
717
			return null;
718
			if (fishEyeFigure != null) {
718
		}
719
				isFisheyeEnabled = true;
719
		return rectangle;
720
			}
720
	}
721
			return fishEyeFigure;
721
722
722
	void addSourceConnection(GraphConnection connection) {
723
		} else {
723
		this.sourceConnections.add(connection);
724
			// Remove the fisheye and dispose the font
724
	}
725
			this.getGraphModel().removeFishEye(fishEyeFigure, nodeFigure, animate);
725
726
			if (fishEyeFont != null) {
726
	void addTargetConnection(GraphConnection connection) {
727
				this.fishEyeFont.dispose();
727
		this.targetConnections.add(connection);
728
				this.fishEyeFont = null;
728
	}
729
			}
729
730
			isFisheyeEnabled = false;
730
	void removeSourceConnection(GraphConnection connection) {
731
			return null;
731
		this.sourceConnections.remove(connection);
732
		}
732
	}
733
	}
733
734
734
	void removeTargetConnection(GraphConnection connection) {
735
	IContainer getParent() {
735
		this.targetConnections.remove(connection);
736
		return parent;
736
	}
737
	}
737
738
738
	/**
739
	boolean isHighlighted() {
739
	 * Sets the node as selected.
740
		return highlighted > 0;
740
	 */
741
	}
741
	void setSelected(boolean selected) {
742
742
		if (selected = isSelected()) {
743
	void invokeLayoutListeners(LayoutConstraint constraint) {
743
			return;
744
		graph.invokeConstraintAdapters(this, constraint);
744
		}
745
	}
745
		if (selected) {
746
746
			highlight();
747
	protected void updateFigureForModel(IFigure currentFigure) {
747
		} else {
748
		if (currentFigure == null) {
748
			unhighlight();
749
			return;
749
		}
750
		}
750
		this.selected = selected;
751
751
	}
752
		if (!(currentFigure instanceof GraphLabel)) {
752
753
			return;
753
	/*
754
		}
754
	 * (non-Javadoc)
755
		GraphLabel figure = (GraphLabel) currentFigure;
755
	 * 
756
		IFigure toolTip;
756
	 * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType()
757
757
	 */
758
		if (!checkStyle(ZestStyles.NODES_HIDE_TEXT)) {
758
	public int getItemType() {
759
			figure.setText(this.getText());
759
		return NODE;
760
		}
760
	}
761
		figure.setIcon(getImage());
761
762
762
	/**
763
		if (highlighted == HIGHLIGHT_ON) {
763
	 * @noreference This method is not intended to be referenced by clients.
764
			figure.setForegroundColor(getForegroundColor());
764
	 */
765
			figure.setBackgroundColor(getHighlightColor());
765
	public IFigure getFigure() {
766
			figure.setBorderColor(getBorderHighlightColor());
766
		if (this.nodeFigure == null) {
767
		} else {
767
			initFigure();
768
			figure.setForegroundColor(getForegroundColor());
768
		}
769
			figure.setBackgroundColor(getBackgroundColor());
769
		return this.getNodeFigure();
770
			figure.setBorderColor(getBorderColor());
770
	}
771
		}
771
772
772
	private InternalNodeLayout layout;
773
		figure.setBorderWidth(getBorderWidth());
773
774
774
	/**
775
		figure.setFont(getFont());
775
	 * @noreference This method is not intended to be referenced by clients.
776
776
	 */
777
		if (this.getTooltip() == null && hasCustomTooltip == false) {
777
	public InternalNodeLayout getLayout() {
778
			// if we have a custom tooltip, don't try and create our own.
778
		if (layout == null) {
779
			toolTip = new Label();
779
			layout = new InternalNodeLayout(this);
780
			((Label) toolTip).setText(getText());
780
		}
781
		} else {
781
		return layout;
782
			toolTip = this.getTooltip();
782
	}
783
		}
783
784
		figure.setToolTip(toolTip);
784
	void applyLayoutChanges() {
785
785
		if (layout != null) {
786
		refreshLocation();
786
			layout.applyLayout();
787
787
		}
788
		if (isFisheyeEnabled) {
788
	}
789
			IFigure newFisheyeFigure = createFishEyeFigure();
789
}
790
			if (graph.replaceFishFigure(this.fishEyeFigure, newFisheyeFigure)) {
791
				this.fishEyeFigure = newFisheyeFigure;
792
			}
793
		}
794
	}
795
796
	protected IFigure createFigureForModel() {
797
		GraphNode node = this;
798
		boolean cacheLabel = (this).cacheLabel();
799
		GraphLabel label = new GraphLabel(node.getText(), node.getImage(), cacheLabel);
800
		label.setFont(this.font);
801
		if (checkStyle(ZestStyles.NODES_HIDE_TEXT)) {
802
			label.setText("");
803
		}
804
		updateFigureForModel(label);
805
		return label;
806
	}
807
808
	private IFigure createFishEyeFigure() {
809
		GraphNode node = this;
810
		boolean cacheLabel = this.cacheLabel();
811
		GraphLabel label = new GraphLabel(node.getText(), node.getImage(), cacheLabel);
812
813
		if (!checkStyle(ZestStyles.NODES_HIDE_TEXT)) {
814
			label.setText(this.getText());
815
		}
816
		label.setIcon(getImage());
817
818
		// @tag TODO: Add border and foreground colours to highlight
819
		// (this.borderColor)
820
		if (highlighted == HIGHLIGHT_ON) {
821
			label.setForegroundColor(getForegroundColor());
822
			label.setBackgroundColor(getHighlightColor());
823
		} else {
824
			label.setForegroundColor(getForegroundColor());
825
			label.setBackgroundColor(getBackgroundColor());
826
		}
827
828
		label.setFont(getFont());
829
		return label;
830
	}
831
832
	public boolean isVisible() {
833
		return visible;
834
	}
835
836
	void addSourceConnection(GraphConnection connection) {
837
		this.sourceConnections.add(connection);
838
	}
839
840
	void addTargetConnection(GraphConnection connection) {
841
		this.targetConnections.add(connection);
842
	}
843
844
	void removeSourceConnection(GraphConnection connection) {
845
		this.sourceConnections.remove(connection);
846
	}
847
848
	void removeTargetConnection(GraphConnection connection) {
849
		this.targetConnections.remove(connection);
850
	}
851
852
	/**
853
	 * Sets the node as selected.
854
	 */
855
	void setSelected(boolean selected) {
856
		if (selected = isSelected()) {
857
			return;
858
		}
859
		if (selected) {
860
			highlight();
861
		} else {
862
			unhighlight();
863
		}
864
		this.selected = selected;
865
	}
866
867
	/*
868
	 * (non-Javadoc)
869
	 * 
870
	 * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType()
871
	 */
872
	public int getItemType() {
873
		return NODE;
874
	}
875
876
	class LayoutGraphNode implements LayoutEntity {
877
		Object layoutInformation = null;
878
879
		public double getHeightInLayout() {
880
			return getSize().height;
881
		}
882
883
		public Object getLayoutInformation() {
884
			return layoutInformation;
885
		}
886
887
		public String toString() {
888
			return getText();
889
		}
890
891
		public double getWidthInLayout() {
892
			return getSize().width;
893
		}
894
895
		public double getXInLayout() {
896
			return getLocation().x;
897
		}
898
899
		public double getYInLayout() {
900
			return getLocation().y;
901
		}
902
903
		public void populateLayoutConstraint(LayoutConstraint constraint) {
904
			invokeLayoutListeners(constraint);
905
		}
906
907
		public void setLayoutInformation(Object internalEntity) {
908
			this.layoutInformation = internalEntity;
909
910
		}
911
912
		public void setLocationInLayout(double x, double y) {
913
			setLocation(x, y);
914
		}
915
916
		public void setSizeInLayout(double width, double height) {
917
			setSize(width, height);
918
		}
919
920
		/**
921
		 * Compares two nodes.
922
		 */
923
		public int compareTo(Object otherNode) {
924
			int rv = 0;
925
			if (otherNode instanceof GraphNode) {
926
				GraphNode node = (GraphNode) otherNode;
927
				if (getText() != null) {
928
					rv = getText().compareTo(node.getText());
929
				}
930
			}
931
			return rv;
932
		}
933
934
		public Object getGraphData() {
935
			return GraphNode.this;
936
		}
937
938
		public void setGraphData(Object o) {
939
			// TODO Auto-generated method stub
940
941
		}
942
943
	}
944
945
	IFigure getFigure() {
946
		if (this.nodeFigure == null) {
947
			initFigure();
948
		}
949
		return this.getNodeFigure();
950
	}
951
952
	void paint() {
953
954
	}
955
}
(-)src/org/eclipse/zest/core/widgets/IContainer.java (-64 / +93 lines)
Lines 1-64 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * All rights reserved. This program and the accompanying materials
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * which accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 * 
8
 * Contributors:
8
 * Contributors: The Chisel Group, University of Victoria 
9
 *     The Chisel Group, University of Victoria
9
 *               Mateusz Matela
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.core.widgets;
11
package org.eclipse.zest.core.widgets;
12
12
13
import java.util.List;
13
import java.util.List;
14
14
15
import org.eclipse.zest.layouts.LayoutAlgorithm;
15
import org.eclipse.draw2d.IFigure;
16
16
import org.eclipse.swt.widgets.Widget;
17
/**
17
import org.eclipse.zest.layouts.LayoutAlgorithm;
18
 * This interface describes all Zest components that are Containers. This is an internal interface
18
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
19
 * and thus should not be used outside of Zest.  Implementors of this interface must include the 
19
20
 * following two methods
20
/**
21
 *   o addNode(GraphNode)
21
 * @noimplement This interface is not intended to be implemented by clients.
22
 *   o addNode(GraphContainer)
22
 * 
23
 *   
23
 */
24
 * These are not actually listed here because Java does not allow protected methods in
24
public interface IContainer {
25
 * interfaces.
25
26
 * 
26
	public abstract Graph getGraph();
27
 * @author Ian Bull
27
28
 */
28
	/**
29
public interface IContainer {
29
	 * @since 2.0
30
30
	 */
31
	public Graph getGraph();
31
	public Widget getItem();
32
32
33
	// All implementers must include this method
33
	public abstract List getNodes();
34
	/* protected void addNode(GraphNode node); */
34
35
35
	/**
36
	// All implementers must include this method
36
	 * Returns list of connections laying inside this container. Only
37
	/* protected void addNode(GraphContainer container); */
37
	 * connections which both source and target nodes lay directly in this
38
38
	 * container are returned.
39
	public int getItemType();
39
	 * 
40
40
	 * @return
41
	/**
41
	 * @since 2.0
42
	 * Re-applies the current layout algorithm
42
	 */
43
	 */
43
	public abstract List getConnections();
44
	public void applyLayout();
44
45
45
	/**
46
	/**
46
	 * @noreference This method is not intended to be referenced by clients.
47
	 * Sets the LayoutAlgorithm for this container and optionally applies it.
47
	 * @param graphNode
48
	 *  
48
	 */
49
	 * @param algorithm The layout algorithm to set
49
	public abstract void addNode(GraphNode graphNode);
50
	 * @param applyLayout 
50
51
	 */
51
	/**
52
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean applyLayout);
52
	 * @noreference This method is not intended to be referenced by clients.
53
53
	 * @param figure
54
	public List getNodes();
54
	 */
55
55
	public abstract void addSubgraphFigure(IFigure figure);
56
	/* protected void highlightNode(GraphNode node); */
56
57
57
	public abstract int getItemType();
58
	/* protected void highlightNode(GraphContainer container);*/
58
59
59
	/**
60
	/* protected void unhighlightNode(GraphNode node); */
60
	 * @return
61
61
	 * @since 2.0
62
	/* protected void unhighlightNode(GraphContainer container);*/
62
	 */
63
63
	public abstract DisplayIndependentRectangle getLayoutBounds();
64
} // end of IContainer
64
65
	/**
66
	 * @noreference This method is not intended to be referenced by clients.
67
	 * @return
68
	 */
69
	public abstract InternalLayoutContext getLayoutContext();
70
71
	public void applyLayout();
72
73
	public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean apply);
74
75
	/**
76
	 * Takes a list of connections and returns only those which source and
77
	 * target nodes lay directly in this container.
78
	 * 
79
	 * @param connections
80
	 *            list of GraphConnection to filter
81
	 * @return filtered list
82
	 */
83
	// protected List filterConnections(List connections) {
84
	// List result = new ArrayList();
85
	// for (Iterator iterator = connections.iterator(); iterator.hasNext();) {
86
	// GraphConnection connection = (GraphConnection) iterator.next();
87
	// if (connection.getSource().getParent() == this &&
88
	// connection.getDestination().getParent() == this)
89
	// result.add(connection);
90
	// }
91
	// return result;
92
	// }
93
}
(-)src/org/eclipse/zest/core/widgets/InternalLayoutContext.java (+540 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.HashSet;
15
import java.util.Iterator;
16
import java.util.List;
17
18
import org.eclipse.draw2d.Animation;
19
import org.eclipse.zest.layouts.LayoutAlgorithm;
20
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
21
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
22
import org.eclipse.zest.layouts.interfaces.ContextListener;
23
import org.eclipse.zest.layouts.interfaces.EntityLayout;
24
import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager;
25
import org.eclipse.zest.layouts.interfaces.GraphStructureListener;
26
import org.eclipse.zest.layouts.interfaces.LayoutContext;
27
import org.eclipse.zest.layouts.interfaces.LayoutListener;
28
import org.eclipse.zest.layouts.interfaces.NodeLayout;
29
import org.eclipse.zest.layouts.interfaces.PruningListener;
30
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
31
32
class InternalLayoutContext implements LayoutContext {
33
34
	final IContainer container;
35
	private final List filters = new ArrayList();
36
	private final List contextListeners = new ArrayList();
37
	private final List graphStructureListeners = new ArrayList();
38
	private final List layoutListeners = new ArrayList();
39
	private final List pruningListeners = new ArrayList();
40
	private LayoutAlgorithm mainAlgorithm;
41
	private LayoutAlgorithm layoutAlgorithm;
42
	private ExpandCollapseManager expandCollapseManager;
43
	private SubgraphFactory subgraphFactory = new DefaultSubgraph.DefaultSubgraphFactory();
44
	private final HashSet subgraphs = new HashSet();
45
	private boolean eventsOn = true;
46
	private boolean backgorundLayoutEnabled = true;
47
	private boolean externalLayoutInvocation = false;
48
49
	/**
50
	 * @param graph
51
	 *            the graph owning this context
52
	 */
53
	InternalLayoutContext(Graph graph) {
54
		this.container = graph;
55
	}
56
57
	InternalLayoutContext(GraphContainer container) {
58
		this.container = container;
59
	}
60
61
	public void addContextListener(ContextListener listener) {
62
		contextListeners.add(listener);
63
	}
64
65
	public void addGraphStructureListener(GraphStructureListener listener) {
66
		graphStructureListeners.add(listener);
67
	}
68
69
	public void addLayoutListener(LayoutListener listener) {
70
		layoutListeners.add(listener);
71
	}
72
73
	public void addPruningListener(PruningListener listener) {
74
		pruningListeners.add(listener);
75
	}
76
77
	public SubgraphLayout createSubgraph(NodeLayout[] nodes) {
78
		checkChangesAllowed();
79
		InternalNodeLayout[] internalNodes = new InternalNodeLayout[nodes.length];
80
		for (int i = 0; i < nodes.length; i++) {
81
			internalNodes[i] = (InternalNodeLayout) nodes[i];
82
		}
83
		SubgraphLayout subgraph = subgraphFactory.createSubgraph(internalNodes,
84
				this);
85
		subgraphs.add(subgraph);
86
		return subgraph;
87
	}
88
89
	void removeSubgrah(DefaultSubgraph subgraph) {
90
		subgraphs.remove(subgraph);
91
	}
92
93
	public void flushChanges(boolean animationHint) {
94
		// TODO support for asynchronous call
95
		if (!container.getGraph().isVisible() && animationHint) {
96
			return;
97
		}
98
		eventsOn = false;
99
		if (animationHint) {
100
			Animation.markBegin();
101
		}
102
		for (Iterator iterator = container.getNodes().iterator(); iterator
103
				.hasNext();) {
104
			GraphNode node = (GraphNode) iterator.next();
105
			node.applyLayoutChanges();
106
		}
107
		for (Iterator iterator = container.getConnections().iterator(); iterator
108
				.hasNext();) {
109
			GraphConnection connection = (GraphConnection) iterator.next();
110
			connection.applyLayoutChanges();
111
		}
112
		for (Iterator iterator = subgraphs.iterator(); iterator.hasNext();) {
113
			DefaultSubgraph subgraph = (DefaultSubgraph) iterator.next();
114
			subgraph.applyLayoutChanges();
115
		}
116
		if (animationHint) {
117
			Animation.run(Graph.ANIMATION_TIME);
118
		}
119
		eventsOn = true;
120
	}
121
122
	public DisplayIndependentRectangle getBounds() {
123
		DisplayIndependentRectangle result = new DisplayIndependentRectangle(
124
				container.getLayoutBounds());
125
		result.width -= 20;
126
		result.height -= 20;
127
		return result;
128
	}
129
130
	public LayoutAlgorithm getMainLayoutAlgorithm() {
131
		return mainAlgorithm;
132
	}
133
134
	public ExpandCollapseManager getExpandCollapseManager() {
135
		return expandCollapseManager;
136
	}
137
138
	public NodeLayout[] getNodes() {
139
		ArrayList result = new ArrayList();
140
		for (Iterator iterator = this.container.getNodes().iterator(); iterator
141
				.hasNext();) {
142
			GraphNode node = (GraphNode) iterator.next();
143
			if (!isLayoutItemFiltered(node)) {
144
				result.add(node.getLayout());
145
			}
146
		}
147
		return (NodeLayout[]) result.toArray(new NodeLayout[result.size()]);
148
	}
149
150
	public EntityLayout[] getEntities() {
151
		HashSet addedSubgraphs = new HashSet();
152
		ArrayList result = new ArrayList();
153
		for (Iterator iterator = this.container.getNodes().iterator(); iterator
154
				.hasNext();) {
155
			GraphNode node = (GraphNode) iterator.next();
156
			if (!isLayoutItemFiltered(node)) {
157
				InternalNodeLayout nodeLayout = node.getLayout();
158
				if (!nodeLayout.isPruned()) {
159
					result.add(nodeLayout);
160
				} else {
161
					SubgraphLayout subgraph = nodeLayout.getSubgraph();
162
					if (subgraph.isGraphEntity()
163
							&& !addedSubgraphs.contains(subgraph)) {
164
						result.add(subgraph);
165
						addedSubgraphs.add(subgraph);
166
					}
167
				}
168
			}
169
		}
170
		return (EntityLayout[]) result.toArray(new EntityLayout[result.size()]);
171
	}
172
173
	public SubgraphLayout[] getSubgraphs() {
174
		SubgraphLayout[] result = new SubgraphLayout[subgraphs.size()];
175
		int subgraphCount = 0;
176
		for (Iterator iterator = subgraphs.iterator(); iterator.hasNext();) {
177
			SubgraphLayout subgraph = (SubgraphLayout) iterator.next();
178
			NodeLayout[] nodes = subgraph.getNodes();
179
			for (int i = 0; i < nodes.length; i++) {
180
				if (!isLayoutItemFiltered(((InternalNodeLayout) nodes[i])
181
						.getNode())) {
182
					result[subgraphCount++] = subgraph;
183
					break;
184
				}
185
			}
186
		}
187
		if (subgraphCount == subgraphs.size()) {
188
			return result;
189
		} else {
190
			SubgraphLayout[] result2 = new SubgraphLayout[subgraphCount];
191
			System.arraycopy(result, 0, result2, 0, subgraphCount);
192
			return result2;
193
		}
194
	}
195
196
	public boolean isBoundsExpandable() {
197
		return false;
198
	}
199
200
	public boolean isBackgroundLayoutEnabled() {
201
		return backgorundLayoutEnabled;
202
	}
203
204
	void setBackgroundLayoutEnabled(boolean enabled) {
205
		if (this.backgorundLayoutEnabled != enabled) {
206
			this.backgorundLayoutEnabled = enabled;
207
			fireBackgroundEnableChangedEvent();
208
		}
209
	}
210
211
	public boolean isPruningEnabled() {
212
		return expandCollapseManager != null;
213
	}
214
215
	public void removeContextListener(ContextListener listener) {
216
		contextListeners.remove(listener);
217
	}
218
219
	public void removeGraphStructureListener(GraphStructureListener listener) {
220
		graphStructureListeners.remove(listener);
221
	}
222
223
	public void removeLayoutListener(LayoutListener listener) {
224
		layoutListeners.remove(listener);
225
	}
226
227
	public void removePruningListener(PruningListener listener) {
228
		pruningListeners.remove(listener);
229
	}
230
231
	public void setMainLayoutAlgorithm(LayoutAlgorithm algorithm) {
232
		mainAlgorithm = algorithm;
233
	}
234
235
	public void setExpandCollapseManager(
236
			ExpandCollapseManager expandCollapseManager) {
237
		this.expandCollapseManager = expandCollapseManager;
238
		expandCollapseManager.initExpansion(this);
239
	}
240
241
	public ConnectionLayout[] getConnections() {
242
		List connections = container.getConnections();
243
		ConnectionLayout[] result = new ConnectionLayout[connections.size()];
244
		int i = 0;
245
		for (Iterator iterator = connections.iterator(); iterator.hasNext();) {
246
			GraphConnection connection = (GraphConnection) iterator.next();
247
			if (!isLayoutItemFiltered(connection)) {
248
				result[i++] = connection.getLayout();
249
			}
250
		}
251
		if (i == result.length) {
252
			return result;
253
		}
254
		ConnectionLayout[] result2 = new ConnectionLayout[i];
255
		System.arraycopy(result, 0, result2, 0, i);
256
		return result2;
257
	}
258
259
	public ConnectionLayout[] getConnections(EntityLayout source,
260
			EntityLayout target) {
261
		ArrayList result = new ArrayList();
262
263
		ArrayList sourcesList = new ArrayList();
264
		if (source instanceof NodeLayout) {
265
			sourcesList.add(source);
266
		}
267
		if (source instanceof SubgraphLayout) {
268
			sourcesList.addAll(Arrays.asList(((SubgraphLayout) source)
269
					.getNodes()));
270
		}
271
272
		HashSet targets = new HashSet();
273
		if (target instanceof NodeLayout) {
274
			targets.add(target);
275
		}
276
		if (target instanceof SubgraphLayout) {
277
			targets.addAll(Arrays.asList(((SubgraphLayout) target).getNodes()));
278
		}
279
280
		for (Iterator iterator = sourcesList.iterator(); iterator.hasNext();) {
281
			NodeLayout source2 = (NodeLayout) iterator.next();
282
			ConnectionLayout[] outgoingConnections = source2
283
					.getOutgoingConnections();
284
			for (int i = 0; i < outgoingConnections.length; i++) {
285
				ConnectionLayout connection = outgoingConnections[i];
286
				if ((connection.getSource() == source2 && targets
287
						.contains(connection.getTarget()))
288
						|| (connection.getTarget() == source2 && targets
289
								.contains(connection.getSource()))) {
290
					result.add(connection);
291
				}
292
			}
293
294
		}
295
		return (ConnectionLayout[]) result.toArray(new ConnectionLayout[result
296
				.size()]);
297
	}
298
299
	void addFilter(LayoutFilter filter) {
300
		filters.add(filter);
301
	}
302
303
	void removeFilter(LayoutFilter filter) {
304
		filters.remove(filter);
305
	}
306
307
	boolean isLayoutItemFiltered(GraphItem item) {
308
		for (Iterator it = filters.iterator(); it.hasNext();) {
309
			LayoutFilter filter = (LayoutFilter) it.next();
310
			if (filter.isObjectFiltered(item)) {
311
				return true;
312
			}
313
		}
314
		return false;
315
	}
316
317
	void setExpanded(NodeLayout node, boolean expanded) {
318
		externalLayoutInvocation = true;
319
		if (expandCollapseManager != null) {
320
			expandCollapseManager.setExpanded(this, node, expanded);
321
		}
322
		externalLayoutInvocation = false;
323
	}
324
325
	boolean canExpand(NodeLayout node) {
326
		return expandCollapseManager != null
327
				&& expandCollapseManager.canExpand(this, node);
328
	}
329
330
	boolean canCollapse(NodeLayout node) {
331
		return expandCollapseManager != null
332
				&& expandCollapseManager.canCollapse(this, node);
333
	}
334
335
	void setSubgraphFactory(SubgraphFactory factory) {
336
		subgraphFactory = factory;
337
	}
338
339
	SubgraphFactory getSubgraphFactory() {
340
		return subgraphFactory;
341
	}
342
343
	void applyMainAlgorithm() {
344
		if (backgorundLayoutEnabled && mainAlgorithm != null) {
345
			mainAlgorithm.applyLayout(true);
346
			flushChanges(false);
347
		}
348
	}
349
350
	/**
351
	 * Sets layout algorithm for this context. It differs from
352
	 * {@link #setMainLayoutAlgorithm(LayoutAlgorithm) main algorithm} in that
353
	 * it's always used when {@link #applyLayoutAlgorithm(boolean)} and not
354
	 * after firing of events.
355
	 */
356
	void setLayoutAlgorithm(LayoutAlgorithm algorithm) {
357
		if (this.layoutAlgorithm != null) {
358
			this.layoutAlgorithm.setLayoutContext(null);
359
		}
360
		this.layoutAlgorithm = algorithm;
361
		this.layoutAlgorithm.setLayoutContext(this);
362
	}
363
364
	LayoutAlgorithm getLayoutAlgorithm() {
365
		return layoutAlgorithm;
366
	}
367
368
	void applyLayout(boolean clean) {
369
		if (layoutAlgorithm != null) {
370
			externalLayoutInvocation = true;
371
			layoutAlgorithm.applyLayout(clean);
372
			externalLayoutInvocation = false;
373
		}
374
	}
375
376
	void checkChangesAllowed() {
377
		if (!backgorundLayoutEnabled && !externalLayoutInvocation) {
378
			throw new RuntimeException(
379
					"Layout not allowed to perform changes in layout context!");
380
		}
381
	}
382
383
	void fireNodeAddedEvent(NodeLayout node) {
384
		boolean intercepted = !eventsOn;
385
		GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners
386
				.toArray(new GraphStructureListener[graphStructureListeners
387
						.size()]);
388
		for (int i = 0; i < listeners.length && !intercepted; i++) {
389
			intercepted = listeners[i].nodeAdded(this, node);
390
		}
391
		if (!intercepted) {
392
			applyMainAlgorithm();
393
		}
394
	}
395
396
	void fireNodeRemovedEvent(NodeLayout node) {
397
		boolean intercepted = !eventsOn;
398
		GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners
399
				.toArray(new GraphStructureListener[graphStructureListeners
400
						.size()]);
401
		for (int i = 0; i < listeners.length && !intercepted; i++) {
402
			intercepted = listeners[i].nodeRemoved(this, node);
403
		}
404
		if (!intercepted) {
405
			applyMainAlgorithm();
406
		}
407
	}
408
409
	void fireConnectionAddedEvent(ConnectionLayout connection) {
410
		InternalLayoutContext sourceContext = ((InternalNodeLayout) connection
411
				.getSource()).getOwnerLayoutContext();
412
		InternalLayoutContext targetContext = ((InternalNodeLayout) connection
413
				.getTarget()).getOwnerLayoutContext();
414
		if (sourceContext != targetContext) {
415
			return;
416
		}
417
		if (sourceContext == this) {
418
			boolean intercepted = !eventsOn;
419
			GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners
420
					.toArray(new GraphStructureListener[graphStructureListeners
421
							.size()]);
422
			for (int i = 0; i < listeners.length && !intercepted; i++) {
423
				intercepted = listeners[i].connectionAdded(this, connection);
424
			}
425
			if (!intercepted) {
426
				applyMainAlgorithm();
427
			}
428
		} else {
429
			sourceContext.fireConnectionAddedEvent(connection);
430
		}
431
	}
432
433
	void fireConnectionRemovedEvent(ConnectionLayout connection) {
434
		InternalLayoutContext sourceContext = ((InternalNodeLayout) connection
435
				.getSource()).getOwnerLayoutContext();
436
		InternalLayoutContext targetContext = ((InternalNodeLayout) connection
437
				.getTarget()).getOwnerLayoutContext();
438
		if (sourceContext != targetContext) {
439
			return;
440
		}
441
		if (sourceContext == this) {
442
			boolean intercepted = !eventsOn;
443
			GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners
444
					.toArray(new GraphStructureListener[graphStructureListeners
445
							.size()]);
446
			for (int i = 0; i < listeners.length && !intercepted; i++) {
447
				intercepted = listeners[i].connectionRemoved(this, connection);
448
			}
449
			if (!intercepted) {
450
				applyMainAlgorithm();
451
			}
452
		} else {
453
			sourceContext.fireConnectionAddedEvent(connection);
454
		}
455
	}
456
457
	void fireBoundsChangedEvent() {
458
		boolean intercepted = !eventsOn;
459
		ContextListener[] listeners = (ContextListener[]) contextListeners
460
				.toArray(new ContextListener[contextListeners.size()]);
461
		for (int i = 0; i < listeners.length && !intercepted; i++) {
462
			intercepted = listeners[i].boundsChanged(this);
463
		}
464
		if (!intercepted) {
465
			applyMainAlgorithm();
466
		}
467
	}
468
469
	void fireBackgroundEnableChangedEvent() {
470
		ContextListener[] listeners = (ContextListener[]) contextListeners
471
				.toArray(new ContextListener[contextListeners.size()]);
472
		for (int i = 0; i < listeners.length; i++) {
473
			listeners[i].backgroundEnableChanged(this);
474
		}
475
	}
476
477
	void fireNodeMovedEvent(InternalNodeLayout node) {
478
		if (eventsOn) {
479
			node.refreshLocation();
480
		}
481
		boolean intercepted = !eventsOn;
482
		LayoutListener[] listeners = (LayoutListener[]) layoutListeners
483
				.toArray(new LayoutListener[layoutListeners.size()]);
484
		node.setLocation(node.getNode().getLocation().x, node.getNode()
485
				.getLocation().y);
486
		for (int i = 0; i < listeners.length && !intercepted; i++) {
487
			intercepted = listeners[i].nodeMoved(this, node);
488
		}
489
		if (!intercepted) {
490
			applyMainAlgorithm();
491
		}
492
	}
493
494
	void fireNodeResizedEvent(InternalNodeLayout node) {
495
		if (eventsOn) {
496
			node.refreshSize();
497
			node.refreshLocation();
498
		}
499
		boolean intercepted = !eventsOn;
500
		LayoutListener[] listeners = (LayoutListener[]) layoutListeners
501
				.toArray(new LayoutListener[layoutListeners.size()]);
502
		for (int i = 0; i < listeners.length && !intercepted; i++) {
503
			intercepted = listeners[i].nodeResized(this, node);
504
		}
505
		if (!intercepted) {
506
			applyMainAlgorithm();
507
		}
508
	}
509
510
	void fireSubgraphMovedEvent(DefaultSubgraph subgraph) {
511
		if (eventsOn) {
512
			subgraph.refreshLocation();
513
		}
514
		boolean intercepted = !eventsOn;
515
		LayoutListener[] listeners = (LayoutListener[]) layoutListeners
516
				.toArray(new LayoutListener[layoutListeners.size()]);
517
		for (int i = 0; i < listeners.length && !intercepted; i++) {
518
			intercepted = listeners[i].subgraphMoved(this, subgraph);
519
		}
520
		if (!intercepted) {
521
			applyMainAlgorithm();
522
		}
523
	}
524
525
	void fireSubgraphResizedEvent(DefaultSubgraph subgraph) {
526
		if (eventsOn) {
527
			subgraph.refreshSize();
528
			subgraph.refreshLocation();
529
		}
530
		boolean intercepted = !eventsOn;
531
		LayoutListener[] listeners = (LayoutListener[]) layoutListeners
532
				.toArray(new LayoutListener[layoutListeners.size()]);
533
		for (int i = 0; i < listeners.length && !intercepted; i++) {
534
			intercepted = listeners[i].subgraphResized(this, subgraph);
535
		}
536
		if (!intercepted) {
537
			applyMainAlgorithm();
538
		}
539
	}
540
}
(-)src/org/eclipse/zest/core/widgets/InternalNodeLayout.java (+329 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import java.util.ArrayList;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.Iterator;
16
17
import org.eclipse.draw2d.FigureListener;
18
import org.eclipse.draw2d.IFigure;
19
import org.eclipse.draw2d.geometry.Dimension;
20
import org.eclipse.draw2d.geometry.Point;
21
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
22
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
23
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
24
import org.eclipse.zest.layouts.interfaces.EntityLayout;
25
import org.eclipse.zest.layouts.interfaces.NodeLayout;
26
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
27
28
class InternalNodeLayout implements NodeLayout {
29
30
	/**
31
	 * This listener is added to nodes' figures as a workaround for the problem
32
	 * of minimized nodes leaving single on the graph pixels when zoomed out
33
	 */
34
	private final static FigureListener figureListener = new FigureListener() {
35
		public void figureMoved(IFigure source) {
36
			// hide figures of minimized nodes
37
			GraphNode node = (GraphNode) figureToNode.get(source);
38
			if (node.getLayout().isMinimized() && source.getSize().equals(0, 0)) {
39
				source.setVisible(false);
40
			} else {
41
				source.setVisible(node.isVisible());
42
			}
43
		}
44
	};
45
	private final static HashMap figureToNode = new HashMap();
46
47
	private DisplayIndependentPoint location;
48
	private DisplayIndependentDimension size;
49
	private boolean minimized = false;
50
	private final GraphNode node;
51
	private final InternalLayoutContext ownerLayoutContext;
52
	private DefaultSubgraph subgraph;
53
	private boolean isDisposed = false;
54
55
	public InternalNodeLayout(GraphNode graphNode) {
56
		this.node = graphNode;
57
		this.ownerLayoutContext = node.parent.getLayoutContext();
58
		graphNode.nodeFigure.addFigureListener(figureListener);
59
		figureToNode.put(graphNode.nodeFigure, graphNode);
60
	}
61
62
	public DisplayIndependentPoint getLocation() {
63
		if (location == null) {
64
			refreshLocation();
65
		}
66
		return new DisplayIndependentPoint(location);
67
	}
68
69
	public DisplayIndependentDimension getSize() {
70
		if (size == null) {
71
			refreshSize();
72
		}
73
		return new DisplayIndependentDimension(size);
74
	}
75
76
	public SubgraphLayout getSubgraph() {
77
		return subgraph;
78
	}
79
80
	public boolean isMovable() {
81
		return true;
82
	}
83
84
	public boolean isPrunable() {
85
		return ownerLayoutContext.isPruningEnabled();
86
	}
87
88
	public boolean isPruned() {
89
		return subgraph != null;
90
	}
91
92
	public boolean isResizable() {
93
		return (node.parent.getItem().getStyle() & ZestStyles.NODES_NO_LAYOUT_RESIZE) == 0;
94
	}
95
96
	public void prune(SubgraphLayout subgraph) {
97
		if (subgraph != null && !(subgraph instanceof DefaultSubgraph)) {
98
			throw new RuntimeException(
99
					"InternalNodeLayout can be pruned only to instance of DefaultSubgraph.");
100
		}
101
		ownerLayoutContext.checkChangesAllowed();
102
		if (subgraph == this.subgraph) {
103
			return;
104
		}
105
		if (this.subgraph != null) {
106
			SubgraphLayout subgraph2 = this.subgraph;
107
			this.subgraph = null;
108
			subgraph2.removeNodes(new NodeLayout[] { this });
109
		}
110
		if (subgraph != null) {
111
			this.subgraph = (DefaultSubgraph) subgraph;
112
			subgraph.addNodes(new NodeLayout[] { this });
113
		}
114
	}
115
116
	public void setLocation(double x, double y) {
117
		if (!ownerLayoutContext.isLayoutItemFiltered(this.getNode())) {
118
			ownerLayoutContext.checkChangesAllowed();
119
			internalSetLocation(x, y);
120
		}
121
	}
122
123
	private void internalSetLocation(double x, double y) {
124
		if (location != null) {
125
			location.x = x;
126
			location.y = y;
127
		} else {
128
			location = new DisplayIndependentPoint(x, y);
129
		}
130
	}
131
132
	public void setSize(double width, double height) {
133
		ownerLayoutContext.checkChangesAllowed();
134
		internalSetSize(width, height);
135
	}
136
137
	private void internalSetSize(double width, double height) {
138
		if (size != null) {
139
			size.width = width;
140
			size.height = height;
141
		} else {
142
			size = new DisplayIndependentDimension(width, height);
143
		}
144
	}
145
146
	public void setMinimized(boolean minimized) {
147
		ownerLayoutContext.checkChangesAllowed();
148
		getSize();
149
		this.minimized = minimized;
150
	}
151
152
	public boolean isMinimized() {
153
		return minimized;
154
	}
155
156
	public NodeLayout[] getPredecessingNodes() {
157
		ConnectionLayout[] connections = getIncomingConnections();
158
		NodeLayout[] result = new NodeLayout[connections.length];
159
		for (int i = 0; i < connections.length; i++) {
160
			result[i] = connections[i].getSource();
161
			if (result[i] == this) {
162
				result[i] = connections[i].getTarget();
163
			}
164
		}
165
		return result;
166
	}
167
168
	public NodeLayout[] getSuccessingNodes() {
169
		ConnectionLayout[] connections = getOutgoingConnections();
170
		NodeLayout[] result = new NodeLayout[connections.length];
171
		for (int i = 0; i < connections.length; i++) {
172
			result[i] = connections[i].getTarget();
173
			if (result[i] == this) {
174
				result[i] = connections[i].getSource();
175
			}
176
		}
177
		return result;
178
	}
179
180
	public EntityLayout[] getSuccessingEntities() {
181
		if (isPruned()) {
182
			return new NodeLayout[0];
183
		}
184
		ArrayList result = new ArrayList();
185
		HashSet addedSubgraphs = new HashSet();
186
		NodeLayout[] successingNodes = getSuccessingNodes();
187
		for (int i = 0; i < successingNodes.length; i++) {
188
			if (!successingNodes[i].isPruned()) {
189
				result.add(successingNodes[i]);
190
			} else {
191
				SubgraphLayout successingSubgraph = successingNodes[i]
192
						.getSubgraph();
193
				if (successingSubgraph.isGraphEntity()
194
						&& !addedSubgraphs.contains(successingSubgraph)) {
195
					result.add(successingSubgraph);
196
					addedSubgraphs.add(successingSubgraph);
197
				}
198
			}
199
		}
200
		return (EntityLayout[]) result.toArray(new EntityLayout[result.size()]);
201
	}
202
203
	public EntityLayout[] getPredecessingEntities() {
204
		if (isPruned()) {
205
			return new NodeLayout[0];
206
		}
207
		ArrayList result = new ArrayList();
208
		HashSet addedSubgraphs = new HashSet();
209
		NodeLayout[] predecessingNodes = getPredecessingNodes();
210
		for (int i = 0; i < predecessingNodes.length; i++) {
211
			if (!predecessingNodes[i].isPruned()) {
212
				result.add(predecessingNodes[i]);
213
			} else {
214
				SubgraphLayout predecessingSubgraph = predecessingNodes[i]
215
						.getSubgraph();
216
				if (predecessingSubgraph.isGraphEntity()
217
						&& !addedSubgraphs.contains(predecessingSubgraph)) {
218
					result.add(predecessingSubgraph);
219
					addedSubgraphs.add(predecessingSubgraph);
220
				}
221
			}
222
		}
223
		return (EntityLayout[]) result.toArray(new EntityLayout[result.size()]);
224
	}
225
226
	public ConnectionLayout[] getIncomingConnections() {
227
		ArrayList result = new ArrayList();
228
		for (Iterator iterator = node.getTargetConnections().iterator(); iterator
229
				.hasNext();) {
230
			GraphConnection connection = (GraphConnection) iterator.next();
231
			if (!ownerLayoutContext.isLayoutItemFiltered(connection)) {
232
				result.add(connection.getLayout());
233
			}
234
		}
235
		for (Iterator iterator = node.getSourceConnections().iterator(); iterator
236
				.hasNext();) {
237
			GraphConnection connection = (GraphConnection) iterator.next();
238
			if (!connection.isDirected()
239
					&& !ownerLayoutContext.isLayoutItemFiltered(connection)) {
240
				result.add(connection.getLayout());
241
			}
242
		}
243
		return (ConnectionLayout[]) result.toArray(new ConnectionLayout[result
244
				.size()]);
245
	}
246
247
	public ConnectionLayout[] getOutgoingConnections() {
248
		ArrayList result = new ArrayList();
249
		for (Iterator iterator = node.getSourceConnections().iterator(); iterator
250
				.hasNext();) {
251
			GraphConnection connection = (GraphConnection) iterator.next();
252
			if (!ownerLayoutContext.isLayoutItemFiltered(connection)) {
253
				result.add(connection.getLayout());
254
			}
255
		}
256
		for (Iterator iterator = node.getTargetConnections().iterator(); iterator
257
				.hasNext();) {
258
			GraphConnection connection = (GraphConnection) iterator.next();
259
			if (!connection.isDirected()
260
					&& !ownerLayoutContext.isLayoutItemFiltered(connection)) {
261
				result.add(connection.getLayout());
262
			}
263
		}
264
		return (ConnectionLayout[]) result.toArray(new ConnectionLayout[result
265
				.size()]);
266
	}
267
268
	public double getPreferredAspectRatio() {
269
		return 0;
270
	}
271
272
	GraphNode getNode() {
273
		return node;
274
	}
275
276
	void applyLayout() {
277
		if (minimized) {
278
			node.setSize(0, 0);
279
			if (location != null) {
280
				node.setLocation(location.x, location.y);
281
			}
282
		} else {
283
			node.setSize(-1, -1);
284
			if (location != null) {
285
				node.setLocation(location.x - getSize().width / 2, location.y
286
						- size.height / 2);
287
			}
288
			if (size != null) {
289
				Dimension currentSize = node.getSize();
290
				if (size.width != currentSize.width
291
						|| size.height != currentSize.height) {
292
					node.setSize(size.width, size.height);
293
				}
294
			}
295
		}
296
	}
297
298
	InternalLayoutContext getOwnerLayoutContext() {
299
		return ownerLayoutContext;
300
	}
301
302
	void refreshSize() {
303
		Dimension size2 = node.getSize();
304
		internalSetSize(size2.width, size2.height);
305
	}
306
307
	void refreshLocation() {
308
		Point location2 = node.getLocation();
309
		internalSetLocation(location2.x + getSize().width / 2, location2.y
310
				+ size.height / 2);
311
	}
312
313
	public String toString() {
314
		return node.toString() + "(layout)";
315
	}
316
317
	void dispose() {
318
		isDisposed = true;
319
		if (subgraph != null) {
320
			subgraph.removeDisposedNodes();
321
		}
322
		ownerLayoutContext.fireNodeRemovedEvent(node.getLayout());
323
		figureToNode.remove(node.nodeFigure);
324
	}
325
326
	boolean isDisposed() {
327
		return isDisposed;
328
	}
329
}
(-)src/org/eclipse/zest/core/widgets/LayoutFilter.java (+20 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria 
9
 *               Mateusz Matela
10
 ******************************************************************************/
11
package org.eclipse.zest.core.widgets;
12
13
/**
14
 * @since 2.0
15
 */
16
public interface LayoutFilter {
17
18
	public boolean isObjectFiltered(GraphItem item);
19
20
}
(-)src/org/eclipse/zest/core/widgets/PrunedSuccessorsSubgraph.java (+272 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import java.util.Arrays;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.Iterator;
16
17
import org.eclipse.draw2d.AncestorListener;
18
import org.eclipse.draw2d.ColorConstants;
19
import org.eclipse.draw2d.FigureListener;
20
import org.eclipse.draw2d.IFigure;
21
import org.eclipse.draw2d.geometry.Dimension;
22
import org.eclipse.draw2d.geometry.Point;
23
import org.eclipse.draw2d.geometry.Rectangle;
24
import org.eclipse.swt.graphics.Font;
25
import org.eclipse.swt.graphics.FontData;
26
import org.eclipse.swt.widgets.Display;
27
import org.eclipse.zest.core.widgets.internal.GraphLabel;
28
import org.eclipse.zest.core.widgets.internal.ZestRootLayer;
29
import org.eclipse.zest.layouts.interfaces.LayoutContext;
30
import org.eclipse.zest.layouts.interfaces.NodeLayout;
31
32
/**
33
 * A subgraph that for each unexpanded node in a graph adds a label showing
34
 * number of pruned successors (as unexpanded node is considered a node for
35
 * which {@link Graph#canExpand(GraphNode)} returns true AND
36
 * {@link Graph#canCollapse(GraphNode)} returns false). It doesn't matter which
37
 * subgraph a node is pruned to, so the factory for this subgraph uses one
38
 * instance for whole layout context.
39
 */
40
class PrunedSuccessorsSubgraph extends DefaultSubgraph {
41
42
	private class LabelAncestorListener extends AncestorListener.Stub {
43
		private final IFigure originalFigure;
44
		private IFigure fisheyeFigure;
45
46
		public LabelAncestorListener(IFigure originalFigure,
47
				IFigure fisheyeFigure) {
48
			this.originalFigure = originalFigure;
49
			this.fisheyeFigure = fisheyeFigure;
50
		}
51
52
		public void ancestorRemoved(IFigure ancestor) {
53
			if (fisheyeFigure != null) {
54
				final GraphLabel label = (GraphLabel) nodeFigureToLabel
55
						.get(fisheyeFigure);
56
				if (label == null) {
57
					return;
58
				}
59
				nodeFigureToLabel.remove(fisheyeFigure);
60
				Display.getDefault().asyncExec(new Runnable() {
61
					public void run() {
62
						label.removeAncestorListener(LabelAncestorListener.this);
63
					}
64
				});
65
				fisheyeFigure.removeFigureListener(nodeFigureListener);
66
				originalFigure.addFigureListener(nodeFigureListener);
67
				labelToAncestorListener.remove(label);
68
				fisheyeFigure = null;
69
				addLabelForFigure(originalFigure, label);
70
				refreshLabelBounds(originalFigure, label);
71
			}
72
		}
73
	}
74
75
	private final FigureListener nodeFigureListener = new FigureListener() {
76
		public void figureMoved(IFigure source) {
77
			GraphLabel label = (GraphLabel) nodeFigureToLabel.get(source);
78
			if (label != null) {
79
				refreshLabelBounds(source, label);
80
			}
81
		}
82
	};
83
84
	private final FisheyeListener fisheyeListener = new FisheyeListener() {
85
86
		public void fisheyeReplaced(Graph graph, IFigure oldFisheyeFigure,
87
				IFigure newFisheyeFigure) {
88
			oldFisheyeFigure.removeFigureListener(nodeFigureListener);
89
			newFisheyeFigure.addFigureListener(nodeFigureListener);
90
			GraphLabel label = (GraphLabel) nodeFigureToLabel
91
					.remove(oldFisheyeFigure);
92
			nodeFigureToLabel.put(newFisheyeFigure, label);
93
94
			LabelAncestorListener ancestorListener = (LabelAncestorListener) labelToAncestorListener
95
					.get(label);
96
			ancestorListener.fisheyeFigure = null;
97
			addLabelForFigure(newFisheyeFigure, label);
98
			ancestorListener.fisheyeFigure = newFisheyeFigure;
99
			refreshLabelBounds(newFisheyeFigure, label);
100
		}
101
102
		public void fisheyeRemoved(Graph graph, IFigure originalFigure,
103
				IFigure fisheyeFigure) {
104
			// do nothing - labelAncestorListener will take care of cleaning
105
			// up
106
		}
107
108
		public void fisheyeAdded(Graph graph, IFigure originalFigure,
109
				IFigure fisheyeFigure) {
110
			originalFigure.removeFigureListener(nodeFigureListener);
111
			fisheyeFigure.addFigureListener(nodeFigureListener);
112
			GraphLabel label = (GraphLabel) nodeFigureToLabel
113
					.get(originalFigure);
114
			if (label == null) {
115
				return;
116
			}
117
			nodeFigureToLabel.put(fisheyeFigure, label);
118
			refreshLabelBounds(fisheyeFigure, label);
119
			addLabelForFigure(fisheyeFigure, label);
120
			LabelAncestorListener labelAncestorListener = new LabelAncestorListener(
121
					originalFigure, fisheyeFigure);
122
			label.addAncestorListener(labelAncestorListener);
123
			labelToAncestorListener.put(label, labelAncestorListener);
124
		}
125
	};
126
127
	/**
128
	 * Maps from figures of nodes to labels showing number of nodes hidden
129
	 * successors
130
	 */
131
	private HashMap nodeFigureToLabel = new HashMap();
132
133
	private HashMap labelToAncestorListener = new HashMap();
134
135
	protected PrunedSuccessorsSubgraph(LayoutContext context2) {
136
		super(context2);
137
		context.container.getGraph().addFisheyeListener(fisheyeListener);
138
	}
139
140
	public void addNodes(NodeLayout[] nodes) {
141
		super.addNodes(nodes);
142
		HashSet nodesToUpdate = new HashSet();
143
		for (int i = 0; i < nodes.length; i++) {
144
			nodesToUpdate
145
					.addAll(Arrays.asList(nodes[i].getPredecessingNodes()));
146
		}
147
		for (Iterator iterator = nodesToUpdate.iterator(); iterator.hasNext();) {
148
			InternalNodeLayout nodeToUpdate = (InternalNodeLayout) iterator
149
					.next();
150
			updateNodeLabel(nodeToUpdate);
151
		}
152
153
	}
154
155
	public void removeNodes(NodeLayout[] nodes) {
156
		super.removeNodes(nodes);
157
		HashSet nodesToUpdate = new HashSet();
158
		for (int i = 0; i < nodes.length; i++) {
159
			nodesToUpdate
160
					.addAll(Arrays.asList(nodes[i].getPredecessingNodes()));
161
			if (((InternalNodeLayout) nodes[i]).isDisposed()) {
162
				removeFigureForNode((InternalNodeLayout) nodes[i]);
163
			} else {
164
				nodesToUpdate.add(nodes[i]);
165
			}
166
		}
167
		for (Iterator iterator = nodesToUpdate.iterator(); iterator.hasNext();) {
168
			InternalNodeLayout predecessor = (InternalNodeLayout) iterator
169
					.next();
170
			updateNodeLabel(predecessor);
171
		}
172
	}
173
174
	private void addLabelForFigure(IFigure figure, GraphLabel label) {
175
		IFigure parent = figure.getParent();
176
		if (parent instanceof ZestRootLayer) {
177
			((ZestRootLayer) parent).addDecoration(figure, label);
178
		} else {
179
			if (parent.getChildren().contains(label)) {
180
				parent.remove(label);
181
			}
182
			int index = parent.getChildren().indexOf(figure);
183
			parent.add(label, index + 1);
184
		}
185
	}
186
187
	private void refreshLabelBounds(IFigure figure, GraphLabel label) {
188
		Rectangle figureBounds = figure.getBounds();
189
		if (figureBounds.width * figureBounds.height > 0) {
190
			label.setText(label.getText()); // hack: resets label's size
191
			Dimension labelSize = label.getSize();
192
			labelSize.expand(-6, -4);
193
			Point anchorPoint = figure.getBounds().getBottomRight();
194
			anchorPoint.x -= labelSize.width / 2;
195
			anchorPoint.y -= labelSize.height / 2;
196
			Rectangle bounds = new Rectangle(anchorPoint, labelSize);
197
			label.setBounds(bounds);
198
			label.getParent().setConstraint(label, bounds);
199
		} else {
200
			label.getParent().setConstraint(label,
201
					new Rectangle(figureBounds.x, figureBounds.y, 0, 0));
202
			label.setBounds(new Rectangle(figureBounds.x, figureBounds.y, 0, 0));
203
		}
204
	}
205
206
	void updateNodeLabel(InternalNodeLayout internalNode) {
207
		if (internalNode.isDisposed()) {
208
			return;
209
		}
210
		IFigure figure = internalNode.getNode().getFigure();
211
		GraphLabel label = (GraphLabel) nodeFigureToLabel.get(figure);
212
		IFigure fisheye = getFisheyeFigure(figure);
213
		if (fisheye != null) {
214
			figure = fisheye;
215
		}
216
		if (label == null) {
217
			label = new GraphLabel(false);
218
			label.setForegroundColor(ColorConstants.white);
219
			label.setBackgroundColor(ColorConstants.red);
220
			FontData fontData = Display.getDefault().getSystemFont()
221
					.getFontData()[0];
222
			fontData.setHeight(6);
223
			label.setFont(new Font(Display.getCurrent(), fontData));
224
			figure.addFigureListener(nodeFigureListener);
225
			addLabelForFigure(figure, label);
226
			nodeFigureToLabel.put(figure, label);
227
		}
228
229
		GraphNode graphNode = internalNode.getNode();
230
		if (!graphNode.getGraphModel().canExpand(graphNode)
231
				|| graphNode.getGraphModel().canCollapse(graphNode)
232
				|| internalNode.isPruned()) {
233
			label.setVisible(false);
234
		} else {
235
			NodeLayout[] successors = internalNode.getSuccessingNodes();
236
			int numberOfHiddenSuccessors = 0;
237
			for (int i = 0; i < successors.length; i++) {
238
				if (successors[i].isPruned()) {
239
					numberOfHiddenSuccessors++;
240
				}
241
			}
242
			String labelText = numberOfHiddenSuccessors > 0 ? ""
243
					+ numberOfHiddenSuccessors : "";
244
			if (!labelText.equals(label.getText())) {
245
				label.setText(labelText);
246
			}
247
			label.setVisible(true);
248
		}
249
250
		refreshLabelBounds(figure, label);
251
	}
252
253
	private IFigure getFisheyeFigure(IFigure originalFigure) {
254
		// a node has a fisheye if and only if its label has an AncestorListener
255
		GraphLabel label = (GraphLabel) nodeFigureToLabel.get(originalFigure);
256
		LabelAncestorListener ancestorListener = (LabelAncestorListener) labelToAncestorListener
257
				.get(label);
258
		if (ancestorListener != null) {
259
			return ancestorListener.fisheyeFigure;
260
		}
261
		return null;
262
	}
263
264
	private void removeFigureForNode(InternalNodeLayout internalNode) {
265
		IFigure figure = internalNode.getNode().getFigure();
266
		GraphLabel label = (GraphLabel) nodeFigureToLabel.get(figure);
267
		if (label != null && label.getParent() != null) {
268
			label.getParent().remove(label);
269
		}
270
		nodeFigureToLabel.remove(figure);
271
	}
272
}
(-)src/org/eclipse/zest/core/widgets/SubgraphFactory.java (+25 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
11
12
import org.eclipse.zest.layouts.interfaces.LayoutContext;
13
import org.eclipse.zest.layouts.interfaces.NodeLayout;
14
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
15
16
/**
17
 * Factory used by {@link Graph} to create subgraphs. One instance of
18
 * SubgraphFactory can be used with multiple graphs unless explicitly stated
19
 * otherwise.
20
 * 
21
 * @since 2.0
22
 */
23
public interface SubgraphFactory {
24
	SubgraphLayout createSubgraph(NodeLayout[] nodes, LayoutContext context);
25
}
(-)src/org/eclipse/zest/core/widgets/ZestStyles.java (-142 / +137 lines)
Lines 1-142 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets;
10
package org.eclipse.zest.core.widgets;
11
11
12
/**
12
/**
13
 * Style constants used in Zest.
13
 * Style constants used in Zest.
14
 * 
14
 * 
15
 * @author Chris Callendar
15
 * @author Chris Callendar
16
 */
16
 */
17
public final class ZestStyles {
17
public final class ZestStyles {
18
18
19
	/**
19
	/**
20
	 * A constant known to be zero (0), used in operations which take bit flags
20
	 * A constant known to be zero (0), used in operations which take bit flags
21
	 * to indicate that "no bits are set".
21
	 * to indicate that "no bits are set".
22
	 */
22
	 */
23
	public static final int NONE = 0;
23
	public static final int NONE = 0;
24
24
25
	/**
25
	/**
26
	 * Style constant indicating that invisible nodes should be ignored for
26
	 * Style constant indicating that invisible nodes should be ignored for
27
	 * layouts.
27
	 * layouts.
28
	 */
28
	 */
29
	public static final int IGNORE_INVISIBLE_LAYOUT = 1 << 1;
29
	public static final int IGNORE_INVISIBLE_LAYOUT = 1 << 1;
30
30
31
	/**
31
	/**
32
	 * Style constant indicating if the selected node's neighbors should be
32
	 * Style constant indicating if the selected node's neighbors should be
33
	 * highlighted. Note: this is a node-level style. It should not be applied
33
	 * highlighted. Note: this is a node-level style. It should not be applied
34
	 * to graph views during construction.
34
	 * to graph views during construction.
35
	 * 
35
	 * 
36
	 */
36
	 */
37
	//public static final int NODES_HIGHLIGHT_ADJACENT = 1 << 1;
37
	// public static final int NODES_HIGHLIGHT_ADJACENT = 1 << 1;
38
	/**
38
	/**
39
	 * Style constant indicating that node labels should be cached. This is
39
	 * Style constant indicating that node labels should be cached. This is
40
	 * important under GTK+ because font drawing is slower than Windows.
40
	 * important under GTK+ because font drawing is slower than Windows.
41
	 */
41
	 */
42
	public static final int NODES_CACHE_LABEL = 1 << 1;
42
	public static final int NODES_CACHE_LABEL = 1 << 1;
43
43
44
	/**
44
	/**
45
	 * Style to specify that the node should contain a fisheye label
45
	 * Style to specify that the node should contain a fisheye label when the
46
	 * when the mouse moves over it.  By default the fisheye node is just the
46
	 * mouse moves over it. By default the fisheye node is just the label with
47
	 * label with larger text.
47
	 * larger text.
48
	 */
48
	 */
49
	public static final int NODES_FISHEYE = 1 << 2;
49
	public static final int NODES_FISHEYE = 1 << 2;
50
50
51
	/**
51
	/**
52
	 * Style to specify that the node should not show its text (only its image).
52
	 * Style to specify that the node should not show its text (only its image).
53
	 * This with the NODES_FISHEYE style should help with large graphs (since the
53
	 * This with the NODES_FISHEYE style should help with large graphs (since
54
	 * fisheye style will show the text).
54
	 * the fisheye style will show the text).
55
	 */
55
	 */
56
	public static final int NODES_HIDE_TEXT = 1 << 3;
56
	public static final int NODES_HIDE_TEXT = 1 << 3;
57
57
58
	/**
58
	/**
59
	 * Style constant indiciating that nodes should not be resized on layout.
59
	 * Style constant indiciating that nodes should not be resized on layout.
60
	 */
60
	 */
61
	public static final int NODES_NO_LAYOUT_RESIZE = 1 << 4;
61
	public static final int NODES_NO_LAYOUT_RESIZE = 1 << 4;
62
	/**
62
	/**
63
	 * Style indicating that connections should show their direction by default.
63
	 * Style indicating that connections should show their direction by default.
64
	 */
64
	 */
65
	public static final int CONNECTIONS_DIRECTED = 1 << 1;
65
	public static final int CONNECTIONS_DIRECTED = 1 << 1;
66
66
67
	/**
67
	/**
68
	 * Style constant to indicate that connections should be drawn with solid
68
	 * Style constant to indicate that connections should be drawn with solid
69
	 * lines (this is the default).
69
	 * lines (this is the default).
70
	 */
70
	 */
71
	public static final int CONNECTIONS_SOLID = 1 << 2;
71
	public static final int CONNECTIONS_SOLID = 1 << 2;
72
	/**
72
	/**
73
	 * Style constant to indicate that connections should be drawn with dashed
73
	 * Style constant to indicate that connections should be drawn with dashed
74
	 * lines.
74
	 * lines.
75
	 */
75
	 */
76
	public static final int CONNECTIONS_DASH = 1 << 3;
76
	public static final int CONNECTIONS_DASH = 1 << 3;
77
	/**
77
	/**
78
	 * Style constant to indicate that connections should be drawn with dotted
78
	 * Style constant to indicate that connections should be drawn with dotted
79
	 * lines.
79
	 * lines.
80
	 */
80
	 */
81
	public static final int CONNECTIONS_DOT = 1 << 4;
81
	public static final int CONNECTIONS_DOT = 1 << 4;
82
	/**
82
	/**
83
	 * Style constant to indicate that connections should be drawn with
83
	 * Style constant to indicate that connections should be drawn with
84
	 * dash-dotted lines.
84
	 * dash-dotted lines.
85
	 */
85
	 */
86
	public static final int CONNECTIONS_DASH_DOT = 1 << 5;
86
	public static final int CONNECTIONS_DASH_DOT = 1 << 5;
87
87
88
	/**
88
	/**
89
	 * Bitwise ANDs the styleToCheck integer with the given style.
89
	 * Bitwise ANDs the styleToCheck integer with the given style.
90
	 * 
90
	 * 
91
	 * @param style
91
	 * @param style
92
	 * @param styleToCheck
92
	 * @param styleToCheck
93
	 * @return boolean if styleToCheck is part of the style
93
	 * @return boolean if styleToCheck is part of the style
94
	 */
94
	 */
95
	public static boolean checkStyle(int style, int styleToCheck) {
95
	public static boolean checkStyle(int style, int styleToCheck) {
96
		return ((style & styleToCheck) == styleToCheck);
96
		return ((style & styleToCheck) == styleToCheck);
97
	}
97
	}
98
98
99
	/**
99
	/**
100
	 * Validates the given style for connections to see if it is legal. Returns
100
	 * Validates the given style for connections to see if it is legal. Returns
101
	 * false if not.
101
	 * false if not.
102
	 * 
102
	 * 
103
	 * @param style
103
	 * @param style
104
	 *            the style to check.
104
	 *            the style to check.
105
	 * @return true iff the given style is legal.
105
	 * @return true iff the given style is legal.
106
	 */
106
	 */
107
	public static boolean validateConnectionStyle(int styleToValidate) {
107
	public static boolean validateConnectionStyle(int styleToValidate) {
108
		int style = styleToValidate;
108
		int style = styleToValidate;
109
		// int illegal = CONNECTIONS_CURVED | CONNECTIONS_STRAIGHT |
109
		// int illegal = CONNECTIONS_CURVED | CONNECTIONS_STRAIGHT |
110
		// CONNECTIONS_BEZIER;
110
		// CONNECTIONS_BEZIER;
111
		/*
111
		/*
112
		int illegal = CONNECTIONS_STRAIGHT;
112
		 * int illegal = CONNECTIONS_STRAIGHT; style &= illegal; int rightBit =
113
		style &= illegal;
113
		 * style & (-style); boolean okay = (style == rightBit); if (!okay) {
114
		int rightBit = style & (-style);
114
		 * return okay; }
115
		boolean okay = (style == rightBit);
115
		 */
116
		if (!okay) {
116
117
			return okay;
117
		int illegal = CONNECTIONS_DASH_DOT | CONNECTIONS_DASH | CONNECTIONS_DOT
118
		}
118
				| CONNECTIONS_SOLID;
119
		*/
119
		style = styleToValidate;
120
120
		style &= illegal;
121
		int illegal = CONNECTIONS_DASH_DOT | CONNECTIONS_DASH | CONNECTIONS_DOT | CONNECTIONS_SOLID;
121
		int rightBit = style & (-style);
122
		style = styleToValidate;
122
		boolean okay = (style == rightBit);
123
		style &= illegal;
123
		if (!okay) {
124
		int rightBit = style & (-style);
124
			return okay;
125
		boolean okay = (style == rightBit);
125
		}
126
		if (!okay) {
126
127
			return okay;
127
		return true;
128
		}
128
129
129
		// @tag zest.bug.160368-ConnectionAlign.fix : must check the connections
130
		return true;
130
		// to make sure that there isnt' an illegal combination of alignments.
131
131
		/*
132
		// @tag zest.bug.160368-ConnectionAlign.fix : must check the connections
132
		 * illegal = CONNECTIONS_VALIGN_BOTTOM | CONNECTIONS_VALIGN_MIDDLE |
133
		// to make sure that there isnt' an illegal combination of alignments.
133
		 * CONNECTIONS_VALIGN_TOP; style = styleToValidate; style &= illegal;
134
		/*
134
		 * rightBit = style & (-style); return (style == rightBit);
135
		illegal = CONNECTIONS_VALIGN_BOTTOM | CONNECTIONS_VALIGN_MIDDLE | CONNECTIONS_VALIGN_TOP;
135
		 */
136
		style = styleToValidate;
136
	}
137
		style &= illegal;
137
}
138
		rightBit = style & (-style);
139
		return (style == rightBit);
140
		*/
141
	}
142
}
(-)src/org/eclipse/zest/core/widgets/custom/CGraphNode.java (+76 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria 
9
 *               Mateusz Matela
10
 ******************************************************************************/
11
package org.eclipse.zest.core.widgets.custom;
12
13
import org.eclipse.draw2d.IFigure;
14
import org.eclipse.swt.graphics.Color;
15
import org.eclipse.swt.graphics.Font;
16
import org.eclipse.zest.core.widgets.Graph;
17
import org.eclipse.zest.core.widgets.GraphContainer;
18
import org.eclipse.zest.core.widgets.GraphNode;
19
20
/**
21
 * A Custom Graph Node
22
 * 
23
 * @since 2.0
24
 */
25
public class CGraphNode extends GraphNode {
26
27
	IFigure figure = null;
28
29
	/**
30
	 * @since 2.0
31
	 */
32
	public CGraphNode(Graph graphModel, int style, IFigure figure) {
33
		super(graphModel, style, figure);
34
	}
35
36
	/**
37
	 * @since 2.0
38
	 */
39
	public CGraphNode(GraphContainer graphModel, int style, IFigure figure) {
40
		super(graphModel, style, figure);
41
	}
42
43
	public IFigure getFigure() {
44
		return super.getFigure();
45
	}
46
47
	protected IFigure createFigureForModel() {
48
		this.figure = (IFigure) this.getData();
49
		return this.figure;
50
	}
51
52
	public void setBackgroundColor(Color c) {
53
		getFigure().setBackgroundColor(c);
54
	}
55
56
	public void setFont(Font font) {
57
		getFigure().setFont(font);
58
	}
59
60
	public Color getBackgroundColor() {
61
		return getFigure().getBackgroundColor();
62
	}
63
64
	public Font getFont() {
65
		return getFigure().getFont();
66
	}
67
68
	public Color getForegroundColor() {
69
		return getFigure().getForegroundColor();
70
	}
71
72
	protected void updateFigureForModel(IFigure currentFigure) {
73
		// Undefined
74
	}
75
76
}
(-)src/org/eclipse/zest/core/widgets/custom/LabelSubgraph.java (+68 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.custom;
11
12
import org.eclipse.draw2d.Label;
13
import org.eclipse.swt.graphics.Color;
14
import org.eclipse.zest.core.widgets.FigureSubgraph;
15
import org.eclipse.zest.core.widgets.internal.GraphLabel;
16
import org.eclipse.zest.layouts.interfaces.LayoutContext;
17
import org.eclipse.zest.layouts.interfaces.NodeLayout;
18
19
/**
20
 * A subgraph layout that displays a label showing number of items pruned within
21
 * it.
22
 * 
23
 * @since 2.0
24
 */
25
public class LabelSubgraph extends FigureSubgraph {
26
27
	private Color backgroundColor;
28
	private Color foregroundColor;
29
30
	/**
31
	 * Sets the foreground color of this subgraph (that is color of the text on
32
	 * the label).
33
	 * 
34
	 * @param color
35
	 *            color to set
36
	 */
37
	public void setForegroundColor(Color color) {
38
		figure.setForegroundColor(color);
39
	}
40
41
	/**
42
	 * Sets the background color of this subgraph's label.
43
	 * 
44
	 * @param color
45
	 *            color to set
46
	 */
47
	public void setBackgroundColor(Color color) {
48
		figure.setBackgroundColor(color);
49
	}
50
51
	protected void createFigure() {
52
		figure = new GraphLabel(false);
53
		figure.setForegroundColor(foregroundColor);
54
		figure.setBackgroundColor(backgroundColor);
55
		updateFigure();
56
	}
57
58
	protected void updateFigure() {
59
		((Label) figure).setText("" + nodes.size());
60
	}
61
62
	public LabelSubgraph(NodeLayout[] nodes, LayoutContext context,
63
			Color foregroundColor, Color backgroundColor) {
64
		super(nodes, context);
65
		this.foregroundColor = foregroundColor;
66
		this.backgroundColor = backgroundColor;
67
	}
68
}
(-)src/org/eclipse/zest/core/widgets/custom/TriangleSubgraph.java (+255 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.custom;
11
12
import java.util.HashMap;
13
14
import org.eclipse.draw2d.ColorConstants;
15
import org.eclipse.draw2d.Graphics;
16
import org.eclipse.draw2d.Shape;
17
import org.eclipse.draw2d.geometry.PointList;
18
import org.eclipse.draw2d.geometry.Rectangle;
19
import org.eclipse.swt.graphics.Color;
20
import org.eclipse.zest.core.widgets.FigureSubgraph;
21
import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver;
22
import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver.TreeListener;
23
import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver.TreeNode;
24
import org.eclipse.zest.layouts.interfaces.LayoutContext;
25
import org.eclipse.zest.layouts.interfaces.NodeLayout;
26
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
27
28
/**
29
 * A subgraph that is visualized in a graph as a triangle. It assumes that nodes
30
 * in context that uses them are arranged in a tree structure and the nodes
31
 * added to the subgraph are a subtree (except for the subtree's root, which
32
 * should not be added).
33
 * 
34
 * The triangle has three features that show the properties of a subtree
35
 * contained within it:
36
 * <ul>
37
 * <li><b>Height of the triangle</b> is proportional to the height of the
38
 * subtree. If the subtree contains the whole tree, the triangle's height will
39
 * be equal to value provided with
40
 * {@link TriangleSubgraph#setReferenceHeight(double)} (default is 50).</li>
41
 * <li><b>Length of the triangle's base</b> depends on the number of leaves in
42
 * the subtree. More precisely, it is proportional to the logarithm of the
43
 * percent that the subtree's leaves make of the whole context's leaves. The
44
 * proportion factor is adjusted so that for a subtree containing all the leaves
45
 * the base has length provided with {@link TriangleSubgraph}
46
 * {@link #setReferenceBase(double)} (default is 50) and for a subtree
47
 * containing only one leaf the base has length 1.</li>
48
 * <li><b>Background color of the triangle</b> depends on average number of
49
 * children for nodes in the subtree. The less is this value, the more bright is
50
 * the color (up to white for a subtree with average number of children equal to
51
 * 1). The average value is calculated only for nodes that have at least one
52
 * child. The root of the subtree (which is not directly added to this subgraph)
53
 * is also accounted.</li>
54
 * </ul>
55
 * 
56
 * When the first subgraph of this class is created for a layout context, a
57
 * {@link TreeLayoutObserver} is created for the context. It must keep track of
58
 * changes in the graph structure, so events related to it should not be
59
 * intercepted by other listeners before they reach the subgraph's observer.
60
 * 
61
 * @since 2.0
62
 */
63
public class TriangleSubgraph extends FigureSubgraph {
64
65
	public static class TriangleParameters implements Cloneable {
66
		public Color color = ColorConstants.black;
67
68
		public int direction = TOP_DOWN;
69
70
		public double referenceHeight = 50;
71
72
		public double referenceBase = 50;
73
74
		public Object clone() {
75
			TriangleParameters result = new TriangleParameters();
76
			result.color = color;
77
			result.direction = direction;
78
			result.referenceHeight = referenceHeight;
79
			result.referenceBase = referenceBase;
80
			return result;
81
		}
82
	}
83
84
	private class IsoscelesTriangle extends Shape {
85
86
		private PointList points = new PointList(3);
87
88
		protected void fillShape(Graphics graphics) {
89
			graphics.fillPolygon(points);
90
		}
91
92
		protected void outlineShape(Graphics graphics) {
93
			graphics.drawPolygon(points);
94
		}
95
96
		protected void primTranslate(int dx, int dy) {
97
			super.primTranslate(dx, dy);
98
			points.translate(dx, dy);
99
		}
100
101
		public void validate() {
102
			super.validate();
103
			Rectangle r = new Rectangle();
104
			r.setBounds(getBounds());
105
			r.crop(getInsets());
106
			points.removeAllPoints();
107
			switch (parameters.direction) {
108
			case TOP_DOWN:
109
				points.addPoint(r.x + r.width / 2, r.y);
110
				points.addPoint(r.x, r.y + r.height);
111
				points.addPoint(r.x + r.width, r.y + r.height);
112
				break;
113
			case BOTTOM_UP:
114
				points.addPoint(r.x + r.width / 2, r.y + r.height);
115
				points.addPoint(r.x, r.y);
116
				points.addPoint(r.x + r.width, r.y);
117
				break;
118
			case LEFT_RIGHT:
119
				points.addPoint(r.x, r.y + r.height / 2);
120
				points.addPoint(r.x + r.width, r.y);
121
				points.addPoint(r.x + r.width, r.y + r.height);
122
				break;
123
			case RIGHT_LEFT:
124
				points.addPoint(r.x + r.width, r.y + r.height / 2);
125
				points.addPoint(r.x, r.y);
126
				points.addPoint(r.x, r.y + r.height);
127
				break;
128
			}
129
		}
130
	}
131
132
	private static HashMap contextToTree = new HashMap();
133
134
	private TriangleParameters parameters;
135
136
	public TriangleSubgraph(NodeLayout[] nodes, LayoutContext context,
137
			TriangleParameters triangleParameters) {
138
		super(nodes, context);
139
		this.parameters = triangleParameters;
140
		if (contextToTree.get(context) == null) {
141
			TreeLayoutObserver treeLayoutObserver = new TreeLayoutObserver(
142
					context, null);
143
			treeLayoutObserver.addTreeListener(new TreeListener() {
144
				protected void defaultHandle(TreeNode changedNode) {
145
					SubgraphLayout subgraph = changedNode.getNode()
146
							.getSubgraph();
147
					if (subgraph instanceof TriangleSubgraph) {
148
						((TriangleSubgraph) subgraph).updateFigure();
149
					}
150
				}
151
			});
152
			contextToTree.put(context, treeLayoutObserver);
153
		}
154
	}
155
156
	protected void createFigure() {
157
		figure = new IsoscelesTriangle();
158
		figure.setBackgroundColor(parameters.color);
159
		figure.setForegroundColor(parameters.color);
160
	}
161
162
	private double log(double value, double base) {
163
		return Math.log(value) / Math.log(base);
164
	}
165
166
	protected void updateFigure() {
167
		TreeLayoutObserver tree = (TreeLayoutObserver) contextToTree
168
				.get(context);
169
		TreeNode subgraphRoot = tree.getTreeNode((NodeLayout) nodes.iterator()
170
				.next());
171
		if (subgraphRoot == null) {
172
			return;
173
		}
174
		while (nodes.contains(subgraphRoot.getNode())) {
175
			subgraphRoot = subgraphRoot.getParent();
176
		}
177
178
		TreeNode superRoot = tree.getSuperRoot();
179
		double triangleHeight = parameters.referenceHeight
180
				* subgraphRoot.getHeight() / superRoot.getHeight();
181
182
		int numOfNodes = superRoot.getNumOfDescendants();
183
		int numOfNodesWithChildren = numOfNodes - superRoot.getNumOfLeaves()
184
				+ 1;
185
		double logBase = (numOfNodesWithChildren > 0) ? (double) numOfNodes
186
				/ numOfNodesWithChildren : 1;
187
		// logarithm base is the average number of children for whole context
188
		double triangleBaseModifier = (parameters.referenceBase - 1)
189
				/ log(superRoot.getNumOfLeaves(), logBase);
190
		double triangleBase = parameters.referenceBase
191
				+ triangleBaseModifier
192
				* log((double) subgraphRoot.getNumOfLeaves()
193
						/ superRoot.getNumOfLeaves(), logBase);
194
195
		if (parameters.direction == 0) {
196
			parameters.direction = parameters.direction;
197
		}
198
		if (parameters.direction == TOP_DOWN
199
				|| parameters.direction == BOTTOM_UP) {
200
			figure.setSize((int) (triangleBase + 0.5),
201
					(int) (triangleHeight + 0.5));
202
		} else {
203
			figure.setSize((int) (triangleHeight + 0.5),
204
					(int) (triangleBase + 0.5));
205
		}
206
207
		int numOfNodesWithChildrenInSubgraph = nodes.size()
208
				- subgraphRoot.getNumOfLeaves() + 1;
209
		double avgNumOfChildrenInSugbraph = (numOfNodesWithChildrenInSubgraph > 0) ? (double) nodes
210
				.size()
211
				/ numOfNodesWithChildrenInSubgraph
212
				: 1;
213
		int r = (int) (parameters.color.getRed() + ((double) 255 - parameters.color
214
				.getRed())
215
				/ avgNumOfChildrenInSugbraph);
216
		int g = (int) (parameters.color.getGreen() + ((double) 255 - parameters.color
217
				.getGreen())
218
				/ avgNumOfChildrenInSugbraph);
219
		int b = (int) (parameters.color.getBlue() + ((double) 255 - parameters.color
220
				.getBlue())
221
				/ avgNumOfChildrenInSugbraph);
222
		figure.setBackgroundColor(new Color(parameters.color.getDevice(), r, g,
223
				b));
224
		figure.setForegroundColor(parameters.color);
225
	}
226
227
	public boolean isDirectionDependant() {
228
		return true;
229
	}
230
231
	public void setDirection(int direction) {
232
		super.setDirection(direction);
233
		if (parameters.direction == direction) {
234
			return;
235
		}
236
		if (direction == TOP_DOWN || direction == BOTTOM_UP
237
				|| direction == LEFT_RIGHT || direction == RIGHT_LEFT) {
238
			parameters.direction = direction;
239
			updateFigure();
240
		} else {
241
			throw new IllegalArgumentException("invalid direction");
242
		}
243
	}
244
245
	/**
246
	 * Changes the color of the triangle visualizing this subgraph.
247
	 * 
248
	 * @param color
249
	 *            color to use
250
	 */
251
	public void setColor(Color color) {
252
		parameters.color = color;
253
		updateFigure();
254
	}
255
}
(-)src/org/eclipse/zest/core/widgets/internal/AligningBendpointLocator.java (-219 / +236 lines)
Lines 1-219 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.widgets.internal;
11
package org.eclipse.zest.core.widgets.internal;
12
12
13
import org.eclipse.draw2d.AbstractLocator;
13
import org.eclipse.draw2d.AbstractLocator;
14
import org.eclipse.draw2d.Connection;
14
import org.eclipse.draw2d.Connection;
15
import org.eclipse.draw2d.IFigure;
15
import org.eclipse.draw2d.IFigure;
16
import org.eclipse.draw2d.PositionConstants;
16
import org.eclipse.draw2d.PositionConstants;
17
import org.eclipse.draw2d.geometry.Dimension;
17
import org.eclipse.draw2d.geometry.Dimension;
18
import org.eclipse.draw2d.geometry.Point;
18
import org.eclipse.draw2d.geometry.Point;
19
import org.eclipse.draw2d.geometry.PointList;
19
import org.eclipse.draw2d.geometry.PointList;
20
20
21
/**
21
/**
22
 * A locator that finds the middle of a connection based on the bendpoints.
22
 * A locator that finds the middle of a connection based on the bendpoints.
23
 * @author Del Myers
23
 * 
24
 *
24
 * @author Del Myers
25
 */
25
 * 
26
//@tag zest(bug(154391-ArcEnds(fix))) : use this locator to ensure that labels will be in the middle of connections.
26
 */
27
//@tag zest.bug.160368-ConnectionAlign : replaces MidBenpointLocator in order to have finer control over alignment.
27
// @tag zest(bug(154391-ArcEnds(fix))) : use this locator to ensure that labels
28
public class AligningBendpointLocator extends AbstractLocator {
28
// will be in the middle of connections.
29
	/**
29
// @tag zest.bug.160368-ConnectionAlign : replaces MidBenpointLocator in order
30
	 * "Vertical" alignment contstant. Figures should be placed in the middle
30
// to have finer control over alignment.
31
	 * of the line.
31
public class AligningBendpointLocator extends AbstractLocator {
32
	 */
32
	/**
33
	public static final int MIDDLE = 0;
33
	 * "Vertical" alignment contstant. Figures should be placed in the middle of
34
	/**
34
	 * the line.
35
	 * "Vertical" alignment constant. Figures should be placed above the line.
35
	 */
36
	 */
36
	public static final int MIDDLE = 0;
37
	public static final int ABOVE = 1;
37
	/**
38
	/**
38
	 * "Vertical" alignment constant. Figures should be placed above the line.
39
	 * "Vertical" alignment constant. Figures should be placed below the line.
39
	 */
40
	 */
40
	public static final int ABOVE = 1;
41
	public static final int BELOW = 2;
41
	/**
42
	
42
	 * "Vertical" alignment constant. Figures should be placed below the line.
43
	/**
43
	 */
44
	 * "Horizontal" alignment constant. Figures should be placed in the center
44
	public static final int BELOW = 2;
45
	 * of the points on the line.
45
46
	 */
46
	/**
47
	public static final int CENTER = 0;
47
	 * "Horizontal" alignment constant. Figures should be placed in the center
48
	
48
	 * of the points on the line.
49
	/**
49
	 */
50
	 * "Horizontal" alignment constant. Figures should be placed at the beginning
50
	public static final int CENTER = 0;
51
	 * of the line. Figures will be anchored so that they have one end at the
51
52
	 * beginning of the connection, not so that they are centered at the start
52
	/**
53
	 * point. Which end of the figure is placed at that point will depend
53
	 * "Horizontal" alignment constant. Figures should be placed at the
54
	 * on the direction of the first two points.
54
	 * beginning of the line. Figures will be anchored so that they have one end
55
	 */
55
	 * at the beginning of the connection, not so that they are centered at the
56
	public static final int BEGINNING = 1;
56
	 * start point. Which end of the figure is placed at that point will depend
57
	
57
	 * on the direction of the first two points.
58
	/**
58
	 */
59
	 * "Horizontal" alignment constant. Figures should be placed at the end of
59
	public static final int BEGINNING = 1;
60
	 * the line. Figures will be anchored so that they have one end at the
60
61
	 * beginning of the connection, not so that they are centered at the end
61
	/**
62
	 * point. Which end of the figure is placed at that point will depend
62
	 * "Horizontal" alignment constant. Figures should be placed at the end of
63
	 * on the direction of the last two points.
63
	 * the line. Figures will be anchored so that they have one end at the
64
	 */
64
	 * beginning of the connection, not so that they are centered at the end
65
	public static final int END = 2;
65
	 * point. Which end of the figure is placed at that point will depend on the
66
	
66
	 * direction of the last two points.
67
	/**
67
	 */
68
	 * "Horizontal" alignment constant. Figures should be centered between the
68
	public static final int END = 2;
69
	 * first two points on the connection. For connections with only two points, 
69
70
	 * this will behave the same as CENTER.
70
	/**
71
	 */
71
	 * "Horizontal" alignment constant. Figures should be centered between the
72
	public static final int CENTER_BEGINNING = 3;
72
	 * first two points on the connection. For connections with only two points,
73
	
73
	 * this will behave the same as CENTER.
74
	/**
74
	 */
75
	 * "Horizontal" alignment constant. Figures should be centered between the
75
	public static final int CENTER_BEGINNING = 3;
76
	 * last two points on the connection. For connections with only two points,
76
77
	 * this will behave the same as CENTER.
77
	/**
78
	 */
78
	 * "Horizontal" alignment constant. Figures should be centered between the
79
	public static final int CENTER_END = 4;
79
	 * last two points on the connection. For connections with only two points,
80
	private int horizontal;
80
	 * this will behave the same as CENTER.
81
	private int vertical;
81
	 */
82
	private Connection connection;
82
	public static final int CENTER_END = 4;
83
	/**
83
	private int horizontal;
84
	 * @param connection
84
	private int vertical;
85
	 */
85
	private Connection connection;
86
	public AligningBendpointLocator(Connection connection) {
86
87
		this(connection, CENTER, MIDDLE);
87
	/**
88
	}
88
	 * @param connection
89
	
89
	 */
90
	public AligningBendpointLocator(Connection connection, int horizontal, int vertical) {
90
	public AligningBendpointLocator(Connection connection) {
91
		this.connection = connection;
91
		this(connection, CENTER, MIDDLE);
92
		this.horizontal = horizontal;
92
	}
93
		this.vertical = vertical;
93
94
	}
94
	public AligningBendpointLocator(Connection connection, int horizontal,
95
	
95
			int vertical) {
96
	/* (non-Javadoc)
96
		this.connection = connection;
97
	 * @see org.eclipse.draw2d.ConnectionLocator#getReferencePoint()
97
		this.horizontal = horizontal;
98
	 */
98
		this.vertical = vertical;
99
	protected Point getReferencePoint() {
99
	}
100
		PointList points = getConnection().getPoints();
100
101
		Point p = points.getMidpoint().getCopy();
101
	/*
102
		PointList tempPoints = new PointList();
102
	 * (non-Javadoc)
103
		switch (horizontal) {
103
	 * 
104
		case BEGINNING:
104
	 * @see org.eclipse.draw2d.ConnectionLocator#getReferencePoint()
105
			p = points.getFirstPoint().getCopy();
105
	 */
106
			break;
106
	protected Point getReferencePoint() {
107
		case END:
107
		PointList points = getConnection().getPoints();
108
			p = points.getLastPoint().getCopy();
108
		Point p = points.getMidpoint().getCopy();
109
			break;
109
		PointList tempPoints = new PointList();
110
		case CENTER_BEGINNING:
110
		switch (horizontal) {
111
			tempPoints.addPoint(points.getFirstPoint().getCopy());
111
		case BEGINNING:
112
			tempPoints.addPoint(points.getPoint(1).getCopy());
112
			p = points.getFirstPoint().getCopy();
113
			p = tempPoints.getMidpoint().getCopy();
113
			break;
114
			break;
114
		case END:
115
		case CENTER_END:
115
			p = points.getLastPoint().getCopy();
116
			tempPoints = new PointList();
116
			break;
117
			int s = points.size();
117
		case CENTER_BEGINNING:
118
			tempPoints.addPoint(points.getLastPoint().getCopy());
118
			tempPoints.addPoint(points.getFirstPoint().getCopy());
119
			tempPoints.addPoint(points.getPoint(s-2).getCopy());
119
			tempPoints.addPoint(points.getPoint(1).getCopy());
120
			p = tempPoints.getMidpoint().getCopy();
120
			p = tempPoints.getMidpoint().getCopy();
121
		case CENTER:
121
			break;
122
		default:
122
		case CENTER_END:
123
			p = points.getMidpoint().getCopy();
123
			tempPoints = new PointList();
124
		}
124
			int s = points.size();
125
		return p;
125
			tempPoints.addPoint(points.getLastPoint().getCopy());
126
	}
126
			tempPoints.addPoint(points.getPoint(s - 2).getCopy());
127
	/**
127
			p = tempPoints.getMidpoint().getCopy();
128
	 * Recalculates the position of the figure and returns the updated bounds.
128
		case CENTER:
129
	 * @param target The figure to relocate
129
		default:
130
	 */
130
			p = points.getMidpoint().getCopy();
131
	public void relocate(IFigure target) {
131
		}
132
		Dimension prefSize = target.getPreferredSize();
132
		return p;
133
		Point center = getReferencePoint();
133
	}
134
		calculatePosition();
134
135
		//@tag zest(bug(GEFProblem)) : there seems to be a bug in GEF that if the following is done, then labels get printed in the wrong location
135
	/**
136
		//target.translateToRelative(center);
136
	 * Recalculates the position of the figure and returns the updated bounds.
137
		target.setBounds(getNewBounds(prefSize, center));
137
	 * 
138
	}
138
	 * @param target
139
	
139
	 *            The figure to relocate
140
	/**
140
	 */
141
	 * Translates the center point depending on the horizontal and veritical
141
	public void relocate(IFigure target) {
142
	 * alignments based on the given bounds.
142
		Dimension prefSize = target.getPreferredSize();
143
	 * @param center
143
		Point center = getReferencePoint();
144
	 */
144
		calculatePosition();
145
	private void calculatePosition() {
145
		// @tag zest(bug(GEFProblem)) : there seems to be a bug in GEF that if
146
		PointList points = getConnection().getPoints();
146
		// the following is done, then labels get printed in the wrong location
147
		int position = 0;
147
		// target.translateToRelative(center);
148
		switch(horizontal) {
148
		target.setBounds(getNewBounds(prefSize, center));
149
		case BEGINNING:
149
	}
150
			Point first = points.getFirstPoint();
150
151
			Point next = points.getPoint(1);
151
	/**
152
			if (first.x <= next.x) 
152
	 * Translates the center point depending on the horizontal and veritical
153
				position |= PositionConstants.EAST;
153
	 * alignments based on the given bounds.
154
			else
154
	 * 
155
				position |= PositionConstants.WEST;
155
	 * @param center
156
			break;
156
	 */
157
		case END:
157
	private void calculatePosition() {
158
			Point last = points.getLastPoint();
158
		PointList points = getConnection().getPoints();
159
			int s = points.size();
159
		int position = 0;
160
			Point before = points.getPoint(s-1);
160
		switch (horizontal) {
161
			if (last.x <= before.x) 
161
		case BEGINNING:
162
				position |= PositionConstants.EAST;
162
			Point first = points.getFirstPoint();
163
			else
163
			Point next = points.getPoint(1);
164
				position |= PositionConstants.WEST;
164
			if (first.x <= next.x) {
165
			break;
165
				position |= PositionConstants.EAST;
166
		}
166
			} else {
167
		if (position == 0) position = PositionConstants.CENTER;
167
				position |= PositionConstants.WEST;
168
		switch (vertical) {
168
			}
169
		case ABOVE:
169
			break;
170
			position |= PositionConstants.NORTH;
170
		case END:
171
			break;
171
			Point last = points.getLastPoint();
172
		case BELOW:
172
			int s = points.size();
173
			position |= PositionConstants.SOUTH;
173
			Point before = points.getPoint(s - 1);
174
			break;
174
			if (last.x <= before.x) {
175
		case MIDDLE:
175
				position |= PositionConstants.EAST;
176
			position |= PositionConstants.MIDDLE;
176
			} else {
177
		}
177
				position |= PositionConstants.WEST;
178
		setRelativePosition(position);
178
			}
179
		
179
			break;
180
	}
180
		}
181
181
		if (position == 0) {
182
	/**
182
			position = PositionConstants.CENTER;
183
	 * @return the horizontal alignment.
183
		}
184
	 */
184
		switch (vertical) {
185
	public int getHorizontalAlignment() {
185
		case ABOVE:
186
		return horizontal;
186
			position |= PositionConstants.NORTH;
187
	}
187
			break;
188
	
188
		case BELOW:
189
	/**
189
			position |= PositionConstants.SOUTH;
190
	 * @param horizontal the horizontal alignment to set. One of CENTER,
190
			break;
191
	 * BEGINNING, END, CENTER_BEGINNING, or CENTER_END.
191
		case MIDDLE:
192
	 */
192
			position |= PositionConstants.MIDDLE;
193
	public void setHorizontalAlignment(int horizontal) {
193
		}
194
		this.horizontal = horizontal;
194
		setRelativePosition(position);
195
	}
195
196
	
196
	}
197
	/**
197
198
	 * @param vertical the vertical alignment to set. One of ABOVE, MIDDLE, or
198
	/**
199
	 * BELOW.
199
	 * @return the horizontal alignment.
200
	 */
200
	 */
201
	public void setVerticalAlginment(int vertical) {
201
	public int getHorizontalAlignment() {
202
		this.vertical = vertical;
202
		return horizontal;
203
	}
203
	}
204
	
204
205
	/**
205
	/**
206
	 * @return the vertical alginment.
206
	 * @param horizontal
207
	 */
207
	 *            the horizontal alignment to set. One of CENTER, BEGINNING,
208
	public int getVerticalAlignment() {
208
	 *            END, CENTER_BEGINNING, or CENTER_END.
209
		return vertical;
209
	 */
210
	}
210
	public void setHorizontalAlignment(int horizontal) {
211
	
211
		this.horizontal = horizontal;
212
	/**
212
	}
213
	 * @return the connection
213
214
	 */
214
	/**
215
	public Connection getConnection() {
215
	 * @param vertical
216
		return connection;
216
	 *            the vertical alignment to set. One of ABOVE, MIDDLE, or BELOW.
217
	}
217
	 */
218
218
	public void setVerticalAlginment(int vertical) {
219
}
219
		this.vertical = vertical;
220
	}
221
222
	/**
223
	 * @return the vertical alginment.
224
	 */
225
	public int getVerticalAlignment() {
226
		return vertical;
227
	}
228
229
	/**
230
	 * @return the connection
231
	 */
232
	public Connection getConnection() {
233
		return connection;
234
	}
235
236
}
(-)src/org/eclipse/zest/core/widgets/internal/AspectRatioFreeformLayer.java (-231 / +235 lines)
Lines 1-231 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.internal;
10
package org.eclipse.zest.core.widgets.internal;
11
11
12
import org.eclipse.draw2d.FreeformFigure;
12
import org.eclipse.draw2d.FreeformFigure;
13
import org.eclipse.draw2d.FreeformLayer;
13
import org.eclipse.draw2d.FreeformLayer;
14
import org.eclipse.draw2d.FreeformLayout;
14
import org.eclipse.draw2d.FreeformLayout;
15
import org.eclipse.draw2d.Graphics;
15
import org.eclipse.draw2d.Graphics;
16
import org.eclipse.draw2d.MarginBorder;
16
import org.eclipse.draw2d.MarginBorder;
17
import org.eclipse.draw2d.ScalableFigure;
17
import org.eclipse.draw2d.ScalableFigure;
18
import org.eclipse.draw2d.geometry.Dimension;
18
import org.eclipse.draw2d.geometry.Dimension;
19
import org.eclipse.draw2d.geometry.Point;
19
import org.eclipse.draw2d.geometry.Point;
20
import org.eclipse.draw2d.geometry.PointList;
20
import org.eclipse.draw2d.geometry.PointList;
21
import org.eclipse.draw2d.geometry.PrecisionDimension;
21
import org.eclipse.draw2d.geometry.PrecisionDimension;
22
import org.eclipse.draw2d.geometry.PrecisionPoint;
22
import org.eclipse.draw2d.geometry.PrecisionPoint;
23
import org.eclipse.draw2d.geometry.PrecisionRectangle;
23
import org.eclipse.draw2d.geometry.PrecisionRectangle;
24
import org.eclipse.draw2d.geometry.Rectangle;
24
import org.eclipse.draw2d.geometry.Rectangle;
25
import org.eclipse.draw2d.geometry.Translatable;
25
import org.eclipse.draw2d.geometry.Translatable;
26
import org.eclipse.draw2d.text.CaretInfo;
26
import org.eclipse.draw2d.text.CaretInfo;
27
27
28
//@tag zest.bug.156286-Scaling.fix : make this implement scalable figure so that a zoom manager can be used on GraphEditParts.
28
//@tag zest.bug.156286-Scaling.fix : make this implement scalable figure so that a zoom manager can be used on GraphEditParts.
29
public class AspectRatioFreeformLayer extends FreeformLayer implements ScalableFigure, FreeformFigure {
29
public class AspectRatioFreeformLayer extends FreeformLayer implements
30
30
		ScalableFigure, FreeformFigure {
31
	private double widthScale = 1.0;
31
32
	private double heigthScale = 1.0;
32
	private double widthScale = 1.0;
33
33
	private double heigthScale = 1.0;
34
	public AspectRatioFreeformLayer(String debugLabel) {
34
35
		widthScale = 1D;
35
	public AspectRatioFreeformLayer(String debugLabel) {
36
		heigthScale = 1D;
36
		widthScale = 1D;
37
		setLayoutManager(new FreeformLayout());
37
		heigthScale = 1D;
38
		setBorder(new MarginBorder(5));
38
		setLayoutManager(new FreeformLayout());
39
39
		setBorder(new MarginBorder(5));
40
		//setOpaque(false);
40
41
	}
41
		// setOpaque(false);
42
42
	}
43
	protected boolean isValidationRoot() {
43
44
		return true;
44
	protected boolean isValidationRoot() {
45
	}
45
		return true;
46
46
	}
47
	public void setScale(double wScale, double hScale) {
47
48
		this.widthScale = wScale;
48
	public void setScale(double wScale, double hScale) {
49
		this.heigthScale = hScale;
49
		this.widthScale = wScale;
50
	}
50
		this.heigthScale = hScale;
51
51
	}
52
	public double getWidthScale() {
52
53
		return this.widthScale;
53
	public double getWidthScale() {
54
	}
54
		return this.widthScale;
55
55
	}
56
	public double getHeightScale() {
56
57
		return this.heigthScale;
57
	public double getHeightScale() {
58
	}
58
		return this.heigthScale;
59
59
	}
60
	/*
60
61
	public boolean isCoordinateSystem() {
61
	/*
62
		// TODO Auto-generated method stub
62
	 * public boolean isCoordinateSystem() { // TODO Auto-generated method stub
63
		return true;
63
	 * return true; }
64
	}
64
	 */
65
	*/
65
66
66
	public double getScale() {
67
	public double getScale() {
67
		// TODO Auto-generated method stub
68
		// TODO Auto-generated method stub
68
		throw new RuntimeException("Operation not supported");
69
		throw new RuntimeException("Operation not supported");
69
		// return this.widthScale;
70
		// return this.widthScale;
70
71
71
		// throw new RuntimeException("Operation Not supported");
72
		//throw new RuntimeException("Operation Not supported");
72
	}
73
	}
73
74
74
	public void setScale(double scale) {
75
	public void setScale(double scale) {
75
		// super.setScale( scale );
76
		//super.setScale( scale );
76
		this.widthScale = scale;
77
		this.widthScale = scale;
77
		this.heigthScale = scale;
78
		this.heigthScale = scale;
78
		revalidate();
79
		revalidate();
79
		repaint();
80
		repaint();
80
		// System.out.println("Operation not supported");
81
		//System.out.println("Operation not supported");
81
		// throw new RuntimeException("Operation not supported");
82
		//throw new RuntimeException("Operation not supported");
82
	}
83
	}
83
84
84
	/**
85
	/**
85
	 * @see org.eclipse.draw2d.Figure#getClientArea()
86
	 * @see org.eclipse.draw2d.Figure#getClientArea()
86
	 */
87
	 */
87
88
88
	public Rectangle getClientArea(Rectangle rect) {
89
	public Rectangle getClientArea(Rectangle rect) {
89
		// return super.getClientArea(rect);
90
		//return super.getClientArea(rect);
90
91
91
		rect.width /= widthScale;
92
		rect.width /= widthScale;
92
		rect.height /= heigthScale;
93
		rect.height /= heigthScale;
93
		rect.x /= widthScale;
94
		rect.x /= widthScale;
94
		rect.y /= heigthScale;
95
		rect.y /= heigthScale;
95
		return rect;
96
		return rect;
96
	}
97
	}
97
98
98
	public Dimension getPreferredSize(int wHint, int hHint) {
99
	public Dimension getPreferredSize(int wHint, int hHint) {
99
		Dimension d = super.getPreferredSize(wHint, hHint);
100
		Dimension d = super.getPreferredSize(wHint, hHint);
100
		int w = getInsets().getWidth();
101
		int w = getInsets().getWidth();
101
		int h = getInsets().getHeight();
102
		int h = getInsets().getHeight();
102
		return d.getExpanded(-w, -h).scale(widthScale, heigthScale)
103
		return d.getExpanded(-w, -h).scale(widthScale, heigthScale).expand(w, h);
103
				.expand(w, h);
104
	}
104
	}
105
105
106
	public void translateFromParent(Translatable t) {
106
	public void translateFromParent(Translatable t) {
107
		super.translateFromParent(t);
107
		super.translateFromParent(t);
108
		//t.performScale(1/widthScale);
108
		// t.performScale(1/widthScale);
109
109
110
		if (t instanceof PrecisionRectangle) {
110
		if (t instanceof PrecisionRectangle) {
111
			PrecisionRectangle r = (PrecisionRectangle) t;
111
			PrecisionRectangle r = (PrecisionRectangle) t;
112
			r.preciseX *= 1 / widthScale;
112
			r.preciseX *= 1 / widthScale;
113
			r.preciseY *= 1 / heigthScale;
113
			r.preciseY *= 1 / heigthScale;
114
			r.preciseWidth *= 1 / widthScale;
114
			r.preciseWidth *= 1 / widthScale;
115
			r.preciseHeight *= 1 / heigthScale;
115
			r.preciseHeight *= 1 / heigthScale;
116
			r.updateInts();
116
			r.updateInts();
117
		} else if (t instanceof Rectangle) {
117
		} else if (t instanceof Rectangle) {
118
			Rectangle r = (Rectangle) t;
118
			Rectangle r = (Rectangle) t;
119
			r.scale(1 / widthScale, 1 / heigthScale);
119
			r.scale(1 / widthScale, 1 / heigthScale);
120
		} else if (t instanceof CaretInfo) {
120
		} else if (t instanceof CaretInfo) {
121
			CaretInfo c = (CaretInfo) t;
121
			CaretInfo c = (CaretInfo) t;
122
			c.performScale(1 / heigthScale);
122
			c.performScale(1 / heigthScale);
123
		} else if (t instanceof PrecisionDimension) {
123
		} else if (t instanceof PrecisionDimension) {
124
			PrecisionDimension d = (PrecisionDimension) t;
124
			PrecisionDimension d = (PrecisionDimension) t;
125
			d.preciseWidth *= 1 / widthScale;
125
			d.preciseWidth *= 1 / widthScale;
126
			d.preciseHeight *= 1 / heigthScale;
126
			d.preciseHeight *= 1 / heigthScale;
127
			d.updateInts();
127
			d.updateInts();
128
		} else if (t instanceof Dimension) {
128
		} else if (t instanceof Dimension) {
129
			Dimension d = (Dimension) t;
129
			Dimension d = (Dimension) t;
130
			d.scale(1 / widthScale, 1 / heigthScale);
130
			d.scale(1 / widthScale, 1 / heigthScale);
131
		} else if (t instanceof PrecisionPoint) {
131
		} else if (t instanceof PrecisionPoint) {
132
			PrecisionPoint p = (PrecisionPoint) t;
132
			PrecisionPoint p = (PrecisionPoint) t;
133
			p.preciseX *= 1 / widthScale;
133
			p.preciseX *= 1 / widthScale;
134
			p.preciseY *= 1 / heigthScale;
134
			p.preciseY *= 1 / heigthScale;
135
			p.updateInts();
135
			p.updateInts();
136
		} else if (t instanceof Point) {
136
		} else if (t instanceof Point) {
137
			Point p = (Point) t;
137
			Point p = (Point) t;
138
			p.scale(1 / widthScale, 1 / heigthScale);
138
			p.scale(1 / widthScale, 1 / heigthScale);
139
		} else if (t instanceof PointList) {
139
		} else if (t instanceof PointList) {
140
			throw new RuntimeException("PointList not supported in AspectRatioScale");
140
			throw new RuntimeException(
141
		} else {
141
					"PointList not supported in AspectRatioScale");
142
			throw new RuntimeException(t.toString() + " not supported in AspectRatioScale");
142
		} else {
143
		}
143
			throw new RuntimeException(t.toString()
144
144
					+ " not supported in AspectRatioScale");
145
		//t.performScale(1/widthScale);		
145
		}
146
	}
146
147
147
		// t.performScale(1/widthScale);
148
	public void translateToParent(Translatable t) {
148
	}
149
		//t.performScale(widthScale);
149
150
150
	public void translateToParent(Translatable t) {
151
		if (t instanceof PrecisionRectangle) {
151
		// t.performScale(widthScale);
152
			PrecisionRectangle r = (PrecisionRectangle) t;
152
153
			r.preciseX *= widthScale;
153
		if (t instanceof PrecisionRectangle) {
154
			r.preciseY *= heigthScale;
154
			PrecisionRectangle r = (PrecisionRectangle) t;
155
			r.preciseWidth *= widthScale;
155
			r.preciseX *= widthScale;
156
			r.preciseHeight *= heigthScale;
156
			r.preciseY *= heigthScale;
157
			r.updateInts();
157
			r.preciseWidth *= widthScale;
158
		} else if (t instanceof Rectangle) {
158
			r.preciseHeight *= heigthScale;
159
			Rectangle r = (Rectangle) t;
159
			r.updateInts();
160
			//r.performScale(widthScale);
160
		} else if (t instanceof Rectangle) {
161
			r.scale(widthScale, heigthScale);
161
			Rectangle r = (Rectangle) t;
162
		} else if (t instanceof CaretInfo) {
162
			// r.performScale(widthScale);
163
			CaretInfo c = (CaretInfo) t;
163
			r.scale(widthScale, heigthScale);
164
			c.performScale(heigthScale);
164
		} else if (t instanceof CaretInfo) {
165
		} else if (t instanceof PrecisionDimension) {
165
			CaretInfo c = (CaretInfo) t;
166
			PrecisionDimension d = (PrecisionDimension) t;
166
			c.performScale(heigthScale);
167
			d.preciseWidth *= widthScale;
167
		} else if (t instanceof PrecisionDimension) {
168
			d.preciseHeight *= heigthScale;
168
			PrecisionDimension d = (PrecisionDimension) t;
169
			d.updateInts();
169
			d.preciseWidth *= widthScale;
170
		} else if (t instanceof Dimension) {
170
			d.preciseHeight *= heigthScale;
171
			Dimension d = (Dimension) t;
171
			d.updateInts();
172
			d.scale(widthScale, heigthScale);
172
		} else if (t instanceof Dimension) {
173
		} else if (t instanceof PrecisionPoint) {
173
			Dimension d = (Dimension) t;
174
			PrecisionPoint p = (PrecisionPoint) t;
174
			d.scale(widthScale, heigthScale);
175
			p.preciseX *= widthScale;
175
		} else if (t instanceof PrecisionPoint) {
176
			p.preciseY *= heigthScale;
176
			PrecisionPoint p = (PrecisionPoint) t;
177
			p.updateInts();
177
			p.preciseX *= widthScale;
178
		} else if (t instanceof Point) {
178
			p.preciseY *= heigthScale;
179
			Point p = (Point) t;
179
			p.updateInts();
180
			p.scale(widthScale, heigthScale);
180
		} else if (t instanceof Point) {
181
		} else if (t instanceof PointList) {
181
			Point p = (Point) t;
182
			throw new RuntimeException("PointList not supported in AspectRatioScale");
182
			p.scale(widthScale, heigthScale);
183
		} else {
183
		} else if (t instanceof PointList) {
184
			throw new RuntimeException(t.toString() + " not supported in AspectRatioScale");
184
			throw new RuntimeException(
185
		}
185
					"PointList not supported in AspectRatioScale");
186
186
		} else {
187
		super.translateToParent(t);
187
			throw new RuntimeException(t.toString()
188
	}
188
					+ " not supported in AspectRatioScale");
189
189
		}
190
	//protected boolean useLocalCoordinates() {
190
191
	//	return true;
191
		super.translateToParent(t);
192
	//}
192
	}
193
193
194
	protected void paintClientArea(Graphics graphics) {
194
	// protected boolean useLocalCoordinates() {
195
195
	// return true;
196
		if (getChildren().isEmpty()) {
196
	// }
197
			return;
197
198
		}
198
	protected void paintClientArea(Graphics graphics) {
199
199
200
		XYScaledGraphics g = null;
200
		if (getChildren().isEmpty()) {
201
		boolean disposeGraphics = false;
201
			return;
202
		if (graphics instanceof XYScaledGraphics) {
202
		}
203
			g = (XYScaledGraphics) graphics;
203
204
		} else {
204
		XYScaledGraphics g = null;
205
			g = new XYScaledGraphics(graphics);
205
		boolean disposeGraphics = false;
206
			disposeGraphics = true;
206
		if (graphics instanceof XYScaledGraphics) {
207
		}
207
			g = (XYScaledGraphics) graphics;
208
208
		} else {
209
		boolean optimizeClip = getBorder() == null || getBorder().isOpaque();
209
			g = new XYScaledGraphics(graphics);
210
		if (!optimizeClip) {
210
			disposeGraphics = true;
211
			g.clipRect(getBounds().getCropped(getInsets()));
211
		}
212
		}
212
213
213
		boolean optimizeClip = getBorder() == null || getBorder().isOpaque();
214
		//g.translate((int)(getBounds().x + getInsets().left) , 
214
		if (!optimizeClip) {
215
		//		(int)(getBounds().y  +  getInsets().top) );
215
			g.clipRect(getBounds().getCropped(getInsets()));
216
216
		}
217
		g.scale(widthScale, heigthScale);
217
218
		//g.scale(widthScale);
218
		// g.translate((int)(getBounds().x + getInsets().left) ,
219
219
		// (int)(getBounds().y + getInsets().top) );
220
		//g.scale(widthScale);
220
221
		g.pushState();
221
		g.scale(widthScale, heigthScale);
222
		paintChildren(g);
222
		// g.scale(widthScale);
223
		g.popState();
223
224
		if (disposeGraphics) {
224
		// g.scale(widthScale);
225
			g.dispose();
225
		g.pushState();
226
			graphics.restoreState();
226
		paintChildren(g);
227
		}
227
		g.popState();
228
228
		if (disposeGraphics) {
229
	}
229
			g.dispose();
230
230
			graphics.restoreState();
231
}
231
		}
232
233
	}
234
235
}
(-)src/org/eclipse/zest/core/widgets/internal/CachedLabel.java (-247 / +255 lines)
Lines 1-247 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.internal;
10
package org.eclipse.zest.core.widgets.internal;
11
11
12
import org.eclipse.draw2d.Animation;
12
import org.eclipse.draw2d.Animation;
13
import org.eclipse.draw2d.ColorConstants;
13
import org.eclipse.draw2d.ColorConstants;
14
import org.eclipse.draw2d.Graphics;
14
import org.eclipse.draw2d.Graphics;
15
import org.eclipse.draw2d.Label;
15
import org.eclipse.draw2d.Label;
16
import org.eclipse.draw2d.SWTGraphics;
16
import org.eclipse.draw2d.SWTGraphics;
17
import org.eclipse.draw2d.ScaledGraphics;
17
import org.eclipse.draw2d.ScaledGraphics;
18
import org.eclipse.draw2d.geometry.Point;
18
import org.eclipse.draw2d.geometry.Point;
19
import org.eclipse.draw2d.geometry.Rectangle;
19
import org.eclipse.draw2d.geometry.Rectangle;
20
import org.eclipse.swt.graphics.Color;
20
import org.eclipse.swt.graphics.Color;
21
import org.eclipse.swt.graphics.Font;
21
import org.eclipse.swt.graphics.Font;
22
import org.eclipse.swt.graphics.GC;
22
import org.eclipse.swt.graphics.GC;
23
import org.eclipse.swt.graphics.Image;
23
import org.eclipse.swt.graphics.Image;
24
import org.eclipse.swt.widgets.Display;
24
import org.eclipse.swt.widgets.Display;
25
25
26
/**
26
/**
27
 * A cached label to improve performance of text drawing under linux
27
 * A cached label to improve performance of text drawing under linux
28
 * 
28
 * 
29
 * @author Ian Bull
29
 * @author Ian Bull
30
 * 
30
 * 
31
 */
31
 */
32
public abstract class CachedLabel extends Label {
32
public abstract class CachedLabel extends Label {
33
33
34
	/*
34
	/*
35
	 * (non-Javadoc)
35
	 * (non-Javadoc)
36
	 * 
36
	 * 
37
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
37
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
38
	 */
38
	 */
39
	Image cachedImage = null;
39
	Image cachedImage = null;
40
	boolean cacheLabel = false;
40
	boolean cacheLabel = false;
41
	boolean invalidationRequired = false;
41
	boolean invalidationRequired = false;
42
42
43
	/**
43
	/**
44
	 * CachedLabel constructor.
44
	 * CachedLabel constructor.
45
	 * 
45
	 * 
46
	 * @param cacheLabel
46
	 * @param cacheLabel
47
	 *            Should the label be cached, or should the text be re-layedout
47
	 *            Should the label be cached, or should the text be re-layedout
48
	 *            each time
48
	 *            each time
49
	 */
49
	 */
50
	public CachedLabel(boolean cacheLabel) {
50
	public CachedLabel(boolean cacheLabel) {
51
		this.cacheLabel = cacheLabel;
51
		this.cacheLabel = cacheLabel;
52
	}
52
	}
53
53
54
	/*
54
	/*
55
	 * (non-Javadoc)
55
	 * (non-Javadoc)
56
	 * 
56
	 * 
57
	 * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image)
57
	 * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image)
58
	 */
58
	 */
59
	public void setIcon(Image image) {
59
	public void setIcon(Image image) {
60
		updateInvalidation();
60
		updateInvalidation();
61
		super.setIcon(image);
61
		super.setIcon(image);
62
	}
62
	}
63
63
64
	/*
64
	/*
65
	 * (non-Javadoc)
65
	 * (non-Javadoc)
66
	 * 
66
	 * 
67
	 * @see org.eclipse.draw2d.Figure#setForegroundColor(org.eclipse.swt.graphics.Color)
67
	 * @see
68
	 */
68
	 * org.eclipse.draw2d.Figure#setForegroundColor(org.eclipse.swt.graphics
69
	public void setForegroundColor(Color fg) {
69
	 * .Color)
70
		updateInvalidation();
70
	 */
71
		super.setForegroundColor(fg);
71
	public void setForegroundColor(Color fg) {
72
	}
72
		updateInvalidation();
73
73
		super.setForegroundColor(fg);
74
	/*
74
	}
75
	 * (non-Javadoc)
75
76
	 * 
76
	/*
77
	 * @see org.eclipse.draw2d.Figure#setBackgroundColor(org.eclipse.swt.graphics.Color)
77
	 * (non-Javadoc)
78
	 */
78
	 * 
79
	public void setBackgroundColor(Color bg) {
79
	 * @see
80
		updateInvalidation();
80
	 * org.eclipse.draw2d.Figure#setBackgroundColor(org.eclipse.swt.graphics
81
		super.setBackgroundColor(bg);
81
	 * .Color)
82
	}
82
	 */
83
83
	public void setBackgroundColor(Color bg) {
84
	/*
84
		updateInvalidation();
85
	 * (non-Javadoc)
85
		super.setBackgroundColor(bg);
86
	 * 
86
	}
87
	 * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font)
87
88
	 */
88
	/*
89
	public void setFont(Font f) {
89
	 * (non-Javadoc)
90
		updateInvalidation();
90
	 * 
91
		super.setFont(f);
91
	 * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font)
92
	}
92
	 */
93
93
	public void setFont(Font f) {
94
	/*
94
		updateInvalidation();
95
	 * (non-Javadoc)
95
		super.setFont(f);
96
	 * 
96
	}
97
	 * @see org.eclipse.draw2d.Label#setText(java.lang.String)
97
98
	 */
98
	/*
99
	public void setText(String s) {
99
	 * (non-Javadoc)
100
		updateInvalidation();
100
	 * 
101
		super.setText(s);
101
	 * @see org.eclipse.draw2d.Label#setText(java.lang.String)
102
	}
102
	 */
103
103
	public void setText(String s) {
104
	/*
104
		updateInvalidation();
105
	 * (non-Javadoc)
105
		super.setText(s);
106
	 * 
106
	}
107
	 * @see org.eclipse.draw2d.Figure#setSize(int, int)
107
108
	 */
108
	/*
109
	public void setSize(int w, int h) {
109
	 * (non-Javadoc)
110
		updateInvalidation();
110
	 * 
111
111
	 * @see org.eclipse.draw2d.Figure#setSize(int, int)
112
		if (cachedImage != null && shouldInvalidateCache()) {
112
	 */
113
			cleanImage();
113
	public void setSize(int w, int h) {
114
		}
114
		updateInvalidation();
115
		super.setSize(w, h);
115
116
	}
116
		if (cachedImage != null && shouldInvalidateCache()) {
117
117
			cleanImage();
118
	/*
118
		}
119
	 * (non-Javadoc)
119
		super.setSize(w, h);
120
	 * 
120
	}
121
	 * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle)
121
122
	 */
122
	/*
123
	public void setBounds(Rectangle rect) {
123
	 * (non-Javadoc)
124
		boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height);
124
	 * 
125
125
	 * @see
126
		if (resize && Animation.isAnimating()) {
126
	 * org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle
127
			updateInvalidation();
127
	 * )
128
		}
128
	 */
129
		if (resize && shouldInvalidateCache() && cachedImage != null) {
129
	public void setBounds(Rectangle rect) {
130
			cleanImage();
130
		boolean resize = (rect.width != bounds.width)
131
		}
131
				|| (rect.height != bounds.height);
132
132
133
		super.setBounds(rect);
133
		if (resize && Animation.isAnimating()) {
134
	}
134
			updateInvalidation();
135
135
		}
136
	/**
136
		if (resize && shouldInvalidateCache() && cachedImage != null) {
137
	 * Override this method to return the background colour for the text Note:
137
			cleanImage();
138
	 * Text must have a background color since it is being stored in an image
138
		}
139
	 * (You can set it to white if you want)
139
140
	 * 
140
		super.setBounds(rect);
141
	 * @return
141
	}
142
	 */
142
143
	protected abstract Color getBackgroundTextColor();
143
	/**
144
144
	 * Override this method to return the background colour for the text Note:
145
	/*
145
	 * Text must have a background color since it is being stored in an image
146
	 * (non-Javadoc)
146
	 * (You can set it to white if you want)
147
	 * 
147
	 * 
148
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
148
	 * @return
149
	 */
149
	 */
150
	static Rectangle tempRect = new Rectangle();
150
	protected abstract Color getBackgroundTextColor();
151
151
152
	protected void paintFigure(Graphics graphics) {
152
	/*
153
		if (graphics.getClass() == ScaledGraphics.class) {
153
	 * (non-Javadoc)
154
			if (((ScaledGraphics) graphics).getAbsoluteScale() < 0.30) {
154
	 * 
155
				return;
155
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
156
			}
156
	 */
157
		}
157
	static Rectangle tempRect = new Rectangle();
158
		if (!cacheLabel) {
158
159
			if (isOpaque()) {
159
	protected void paintFigure(Graphics graphics) {
160
				super.paintFigure(graphics);
160
		if (graphics.getClass() == ScaledGraphics.class) {
161
			}
161
			if (((ScaledGraphics) graphics).getAbsoluteScale() < 0.30) {
162
			Rectangle bounds = getBounds();
162
				return;
163
			graphics.translate(bounds.x, bounds.y);
163
			}
164
			if (getIcon() != null) {
164
		}
165
				graphics.drawImage(getIcon(), getIconLocation());
165
		if (!cacheLabel) {
166
			}
166
			if (isOpaque()) {
167
			if (!isEnabled()) {
167
				super.paintFigure(graphics);
168
				graphics.translate(1, 1);
168
			}
169
				graphics.setForegroundColor(ColorConstants.buttonLightest);
169
			Rectangle bounds = getBounds();
170
				graphics.drawText(getSubStringText(), getTextLocation());
170
			graphics.translate(bounds.x, bounds.y);
171
				graphics.translate(-1, -1);
171
			if (getIcon() != null) {
172
				graphics.setForegroundColor(ColorConstants.buttonDarker);
172
				graphics.drawImage(getIcon(), getIconLocation());
173
			}
173
			}
174
			graphics.drawText(getText(), getTextLocation());
174
			if (!isEnabled()) {
175
			graphics.translate(-bounds.x, -bounds.y);
175
				graphics.translate(1, 1);
176
			return;
176
				graphics.setForegroundColor(ColorConstants.buttonLightest);
177
		}
177
				graphics.drawText(getSubStringText(), getTextLocation());
178
178
				graphics.translate(-1, -1);
179
		if (isOpaque()) {
179
				graphics.setForegroundColor(ColorConstants.buttonDarker);
180
			graphics.fillRectangle(getBounds());
180
			}
181
		}
181
			graphics.drawText(getText(), getTextLocation());
182
		Rectangle bounds = getBounds();
182
			graphics.translate(-bounds.x, -bounds.y);
183
		graphics.translate(bounds.x, bounds.y);
183
			return;
184
184
		}
185
		Image icon = getIcon();
185
186
186
		if (isOpaque()) {
187
		if (icon != null) {
187
			graphics.fillRectangle(getBounds());
188
			graphics.drawImage(icon, getIconLocation());
188
		}
189
		}
189
		Rectangle bounds = getBounds();
190
190
		graphics.translate(bounds.x, bounds.y);
191
		int width = getSubStringTextSize().width;
191
192
		int height = getSubStringTextSize().height;
192
		Image icon = getIcon();
193
193
194
		if (cachedImage == null || shouldInvalidateCache()) {
194
		if (icon != null) {
195
			invalidationRequired = false;
195
			graphics.drawImage(icon, getIconLocation());
196
			cleanImage();
196
		}
197
			cachedImage = new Image(Display.getCurrent(), width, height);
197
198
198
		int width = getSubStringTextSize().width;
199
			// @tag TODO : Dispose of the image properly
199
		int height = getSubStringTextSize().height;
200
			//ZestPlugin.getDefault().addImage(cachedImage.toString(), cachedImage);
200
201
201
		if (cachedImage == null || shouldInvalidateCache()) {
202
			GC gc = new GC(cachedImage);
202
			invalidationRequired = false;
203
203
			cleanImage();
204
			Graphics graphics2 = new SWTGraphics(gc);
204
			cachedImage = new Image(Display.getCurrent(), width, height);
205
			graphics2.setBackgroundColor(getBackgroundTextColor());
205
206
			graphics2.fillRectangle(0, 0, width, height);
206
			// @tag TODO : Dispose of the image properly
207
			graphics2.setForegroundColor(getForegroundColor());
207
			// ZestPlugin.getDefault().addImage(cachedImage.toString(),
208
			//graphics2.drawText(getSubStringText(), new Point(0, 0));
208
			// cachedImage);
209
			graphics2.drawText(getText(), new Point(0, 0));
209
210
			gc.dispose();
210
			GC gc = new GC(cachedImage);
211
211
212
		}
212
			Graphics graphics2 = new SWTGraphics(gc);
213
		graphics.drawImage(cachedImage, getTextLocation());
213
			graphics2.setBackgroundColor(getBackgroundTextColor());
214
		graphics.translate(-bounds.x, -bounds.y);
214
			graphics2.fillRectangle(0, 0, width, height);
215
		this.paintBorder(graphics);
215
			graphics2.setForegroundColor(getForegroundColor());
216
216
			// graphics2.drawText(getSubStringText(), new Point(0, 0));
217
	}
217
			graphics2.drawText(getText(), new Point(0, 0));
218
218
			gc.dispose();
219
	/**
219
220
	 * Determine if the image should be remade or not
220
		}
221
	 * 
221
		graphics.drawImage(cachedImage, getTextLocation());
222
	 * @return
222
		graphics.translate(-bounds.x, -bounds.y);
223
	 */
223
		this.paintBorder(graphics);
224
	private boolean shouldInvalidateCache() {
224
225
		if (invalidationRequired && !Animation.isAnimating()) {
225
	}
226
			return true;
226
227
		} else {
227
	/**
228
			return false;
228
	 * Determine if the image should be remade or not
229
		}
229
	 * 
230
	}
230
	 * @return
231
231
	 */
232
	/**
232
	private boolean shouldInvalidateCache() {
233
	 * Notifies the cache that the image will need updating.
233
		if (invalidationRequired && !Animation.isAnimating()) {
234
	 */
234
			return true;
235
	private void updateInvalidation() {
235
		} else {
236
		invalidationRequired = true;
236
			return false;
237
	}
237
		}
238
238
	}
239
	protected void cleanImage() {
239
240
		if (cachedImage != null) {
240
	/**
241
241
	 * Notifies the cache that the image will need updating.
242
			//ZestPlugin.getDefault().removeImage(cachedImage.toString());
242
	 */
243
			cachedImage.dispose();
243
	private void updateInvalidation() {
244
			cachedImage = null;
244
		invalidationRequired = true;
245
		}
245
	}
246
	}
246
247
}
247
	protected void cleanImage() {
248
		if (cachedImage != null) {
249
250
			// ZestPlugin.getDefault().removeImage(cachedImage.toString());
251
			cachedImage.dispose();
252
			cachedImage = null;
253
		}
254
	}
255
}
(-)src/org/eclipse/zest/core/widgets/internal/ContainerFigure.java (-18 / +18 lines)
Lines 1-18 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2009 EclipseSource and others. All rights reserved. This
2
 * Copyright (c) 2009-2010 EclipseSource and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
6
 * 
7
 * Contributors: EclipseSource - initial API and implementation
7
 * Contributors: EclipseSource - initial API and implementation
8
 ******************************************************************************/
8
 ******************************************************************************/
9
package org.eclipse.zest.core.widgets.internal;
9
package org.eclipse.zest.core.widgets.internal;
10
10
11
import org.eclipse.draw2d.Figure;
11
import org.eclipse.draw2d.Figure;
12
12
13
/**
13
/**
14
 * A figure used as the root of a container.
14
 * A figure used as the root of a container.
15
 */
15
 */
16
public class ContainerFigure extends Figure {
16
public class ContainerFigure extends Figure {
17
17
18
}
18
}
(-)src/org/eclipse/zest/core/widgets/internal/ExpandGraphLabel.java (-283 / +296 lines)
Lines 1-283 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.internal;
10
package org.eclipse.zest.core.widgets.internal;
11
11
12
import org.eclipse.draw2d.ActionEvent;
12
import org.eclipse.draw2d.ActionEvent;
13
import org.eclipse.draw2d.ActionListener;
13
import org.eclipse.draw2d.ActionListener;
14
import org.eclipse.draw2d.Clickable;
14
import org.eclipse.draw2d.Clickable;
15
import org.eclipse.draw2d.ColorConstants;
15
import org.eclipse.draw2d.ColorConstants;
16
import org.eclipse.draw2d.Figure;
16
import org.eclipse.draw2d.Figure;
17
import org.eclipse.draw2d.FigureUtilities;
17
import org.eclipse.draw2d.FigureUtilities;
18
import org.eclipse.draw2d.FreeformLayout;
18
import org.eclipse.draw2d.FreeformLayout;
19
import org.eclipse.draw2d.Graphics;
19
import org.eclipse.draw2d.Graphics;
20
import org.eclipse.draw2d.Label;
20
import org.eclipse.draw2d.Label;
21
import org.eclipse.draw2d.ToolbarLayout;
21
import org.eclipse.draw2d.ToolbarLayout;
22
import org.eclipse.draw2d.Triangle;
22
import org.eclipse.draw2d.Triangle;
23
import org.eclipse.draw2d.geometry.Dimension;
23
import org.eclipse.draw2d.geometry.Dimension;
24
import org.eclipse.draw2d.geometry.Point;
24
import org.eclipse.draw2d.geometry.Point;
25
import org.eclipse.draw2d.geometry.Rectangle;
25
import org.eclipse.draw2d.geometry.Rectangle;
26
import org.eclipse.zest.core.widgets.GraphContainer;
26
import org.eclipse.swt.graphics.Color;
27
import org.eclipse.swt.graphics.Color;
27
import org.eclipse.swt.graphics.Font;
28
import org.eclipse.swt.graphics.Font;
28
import org.eclipse.swt.graphics.Image;
29
import org.eclipse.swt.graphics.Image;
29
import org.eclipse.swt.graphics.RGB;
30
import org.eclipse.swt.graphics.RGB;
30
import org.eclipse.swt.widgets.Display;
31
import org.eclipse.swt.widgets.Display;
31
import org.eclipse.zest.core.widgets.GraphContainer;
32
32
33
public class ExpandGraphLabel extends Figure implements ActionListener {
33
public class ExpandGraphLabel extends Figure implements ActionListener {
34
34
35
	public static final int OPEN = 1;
35
	public static final int OPEN = 1;
36
	public static final int CLOSED = 2;
36
	public static final int CLOSED = 2;
37
	private int state = CLOSED;
37
	private int state = CLOSED;
38
	private Expander expander = null;
38
	private Expander expander = null;
39
39
40
	class Expander extends Clickable {
40
	class Expander extends Clickable {
41
		private Triangle triangle;
41
		private Triangle triangle;
42
42
43
		public Expander() {
43
		public Expander() {
44
			setStyle(Clickable.STYLE_TOGGLE);
44
			setStyle(Clickable.STYLE_TOGGLE);
45
			triangle = new Triangle();
45
			triangle = new Triangle();
46
			triangle.setSize(10, 10);
46
			triangle.setSize(10, 10);
47
			triangle.setBackgroundColor(ColorConstants.black);
47
			triangle.setBackgroundColor(ColorConstants.black);
48
			triangle.setForegroundColor(ColorConstants.black);
48
			triangle.setForegroundColor(ColorConstants.black);
49
			triangle.setFill(true);
49
			triangle.setFill(true);
50
			triangle.setDirection(Triangle.EAST);
50
			triangle.setDirection(Triangle.EAST);
51
			triangle.setLocation(new Point(5, 3));
51
			triangle.setLocation(new Point(5, 3));
52
			this.setLayoutManager(new FreeformLayout());
52
			this.setLayoutManager(new FreeformLayout());
53
			this.add(triangle);
53
			this.add(triangle);
54
			this.setPreferredSize(15, 15);
54
			this.setPreferredSize(15, 15);
55
			this.addActionListener(ExpandGraphLabel.this);
55
			this.addActionListener(ExpandGraphLabel.this);
56
		}
56
		}
57
57
58
		public void open() {
58
		public void open() {
59
			triangle.setDirection(Triangle.SOUTH);
59
			triangle.setDirection(Triangle.SOUTH);
60
		}
60
		}
61
61
62
		public void close() {
62
		public void close() {
63
			triangle.setDirection(Triangle.EAST);
63
			triangle.setDirection(Triangle.EAST);
64
		}
64
		}
65
65
66
	}
66
	}
67
67
68
	/**
68
	/**
69
	 * Sets the expander state (the little triangle) to ExpanderGraphLabel.OPEN or ExpanderGraphLabel.CLOSED
69
	 * Sets the expander state (the little triangle) to ExpanderGraphLabel.OPEN
70
	 * @param state
70
	 * or ExpanderGraphLabel.CLOSED
71
	 */
71
	 * 
72
	public void setExpandedState(int state) {
72
	 * @param state
73
		if (state == OPEN) {
73
	 */
74
			expander.open();
74
	public void setExpandedState(int state) {
75
		} else {
75
		if (state == OPEN) {
76
			expander.close();
76
			expander.open();
77
		}
77
		} else {
78
		this.state = state;
78
			expander.close();
79
	}
79
		}
80
80
		this.state = state;
81
	/*
81
	}
82
	 * (non-Javadoc)
82
83
	 * @see org.eclipse.draw2d.ActionListener#actionPerformed(org.eclipse.draw2d.ActionEvent)
83
	/*
84
	 */
84
	 * (non-Javadoc)
85
	public void actionPerformed(ActionEvent event) {
85
	 * 
86
		if (state == OPEN) {
86
	 * @see
87
			container.close(true);
87
	 * org.eclipse.draw2d.ActionListener#actionPerformed(org.eclipse.draw2d.
88
88
	 * ActionEvent)
89
		} else {
89
	 */
90
			container.open(true);
90
	public void actionPerformed(ActionEvent event) {
91
		}
91
		if (state == OPEN) {
92
	}
92
			container.close(true);
93
93
94
	private int arcWidth;
94
		} else {
95
	private Label label = null;
95
			container.open(true);
96
	private final GraphContainer container;
96
		}
97
	private ToolbarLayout layout;
97
	}
98
98
99
	public ExpandGraphLabel(GraphContainer container, boolean cacheLabel) {
99
	private int arcWidth;
100
		this(container, "", null, cacheLabel);
100
	private Label label = null;
101
	}
101
	private final GraphContainer container;
102
102
	private ToolbarLayout layout;
103
	public ExpandGraphLabel(GraphContainer container, Image i, boolean cacheLabel) {
103
104
		this(container, "", i, cacheLabel);
104
	public ExpandGraphLabel(GraphContainer container, boolean cacheLabel) {
105
	}
105
		this(container, "", null, cacheLabel);
106
106
	}
107
	public ExpandGraphLabel(GraphContainer container, String text, boolean cacheLabel) {
107
108
		this(container, text, null, cacheLabel);
108
	public ExpandGraphLabel(GraphContainer container, Image i,
109
	}
109
			boolean cacheLabel) {
110
110
		this(container, "", i, cacheLabel);
111
	public ExpandGraphLabel(GraphContainer container, String text, Image image, boolean cacheLabel) {
111
	}
112
		this.label = new Label(text) {
112
113
113
	public ExpandGraphLabel(GraphContainer container, String text,
114
			/*
114
			boolean cacheLabel) {
115
			 * This method is overwritten so that the text is not truncated.
115
		this(container, text, null, cacheLabel);
116
			 * (non-Javadoc)
116
	}
117
			 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
117
118
			 */
118
	public ExpandGraphLabel(GraphContainer container, String text, Image image,
119
			protected void paintFigure(Graphics graphics) {
119
			boolean cacheLabel) {
120
				if (isOpaque()) {
120
		this.label = new Label(text) {
121
					super.paintFigure(graphics);
121
122
				}
122
			/*
123
				Rectangle bounds = getBounds();
123
			 * This method is overwritten so that the text is not truncated.
124
				graphics.translate(bounds.x, bounds.y);
124
			 * (non-Javadoc)
125
				if (getIcon() != null) {
125
			 * 
126
					graphics.drawImage(getIcon(), getIconLocation());
126
			 * @see
127
				}
127
			 * org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
128
				if (!isEnabled()) {
128
			 */
129
					graphics.translate(1, 1);
129
			protected void paintFigure(Graphics graphics) {
130
					graphics.setForegroundColor(ColorConstants.buttonLightest);
130
				if (isOpaque()) {
131
					graphics.drawText(getSubStringText(), getTextLocation());
131
					super.paintFigure(graphics);
132
					graphics.translate(-1, -1);
132
				}
133
					graphics.setForegroundColor(ColorConstants.buttonDarker);
133
				Rectangle bounds = getBounds();
134
				}
134
				graphics.translate(bounds.x, bounds.y);
135
				graphics.drawText(getText(), getTextLocation());
135
				if (getIcon() != null) {
136
				graphics.translate(-bounds.x, -bounds.y);
136
					graphics.drawImage(getIcon(), getIconLocation());
137
			}
137
				}
138
		};
138
				if (!isEnabled()) {
139
		this.setText(text);
139
					graphics.translate(1, 1);
140
		this.setImage(image);
140
					graphics.setForegroundColor(ColorConstants.buttonLightest);
141
		this.container = container;
141
					graphics.drawText(getSubStringText(), getTextLocation());
142
		this.expander = new Expander();
142
					graphics.translate(-1, -1);
143
		this.arcWidth = 8;
143
					graphics.setForegroundColor(ColorConstants.buttonDarker);
144
		this.setFont(Display.getDefault().getSystemFont());
144
				}
145
		layout = new ToolbarLayout(true);
145
				graphics.drawText(getText(), getTextLocation());
146
		layout.setSpacing(5);
146
				graphics.translate(-bounds.x, -bounds.y);
147
		layout.setMinorAlignment(ToolbarLayout.ALIGN_CENTER);
147
			}
148
		this.setLayoutManager(layout);
148
		};
149
		this.add(this.expander);
149
		this.setText(text);
150
		this.add(this.label);
150
		this.setImage(image);
151
		//this.remove(this.label);
151
		this.container = container;
152
	}
152
		this.expander = new Expander();
153
153
		this.arcWidth = 8;
154
	/**
154
		this.setFont(Display.getDefault().getSystemFont());
155
	 * Adjust the bounds to make the text fit without truncation.
155
		layout = new ToolbarLayout(true);
156
	 */
156
		layout.setSpacing(5);
157
	protected void adjustBoundsToFit() {
157
		layout.setMinorAlignment(ToolbarLayout.ALIGN_CENTER);
158
		String text = getText();
158
		this.setLayoutManager(layout);
159
		if ((text != null) && (text.length() > 0)) {
159
		this.add(this.expander);
160
			Font font = getFont();
160
		this.add(this.label);
161
			if (font != null) {
161
		// this.remove(this.label);
162
				Dimension minSize = FigureUtilities.getTextExtents(text, font);
162
	}
163
				if (getIcon() != null) {
163
164
					org.eclipse.swt.graphics.Rectangle imageRect = getIcon().getBounds();
164
	/**
165
					int expandHeight = Math.max(imageRect.height - minSize.height, 0);
165
	 * Adjust the bounds to make the text fit without truncation.
166
					minSize.expand(imageRect.width + 4, expandHeight);
166
	 */
167
				}
167
	protected void adjustBoundsToFit() {
168
				minSize.expand(10 + (2 * 1) + 100, 4 + (2 * 1));
168
		String text = getText();
169
				label.setBounds(new Rectangle(getLocation(), minSize));
169
		if ((text != null) && (text.length() > 0)) {
170
			}
170
			Font font = getFont();
171
		}
171
			if (font != null) {
172
	}
172
				Dimension minSize = FigureUtilities.getTextExtents(text, font);
173
173
				if (getIcon() != null) {
174
	private Image getIcon() {
174
					org.eclipse.swt.graphics.Rectangle imageRect = getIcon()
175
		return this.label.getIcon();
175
							.getBounds();
176
	}
176
					int expandHeight = Math.max(imageRect.height
177
177
							- minSize.height, 0);
178
	private String getText() {
178
					minSize.expand(imageRect.width + 4, expandHeight);
179
		return this.label.getText();
179
				}
180
	}
180
				minSize.expand(10 + (2 * 1) + 100, 4 + (2 * 1));
181
181
				label.setBounds(new Rectangle(getLocation(), minSize));
182
	/*
182
			}
183
	 * (non-Javadoc)
183
		}
184
	 * 
184
	}
185
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
185
186
	 */
186
	private Image getIcon() {
187
	public void paint(Graphics graphics) {
187
		return this.label.getIcon();
188
188
	}
189
		int blue = getBackgroundColor().getBlue();
189
190
		blue = (int) (blue - (blue * 0.20));
190
	private String getText() {
191
		blue = blue > 0 ? blue : 0;
191
		return this.label.getText();
192
192
	}
193
		int red = getBackgroundColor().getRed();
193
194
		red = (int) (red - (red * 0.20));
194
	/*
195
		red = red > 0 ? red : 0;
195
	 * (non-Javadoc)
196
196
	 * 
197
		int green = getBackgroundColor().getGreen();
197
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
198
		green = (int) (green - (green * 0.20));
198
	 */
199
		green = green > 0 ? green : 0;
199
	public void paint(Graphics graphics) {
200
200
201
		Color lightenColor = new Color(Display.getCurrent(), new RGB(red, green, blue));
201
		int blue = getBackgroundColor().getBlue();
202
		graphics.setForegroundColor(lightenColor);
202
		blue = (int) (blue - (blue * 0.20));
203
		graphics.setBackgroundColor(getBackgroundColor());
203
		blue = blue > 0 ? blue : 0;
204
204
205
		graphics.pushState();
205
		int red = getBackgroundColor().getRed();
206
206
		red = (int) (red - (red * 0.20));
207
		// fill in the background
207
		red = red > 0 ? red : 0;
208
		Rectangle bounds = getBounds().getCopy();
208
209
		Rectangle r = bounds.getCopy();
209
		int green = getBackgroundColor().getGreen();
210
		//r.x += arcWidth / 2;
210
		green = (int) (green - (green * 0.20));
211
		r.y += arcWidth / 2;
211
		green = green > 0 ? green : 0;
212
		//r.width -= arcWidth;
212
213
		r.height -= arcWidth;
213
		Color lightenColor = new Color(Display.getCurrent(), new RGB(red,
214
214
				green, blue));
215
		Rectangle top = bounds.getCopy();
215
		graphics.setForegroundColor(lightenColor);
216
		top.height /= 2;
216
		graphics.setBackgroundColor(getBackgroundColor());
217
		//graphics.setForegroundColor(lightenColor);
217
218
		//graphics.setBackgroundColor(lightenColor);
218
		graphics.pushState();
219
		graphics.setForegroundColor(getBackgroundColor());
219
220
		graphics.setBackgroundColor(getBackgroundColor());
220
		// fill in the background
221
		graphics.fillRoundRectangle(top, arcWidth, arcWidth);
221
		Rectangle bounds = getBounds().getCopy();
222
222
		Rectangle r = bounds.getCopy();
223
		top.y = top.y + top.height;
223
		// r.x += arcWidth / 2;
224
		//graphics.setForegroundColor(getBackgroundColor());
224
		r.y += arcWidth / 2;
225
		//graphics.setBackgroundColor(getBackgroundColor());
225
		// r.width -= arcWidth;
226
		graphics.setForegroundColor(lightenColor);
226
		r.height -= arcWidth;
227
		graphics.setBackgroundColor(lightenColor);
227
228
		graphics.fillRoundRectangle(top, arcWidth, arcWidth);
228
		Rectangle top = bounds.getCopy();
229
229
		top.height /= 2;
230
		//graphics.setForegroundColor(lightenColor);
230
		// graphics.setForegroundColor(lightenColor);
231
		//graphics.setBackgroundColor(getBackgroundColor());
231
		// graphics.setBackgroundColor(lightenColor);
232
		graphics.setBackgroundColor(lightenColor);
232
		graphics.setForegroundColor(getBackgroundColor());
233
		graphics.setForegroundColor(getBackgroundColor());
233
		graphics.setBackgroundColor(getBackgroundColor());
234
		graphics.fillGradient(r, true);
234
		graphics.fillRoundRectangle(top, arcWidth, arcWidth);
235
235
236
		super.paint(graphics);
236
		top.y = top.y + top.height;
237
		graphics.popState();
237
		// graphics.setForegroundColor(getBackgroundColor());
238
		graphics.setForegroundColor(lightenColor);
238
		// graphics.setBackgroundColor(getBackgroundColor());
239
		graphics.setBackgroundColor(lightenColor);
239
		graphics.setForegroundColor(lightenColor);
240
		// paint the border
240
		graphics.setBackgroundColor(lightenColor);
241
		bounds.setSize(bounds.width - 1, bounds.height - 1);
241
		graphics.fillRoundRectangle(top, arcWidth, arcWidth);
242
		graphics.drawRoundRectangle(bounds, arcWidth, arcWidth);
242
243
		lightenColor.dispose();
243
		// graphics.setForegroundColor(lightenColor);
244
	}
244
		// graphics.setBackgroundColor(getBackgroundColor());
245
245
		graphics.setBackgroundColor(lightenColor);
246
//	public Dimension getPreferredSize(int hint, int hint2) {
246
		graphics.setForegroundColor(getBackgroundColor());
247
	//	return this.label.getPreferredSize();
247
		graphics.fillGradient(r, true);
248
	//}
248
249
249
		super.paint(graphics);
250
	public void setTextT(String string) {
250
		graphics.popState();
251
		this.setPreferredSize(null);
251
		graphics.setForegroundColor(lightenColor);
252
		this.label.setText(string);
252
		graphics.setBackgroundColor(lightenColor);
253
		this.add(label);
253
		// paint the border
254
		//System.out.println(this.label.getPreferredSize());
254
		bounds.setSize(bounds.width - 1, bounds.height - 1);
255
		this.layout.layout(this);
255
		graphics.drawRoundRectangle(bounds, arcWidth, arcWidth);
256
		this.invalidate();
256
		lightenColor.dispose();
257
		this.revalidate();
257
	}
258
		this.validate();
258
259
		//this.remove(label);
259
	// public Dimension getPreferredSize(int hint, int hint2) {
260
	}
260
	// return this.label.getPreferredSize();
261
261
	// }
262
	public void setText(String string) {
262
263
		this.label.setText(string);
263
	public void setTextT(String string) {
264
		//this.label.setPreferredSize(500, 30);
264
		this.setPreferredSize(null);
265
		//adjustBoundsToFit();
265
		this.label.setText(string);
266
	}
266
		this.add(label);
267
267
		// System.out.println(this.label.getPreferredSize());
268
	public void setImage(Image image) {
268
		this.layout.layout(this);
269
		this.label.setIcon(image);
269
		this.invalidate();
270
		//adjustBoundsToFit();
270
		this.revalidate();
271
	}
271
		this.validate();
272
272
		// this.remove(label);
273
	public void setLocation(Point p) {
273
	}
274
		// TODO Auto-generated method stub
274
275
		super.setLocation(p);
275
	public void setText(String string) {
276
	}
276
		this.label.setText(string);
277
277
		// this.label.setPreferredSize(500, 30);
278
	public void setBounds(Rectangle rect) {
278
		// adjustBoundsToFit();
279
		// TODO Auto-generated method stub
279
	}
280
		super.setBounds(rect);
280
281
	}
281
	public void setImage(Image image) {
282
282
		this.label.setIcon(image);
283
}
283
		// adjustBoundsToFit();
284
	}
285
286
	public void setLocation(Point p) {
287
		// TODO Auto-generated method stub
288
		super.setLocation(p);
289
	}
290
291
	public void setBounds(Rectangle rect) {
292
		// TODO Auto-generated method stub
293
		super.setBounds(rect);
294
	}
295
296
}
(-)src/org/eclipse/zest/core/widgets/internal/GraphLabel.java (-286 / +282 lines)
Lines 1-286 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2009-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials are made
3
 * All rights reserved. This program and the accompanying materials are made
4
 * available under the terms of the Eclipse Public License v1.0 which
4
 * available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.internal;
10
package org.eclipse.zest.core.widgets.internal;
11
11
12
import org.eclipse.draw2d.ColorConstants;
12
import org.eclipse.draw2d.ColorConstants;
13
import org.eclipse.draw2d.FigureUtilities;
13
import org.eclipse.draw2d.FigureUtilities;
14
import org.eclipse.draw2d.Graphics;
14
import org.eclipse.draw2d.Graphics;
15
import org.eclipse.draw2d.MarginBorder;
15
import org.eclipse.draw2d.MarginBorder;
16
import org.eclipse.draw2d.ScaledGraphics;
16
import org.eclipse.draw2d.ScaledGraphics;
17
import org.eclipse.draw2d.StackLayout;
17
import org.eclipse.draw2d.StackLayout;
18
import org.eclipse.draw2d.geometry.Dimension;
18
import org.eclipse.draw2d.geometry.Dimension;
19
import org.eclipse.draw2d.geometry.Rectangle;
19
import org.eclipse.draw2d.geometry.Rectangle;
20
import org.eclipse.swt.graphics.Color;
20
import org.eclipse.swt.graphics.Color;
21
import org.eclipse.swt.graphics.Font;
21
import org.eclipse.swt.graphics.Font;
22
import org.eclipse.swt.graphics.Image;
22
import org.eclipse.swt.graphics.Image;
23
import org.eclipse.swt.graphics.RGB;
23
import org.eclipse.swt.graphics.RGB;
24
import org.eclipse.swt.widgets.Display;
24
import org.eclipse.swt.widgets.Display;
25
25
26
/**
26
/**
27
 * Overrides the Draw2D Label Figure class to ensure that the text is never
27
 * Overrides the Draw2D Label Figure class to ensure that the text is never
28
 * truncated. Also draws a rounded rectangle border.
28
 * truncated. Also draws a rounded rectangle border.
29
 * 
29
 * 
30
 * @author Chris Callendar
30
 * @author Chris Callendar
31
 */
31
 */
32
public class GraphLabel extends CachedLabel {
32
public class GraphLabel extends CachedLabel {
33
33
34
	private Color borderColor;
34
	private Color borderColor;
35
	private int borderWidth;
35
	private int borderWidth;
36
	private int arcWidth;
36
	private int arcWidth;
37
37
38
	private boolean painting = false;
38
	private boolean painting = false;
39
39
40
	/**
40
	/**
41
	 * Creates a GraphLabel
41
	 * Creates a GraphLabel
42
	 * 
42
	 * 
43
	 * @param cacheLabel
43
	 * @param cacheLabel
44
	 *            Determine if the text should be cached. This will make it
44
	 *            Determine if the text should be cached. This will make it
45
	 *            faster, but the text is not as clear
45
	 *            faster, but the text is not as clear
46
	 */
46
	 */
47
	public GraphLabel(boolean cacheLabel) {
47
	public GraphLabel(boolean cacheLabel) {
48
		this("", cacheLabel);
48
		this("", cacheLabel);
49
	}
49
	}
50
50
51
	/**
51
	/**
52
	 * Creates a graph label with text
52
	 * Creates a graph label with text
53
	 * 
53
	 * 
54
	 * @param text
54
	 * @param text
55
	 *            The text
55
	 *            The text
56
	 * @param cacheLabel
56
	 * @param cacheLabel
57
	 *            Determine if the text should be cached. This will make it
57
	 *            Determine if the text should be cached. This will make it
58
	 *            faster, but the
58
	 *            faster, but the
59
	 */
59
	 */
60
	public GraphLabel(String text, boolean cacheLabel) {
60
	public GraphLabel(String text, boolean cacheLabel) {
61
		this("", null, cacheLabel);
61
		this(text, null, cacheLabel);
62
	}
62
	}
63
63
64
	/**
64
	/**
65
	 * Creates the graph label with an image
65
	 * Creates the graph label with an image
66
	 * 
66
	 * 
67
	 * @param i
67
	 * @param i
68
	 *            The Image
68
	 *            The Image
69
	 * @param cacheLabel
69
	 * @param cacheLabel
70
	 *            Determine if the text should be cached. This will make it
70
	 *            Determine if the text should be cached. This will make it
71
	 *            faster, but the
71
	 *            faster, but the
72
	 */
72
	 */
73
	public GraphLabel(Image i, boolean cacheLabel) {
73
	public GraphLabel(Image i, boolean cacheLabel) {
74
		this("", i, cacheLabel);
74
		this("", i, cacheLabel);
75
	}
75
	}
76
76
77
	/**
77
	/**
78
	 * Creates a graph label with an image and text
78
	 * Creates a graph label with an image and text
79
	 * 
79
	 * 
80
	 * @param text
80
	 * @param text
81
	 *            The text
81
	 *            The text
82
	 * @param i
82
	 * @param i
83
	 *            The Image
83
	 *            The Image
84
	 * @param cacheLabel
84
	 * @param cacheLabel
85
	 *            Determine if the text should be cached. This will make it
85
	 *            Determine if the text should be cached. This will make it
86
	 *            faster, but the
86
	 *            faster, but the
87
	 */
87
	 */
88
	public GraphLabel(String text, Image i, boolean cacheLabel) {
88
	public GraphLabel(String text, Image i, boolean cacheLabel) {
89
		super(cacheLabel);
89
		super(cacheLabel);
90
		initLabel();
90
		initLabel();
91
		setText(text);
91
		setText(text);
92
		setIcon(i);
92
		setIcon(i);
93
		adjustBoundsToFit();
93
		adjustBoundsToFit();
94
	}
94
	}
95
95
96
	/**
96
	/**
97
	 * Initialises the border colour, border width, and sets the layout manager.
97
	 * Initialises the border colour, border width, and sets the layout manager.
98
	 * Also sets the font to be the default system font.
98
	 * Also sets the font to be the default system font.
99
	 */
99
	 */
100
	protected void initLabel() {
100
	protected void initLabel() {
101
		super.setFont(Display.getDefault().getSystemFont());
101
		super.setFont(Display.getDefault().getSystemFont());
102
		this.borderColor = ColorConstants.black;
102
		this.borderColor = ColorConstants.black;
103
		this.borderWidth = 0;
103
		this.borderWidth = 0;
104
		this.arcWidth = 8;
104
		this.arcWidth = 8;
105
		this.setLayoutManager(new StackLayout());
105
		this.setLayoutManager(new StackLayout());
106
		this.setBorder(new MarginBorder(1));
106
		this.setBorder(new MarginBorder(1));
107
	}
107
	}
108
108
109
	/*
109
	/*
110
	 * (non-Javadoc)
110
	 * (non-Javadoc)
111
	 * 
111
	 * 
112
	 * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font)
112
	 * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font)
113
	 */
113
	 */
114
	public void setFont(Font f) {
114
	public void setFont(Font f) {
115
		super.setFont(f);
115
		super.setFont(f);
116
		adjustBoundsToFit();
116
		adjustBoundsToFit();
117
	}
117
	}
118
118
119
	/**
119
	/**
120
	 * Adjust the bounds to make the text fit without truncation.
120
	 * Adjust the bounds to make the text fit without truncation.
121
	 */
121
	 */
122
	protected void adjustBoundsToFit() {
122
	protected void adjustBoundsToFit() {
123
		String text = getText();
123
		String text = getText();
124
		int safeBorderWidth = borderWidth > 0 ? borderWidth : 1;
124
		int safeBorderWidth = borderWidth > 0 ? borderWidth : 1;
125
		if ((text != null)) {
125
		if ((text != null)) {
126
			Font font = getFont();
126
			Font font = getFont();
127
			if (font != null) {
127
			if (font != null) {
128
				Dimension minSize = FigureUtilities.getTextExtents(text, font);
128
				Dimension minSize = FigureUtilities.getTextExtents(text, font);
129
				if (getIcon() != null) {
129
				if (getIcon() != null) {
130
					org.eclipse.swt.graphics.Rectangle imageRect = getIcon().getBounds();
130
					org.eclipse.swt.graphics.Rectangle imageRect = getIcon()
131
					int expandHeight = Math.max(imageRect.height - minSize.height, 0);
131
							.getBounds();
132
					minSize.expand(imageRect.width + 4, expandHeight);
132
					int expandHeight = Math.max(imageRect.height
133
				}
133
							- minSize.height, 0);
134
				minSize.expand(10 + (2 * safeBorderWidth), 4 + (2 * safeBorderWidth));
134
					minSize.expand(imageRect.width + 4, expandHeight);
135
				setBounds(new Rectangle(getLocation(), minSize));
135
				}
136
			}
136
				minSize.expand(10 + (2 * safeBorderWidth),
137
		}
137
						4 + (2 * safeBorderWidth));
138
	}
138
				setBounds(new Rectangle(getLocation(), minSize));
139
139
			}
140
	/*
140
		}
141
	 * (non-Javadoc)
141
	}
142
	 * 
142
143
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
143
	/*
144
	 */
144
	 * (non-Javadoc)
145
	public void paint(Graphics graphics) {
145
	 * 
146
		int blue = getBackgroundColor().getBlue();
146
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
147
		blue = (int) (blue - (blue * 0.20));
147
	 */
148
		blue = blue > 0 ? blue : 0;
148
	public void paint(Graphics graphics) {
149
149
		int blue = getBackgroundColor().getBlue();
150
		int red = getBackgroundColor().getRed();
150
		blue = (int) (blue - (blue * 0.20));
151
		red = (int) (red - (red * 0.20));
151
		blue = blue > 0 ? blue : 0;
152
		red = red > 0 ? red : 0;
152
153
153
		int red = getBackgroundColor().getRed();
154
		int green = getBackgroundColor().getGreen();
154
		red = (int) (red - (red * 0.20));
155
		green = (int) (green - (green * 0.20));
155
		red = red > 0 ? red : 0;
156
		green = green > 0 ? green : 0;
156
157
157
		int green = getBackgroundColor().getGreen();
158
		Color lightenColor = new Color(Display.getCurrent(), new RGB(red, green, blue));
158
		green = (int) (green - (green * 0.20));
159
		graphics.setForegroundColor(lightenColor);
159
		green = green > 0 ? green : 0;
160
		graphics.setBackgroundColor(getBackgroundColor());
160
161
161
		Color lightenColor = new Color(Display.getCurrent(), new RGB(red,
162
		int safeBorderWidth = borderWidth > 0 ? borderWidth : 1;
162
				green, blue));
163
		graphics.pushState();
163
		graphics.setForegroundColor(lightenColor);
164
		double scale = 1;
164
		graphics.setBackgroundColor(getBackgroundColor());
165
165
166
		if (graphics instanceof ScaledGraphics) {
166
		int safeBorderWidth = borderWidth > 0 ? borderWidth : 1;
167
			scale = ((ScaledGraphics) graphics).getAbsoluteScale();
167
		graphics.pushState();
168
		}
168
		double scale = 1;
169
		// Top part inside the border (as fillGradient does not allow to fill a rectangle with round corners).
169
170
		Rectangle rect = getBounds().getCopy();
170
		if (graphics instanceof ScaledGraphics) {
171
		rect.height /= 2;
171
			scale = ((ScaledGraphics) graphics).getAbsoluteScale();
172
		graphics.setForegroundColor(getBackgroundColor());
172
		}
173
		graphics.setBackgroundColor(getBackgroundColor());
173
		// Top part inside the border (as fillGradient does not allow to fill a
174
		graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth * 2 * safeBorderWidth);
174
		// rectangle with round corners).
175
175
		Rectangle rect = getBounds().getCopy();
176
		// Bottom part inside the border.
176
		rect.height /= 2;
177
		rect.y = rect.y + rect.height;
177
		graphics.setForegroundColor(getBackgroundColor());
178
		rect.height += 1; // Not sure why it is needed, but it is needed ;-)
178
		graphics.setBackgroundColor(getBackgroundColor());
179
		graphics.setForegroundColor(lightenColor);
179
		graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth
180
		graphics.setBackgroundColor(lightenColor);
180
				* 2 * safeBorderWidth);
181
		graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth * 2 * safeBorderWidth);
181
182
182
		// Bottom part inside the border.
183
		// Now fill the middle part of top and bottom part with a gradient.
183
		rect.y = rect.y + rect.height;
184
		rect = bounds.getCopy();
184
		rect.height += 1; // Not sure why it is needed, but it is needed ;-)
185
		rect.height -= 2;
185
		graphics.setForegroundColor(lightenColor);
186
		rect.y += (safeBorderWidth) / 2;
186
		graphics.setBackgroundColor(lightenColor);
187
		rect.y += (arcWidth / 2);
187
		graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth
188
		rect.height -= arcWidth / 2;
188
				* 2 * safeBorderWidth);
189
		rect.height -= safeBorderWidth;
189
190
		graphics.setBackgroundColor(lightenColor);
190
		// Now fill the middle part of top and bottom part with a gradient.
191
		graphics.setForegroundColor(getBackgroundColor());
191
		rect = bounds.getCopy();
192
		graphics.fillGradient(rect, true);
192
		rect.height -= 2;
193
193
		rect.y += (safeBorderWidth) / 2;
194
		// Paint the border
194
		rect.y += (arcWidth / 2);
195
		if (borderWidth > 0) {
195
		rect.height -= arcWidth / 2;
196
			rect = getBounds().getCopy();
196
		rect.height -= safeBorderWidth;
197
			rect.x += safeBorderWidth / 2;
197
		graphics.setBackgroundColor(lightenColor);
198
			rect.y += safeBorderWidth / 2;
198
		graphics.setForegroundColor(getBackgroundColor());
199
			rect.width -= safeBorderWidth;
199
		graphics.fillGradient(rect, true);
200
			rect.height -= safeBorderWidth;
200
201
			graphics.setForegroundColor(borderColor);
201
		// Paint the border
202
			graphics.setBackgroundColor(borderColor);
202
		if (borderWidth > 0) {
203
			graphics.setLineWidth((int) (safeBorderWidth * scale));
203
			rect = getBounds().getCopy();
204
			graphics.drawRoundRectangle(rect, arcWidth, arcWidth);
204
			rect.x += safeBorderWidth / 2;
205
		}
205
			rect.y += safeBorderWidth / 2;
206
206
			rect.width -= safeBorderWidth;
207
		super.paint(graphics);
207
			rect.height -= safeBorderWidth;
208
208
			graphics.setForegroundColor(borderColor);
209
		graphics.popState();
209
			graphics.setBackgroundColor(borderColor);
210
210
			graphics.setLineWidth((int) (safeBorderWidth * scale));
211
		lightenColor.dispose();
211
			graphics.drawRoundRectangle(rect, arcWidth, arcWidth);
212
	}
212
		}
213
213
214
	protected Color getBackgroundTextColor() {
214
		super.paint(graphics);
215
		return getBackgroundColor();
215
216
	}
216
		graphics.popState();
217
217
218
	/**
218
		lightenColor.dispose();
219
	 * This method is overridden to ensure that it doesn't get called while the
219
	}
220
	 * super.paintFigure() is being called. Otherwise NullPointerExceptions can
220
221
	 * occur because the icon or text locations are cleared *after* they were
221
	protected Color getBackgroundTextColor() {
222
	 * calculated.
222
		return getBackgroundColor();
223
	 * 
223
	}
224
	 * @see org.eclipse.draw2d.Label#invalidate()
224
225
	 */
225
	/**
226
	public void invalidate() {
226
	 * This method is overridden to ensure that it doesn't get called while the
227
		if (!painting) {
227
	 * super.paintFigure() is being called. Otherwise NullPointerExceptions can
228
			super.invalidate();
228
	 * occur because the icon or text locations are cleared *after* they were
229
		}
229
	 * calculated.
230
	}
230
	 * 
231
231
	 * @see org.eclipse.draw2d.Label#invalidate()
232
	/*
232
	 */
233
	 * (non-Javadoc)
233
	public void invalidate() {
234
	 * 
234
		if (!painting) {
235
	 * @see org.eclipse.draw2d.Label#setText(java.lang.String)
235
			super.invalidate();
236
	 */
236
		}
237
	public void setText(String s) {
237
	}
238
		if (!s.equals("")) {
238
239
			super.setText(s);
239
	/*
240
240
	 * (non-Javadoc)
241
		} else {
241
	 * 
242
			super.setText("");
242
	 * @see org.eclipse.draw2d.Label#setText(java.lang.String)
243
		}
243
	 */
244
		adjustBoundsToFit();
244
	public void setText(String s) {
245
	}
245
		super.setText(s);
246
246
		adjustBoundsToFit();
247
	/*
247
	}
248
	 * (non-Javadoc)
248
249
	 * 
249
	/*
250
	 * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image)
250
	 * (non-Javadoc)
251
	 */
251
	 * 
252
	public void setIcon(Image image) {
252
	 * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image)
253
		super.setIcon(image);
253
	 */
254
		//adjustBoundsToFit();
254
	public void setIcon(Image image) {
255
	}
255
		super.setIcon(image);
256
256
		adjustBoundsToFit();
257
	public Color getBorderColor() {
257
	}
258
		return borderColor;
258
259
	}
259
	public Color getBorderColor() {
260
260
		return borderColor;
261
	public void setBorderColor(Color c) {
261
	}
262
		this.borderColor = c;
262
263
	}
263
	public void setBorderColor(Color c) {
264
264
		this.borderColor = c;
265
	public int getBorderWidth() {
265
	}
266
		return borderWidth;
266
267
	}
267
	public int getBorderWidth() {
268
268
		return borderWidth;
269
	public void setBorderWidth(int width) {
269
	}
270
		this.borderWidth = width;
270
271
	}
271
	public void setBorderWidth(int width) {
272
272
		this.borderWidth = width;
273
	public int getArcWidth() {
273
	}
274
		return arcWidth;
274
275
	}
275
	public int getArcWidth() {
276
276
		return arcWidth;
277
	public void setArcWidth(int arcWidth) {
277
	}
278
		this.arcWidth = arcWidth;
278
279
	}
279
	public void setArcWidth(int arcWidth) {
280
280
		this.arcWidth = arcWidth;
281
	public void setBounds(Rectangle rect) {
281
	}
282
		// TODO Auto-generated method stub
282
}
283
		super.setBounds(rect);
284
	}
285
286
}
(-)src/org/eclipse/zest/core/widgets/internal/LoopAnchor.java (-38 / +40 lines)
Lines 1-38 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.widgets.internal;
11
package org.eclipse.zest.core.widgets.internal;
12
12
13
import org.eclipse.draw2d.ChopboxAnchor;
13
import org.eclipse.draw2d.ChopboxAnchor;
14
import org.eclipse.draw2d.IFigure;
14
import org.eclipse.draw2d.IFigure;
15
import org.eclipse.draw2d.geometry.Point;
15
import org.eclipse.draw2d.geometry.Point;
16
16
17
public class LoopAnchor extends ChopboxAnchor {
17
public class LoopAnchor extends ChopboxAnchor {
18
	public LoopAnchor(IFigure owner) {
18
	public LoopAnchor(IFigure owner) {
19
		super(owner);
19
		super(owner);
20
	}
20
	}
21
	
21
22
	/* (non-Javadoc)
22
	/*
23
	 * @see org.eclipse.draw2d.ChopboxAnchor#getReferencePoint()
23
	 * (non-Javadoc)
24
	 */
24
	 * 
25
	public Point getReferencePoint() {
25
	 * @see org.eclipse.draw2d.ChopboxAnchor#getReferencePoint()
26
		//modification to getReferencePoint. Returns
26
	 */
27
		//a point on the outside of the owners box, rather than the
27
	public Point getReferencePoint() {
28
		//center. Only usefull for self-loops.
28
		// modification to getReferencePoint. Returns
29
		if (getOwner() == null)
29
		// a point on the outside of the owners box, rather than the
30
			return null;
30
		// center. Only usefull for self-loops.
31
		else {
31
		if (getOwner() == null) {
32
			Point ref = getOwner().getBounds().getCenter();
32
			return null;
33
			ref.y = getOwner().getBounds().y;
33
		} else {
34
			getOwner().translateToAbsolute(ref);
34
			Point ref = getOwner().getBounds().getCenter();
35
			return ref;
35
			ref.y = getOwner().getBounds().y;
36
		}
36
			getOwner().translateToAbsolute(ref);
37
	}
37
			return ref;
38
}
38
		}
39
	}
40
}
(-)src/org/eclipse/zest/core/widgets/internal/PolylineArcConnection.java (-254 / +260 lines)
Lines 1-254 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC,
3
 * Canada. All rights reserved. This program and the accompanying materials are
3
 * Canada. All rights reserved. This program and the accompanying materials are
4
 * made available under the terms of the Eclipse Public License v1.0 which
4
 * made available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
8
 * Contributors: The Chisel Group, University of Victoria
9
 *******************************************************************************/
9
 *******************************************************************************/
10
package org.eclipse.zest.core.widgets.internal;
10
package org.eclipse.zest.core.widgets.internal;
11
11
12
import org.eclipse.draw2d.PolylineConnection;
12
import org.eclipse.draw2d.PolylineConnection;
13
import org.eclipse.draw2d.RectangleFigure;
13
import org.eclipse.draw2d.RectangleFigure;
14
import org.eclipse.draw2d.geometry.Point;
14
import org.eclipse.draw2d.geometry.Point;
15
import org.eclipse.draw2d.geometry.PointList;
15
import org.eclipse.draw2d.geometry.PointList;
16
16
17
/*
17
/*
18
 * A connection that draws an arc between nodes, based on a given depth for the
18
 * A connection that draws an arc between nodes, based on a given depth for the
19
 * arc. This connection is drawn as an arc, defined as the circular arc with the
19
 * arc. This connection is drawn as an arc, defined as the circular arc with the
20
 * chord (ax, ay) - (bx, by) (where a and b are the anchors) and a depth d
20
 * chord (ax, ay) - (bx, by) (where a and b are the anchors) and a depth d
21
 * defined as the maximum distance from any point on the chord (i.e. a vector
21
 * defined as the maximum distance from any point on the chord (i.e. a vector
22
 * normal to the chord with magnitude d).
22
 * normal to the chord with magnitude d).
23
 * 
23
 * 
24
 * @author Del Myers
24
 * @author Del Myers
25
 */
25
 */
26
// @tag zest(bug(154391-ArcEnds(fix))) : force the endpoints to match by using a
26
// @tag zest(bug(154391-ArcEnds(fix))) : force the endpoints to match by using a
27
// polyline connection.
27
// polyline connection.
28
// This will be more accurate than the regular ArcConnection, but it may be
28
// This will be more accurate than the regular ArcConnection, but it may be
29
// slower.
29
// slower.
30
public class PolylineArcConnection extends PolylineConnection {
30
public class PolylineArcConnection extends PolylineConnection {
31
	private int depth;
31
	private int depth;
32
	private boolean inverse = false;
32
	private boolean inverse = false;
33
	private static final float PI = (float) 3.14159;
33
	private static final float PI = (float) 3.14159;
34
	private RectangleFigure center;
34
	private RectangleFigure center;
35
35
36
	{
36
	{
37
		this.depth = 0;
37
		this.depth = 0;
38
		center = new RectangleFigure();
38
		center = new RectangleFigure();
39
	}
39
	}
40
40
41
	/*
41
	/*
42
	 * (non-Javadoc)
42
	 * (non-Javadoc)
43
	 * 
43
	 * 
44
	 * @see org.eclipse.draw2d.Polyline#setPoints(org.eclipse.draw2d.geometry.PointList)
44
	 * @see
45
	 */
45
	 * org.eclipse.draw2d.Polyline#setPoints(org.eclipse.draw2d.geometry.PointList
46
	public void setPoints(PointList points) {
46
	 * )
47
		updateArc(points);
47
	 */
48
	}
48
	public void setPoints(PointList points) {
49
49
		updateArc(points);
50
	/**
50
	}
51
	 * This method is not supported by this kind of connection. Points are
51
52
	 * calculated based on the arc definition.
52
	/**
53
	 */
53
	 * This method is not supported by this kind of connection. Points are
54
	public void addPoint(Point pt) {
54
	 * calculated based on the arc definition.
55
	}
55
	 */
56
56
	public void addPoint(Point pt) {
57
	/**
57
	}
58
	 * @param depth
58
59
	 *            the depth to set
59
	/**
60
	 */
60
	 * @param depth
61
	public void setDepth(int depth) {
61
	 *            the depth to set
62
		this.inverse = depth < 0 ? true : false;
62
	 */
63
		this.depth = depth;
63
	public void setDepth(int depth) {
64
		updateArc(getPoints());
64
		this.inverse = depth < 0 ? true : false;
65
	}
65
		this.depth = depth;
66
66
		updateArc(getPoints());
67
	protected void updateArc(PointList pointList) {
67
	}
68
		if (pointList.size() < 2) {
68
69
			return;
69
	protected void updateArc(PointList pointList) {
70
		}
70
		if (pointList.size() < 2) {
71
		if (center.getParent() == this) {
71
			return;
72
			remove(center);
72
		}
73
		}
73
		if (center.getParent() == this) {
74
		Point start = pointList.getFirstPoint();
74
			remove(center);
75
		Point end = pointList.getLastPoint();
75
		}
76
		if (depth == 0) {
76
		Point start = pointList.getFirstPoint();
77
			super.setPoints(pointList);
77
		Point end = pointList.getLastPoint();
78
			return;
78
		if (depth == 0) {
79
		}
79
			super.setPoints(pointList);
80
80
			return;
81
		PointList points = new PointList();
81
		}
82
82
83
		float arcStart = 0;
83
		PointList points = new PointList();
84
		float arcEnd = 0;
84
85
		float arcLength = 0;
85
		float arcStart = 0;
86
		float cartCenterX = 0;
86
		float arcEnd = 0;
87
		float cartCenterY = 0;
87
		float arcLength = 0;
88
		float r = 0;
88
		float cartCenterX = 0;
89
89
		float cartCenterY = 0;
90
		float x1 = start.x;
90
		float r = 0;
91
		float y1 = -start.y;
91
92
		float x2 = end.x;
92
		float x1 = start.x;
93
		float y2 = -end.y;
93
		float y1 = -start.y;
94
		float depth = this.depth;
94
		float x2 = end.x;
95
95
		float y2 = -end.y;
96
		if (start.equals(end)) {
96
		float depth = this.depth;
97
			// do a circle
97
98
			arcStart = -PI / 2;
98
		if (start.equals(end)) {
99
			arcLength = PI * 2;
99
			// do a circle
100
			// @tag drawing(arcs) : try making the center on a line from the
100
			arcStart = -PI / 2;
101
			// center of the parent figure.
101
			arcLength = PI * 2;
102
102
			// @tag drawing(arcs) : try making the center on a line from the
103
			cartCenterX = x1;
103
			// center of the parent figure.
104
			cartCenterY = y1 + depth / 2;
104
105
			r = depth / 2;
105
			cartCenterX = x1;
106
106
			cartCenterY = y1 + depth / 2;
107
		} else {
107
			r = depth / 2;
108
			if (x1 >= x2) {
108
109
				depth = -depth;
109
		} else {
110
			}
110
			if (x1 >= x2) {
111
			// the center of the chord
111
				depth = -depth;
112
			float cartChordX = (x2 + x1) / 2;
112
			}
113
			float cartChordY = (y2 + y1) / 2;
113
			// the center of the chord
114
			float chordLength = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
114
			float cartChordX = (x2 + x1) / 2;
115
			if (Math.abs(depth) >= chordLength / 2) {
115
			float cartChordY = (y2 + y1) / 2;
116
				depth = (chordLength / 3) * (depth / Math.abs(depth));
116
			float chordLength = (float) Math.sqrt((x1 - x2) * (x1 - x2)
117
			}
117
					+ (y1 - y2) * (y1 - y2));
118
			r = ((((chordLength / 2) * (chordLength / 2)) + (depth * depth)) / (2 * depth));
118
			if (Math.abs(depth) >= chordLength / 2) {
119
119
				depth = (chordLength / 3) * (depth / Math.abs(depth));
120
			// Find a vector normal to the chord. This will be used for
120
			}
121
			// translating the
121
			r = ((((chordLength / 2) * (chordLength / 2)) + (depth * depth)) / (2 * depth));
122
			// circle back to screen coordinates.
122
123
			float chordNormal = 0;
123
			// Find a vector normal to the chord. This will be used for
124
124
			// translating the
125
			if (Math.abs(x1 - x2) <= .000001) {
125
			// circle back to screen coordinates.
126
				// slope of 0. NaN is easier to detect than 0.
126
			float chordNormal = 0;
127
				chordNormal = Float.NaN;
127
128
			} else if (Math.abs(y1 - y2) <= 0.000001) {
128
			if (Math.abs(x1 - x2) <= .000001) {
129
				// infinite slope.
129
				// slope of 0. NaN is easier to detect than 0.
130
				chordNormal = Float.POSITIVE_INFINITY;
130
				chordNormal = Float.NaN;
131
			} else {
131
			} else if (Math.abs(y1 - y2) <= 0.000001) {
132
				chordNormal = -1 * (y2 - y1) / (x2 - x1);
132
				// infinite slope.
133
			}
133
				chordNormal = Float.POSITIVE_INFINITY;
134
134
			} else {
135
			float th1;
135
				chordNormal = -1 * (y2 - y1) / (x2 - x1);
136
			if (Float.isNaN(chordNormal)) {
136
			}
137
				cartCenterX = (y1 > y2) ? (cartChordX - r + (depth)) : (cartChordX + r - (depth));
137
138
				cartCenterY = cartChordY;
138
			float th1;
139
				th1 = PI / 2;
139
			if (Float.isNaN(chordNormal)) {
140
			} else if (Float.isInfinite(chordNormal)) {
140
				cartCenterX = (y1 > y2) ? (cartChordX - r + (depth))
141
				cartCenterX = cartChordX;
141
						: (cartChordX + r - (depth));
142
				cartCenterY = cartChordY + r - (depth);
142
				cartCenterY = cartChordY;
143
				th1 = 0;
143
				th1 = PI / 2;
144
			} else {
144
			} else if (Float.isInfinite(chordNormal)) {
145
				// assume that the center of the chord is on the origin.
145
				cartCenterX = cartChordX;
146
				th1 = (float) Math.atan(chordNormal);
146
				cartCenterY = cartChordY + r - (depth);
147
				cartCenterX = (r - (depth)) * (float) Math.sin(th1) + cartChordX;// cartChordX+r
147
				th1 = 0;
148
				// -depth;
148
			} else {
149
				cartCenterY = (r - (depth)) * (float) Math.cos(th1) + cartChordY;// cartChordY+r-depth;
149
				// assume that the center of the chord is on the origin.
150
150
				th1 = (float) Math.atan(chordNormal);
151
			}
151
				cartCenterX = (r - (depth)) * (float) Math.sin(th1)
152
			// figure out the new angles
152
						+ cartChordX;// cartChordX+r
153
			// translate the points to the center of the circle
153
				// -depth;
154
			float cartArcX1 = x1 - cartCenterX;
154
				cartCenterY = (r - (depth)) * (float) Math.cos(th1)
155
			float cartArcY1 = y1 - cartCenterY;
155
						+ cartChordY;// cartChordY+r-depth;
156
			float cartArcX2 = x2 - cartCenterX;
156
157
			float cartArcY2 = y2 - cartCenterY;
157
			}
158
158
			// figure out the new angles
159
			// calculate the length of the arc
159
			// translate the points to the center of the circle
160
			arcStart = angleRadians(cartArcX1, cartArcY1);
160
			float cartArcX1 = x1 - cartCenterX;
161
			arcEnd = angleRadians(cartArcX2, cartArcY2);
161
			float cartArcY1 = y1 - cartCenterY;
162
162
			float cartArcX2 = x2 - cartCenterX;
163
			if (arcEnd < arcStart) {
163
			float cartArcY2 = y2 - cartCenterY;
164
				arcEnd = arcEnd + PI + PI;
164
165
			}
165
			// calculate the length of the arc
166
166
			arcStart = angleRadians(cartArcX1, cartArcY1);
167
			// make sure that we are between the two nodes.
167
			arcEnd = angleRadians(cartArcX2, cartArcY2);
168
			arcLength = arcEnd - arcStart;
168
169
			float pad = PI / Math.abs(r);
169
			if (arcEnd < arcStart) {
170
			arcStart += pad;
170
				arcEnd = arcEnd + PI + PI;
171
			arcEnd -= pad;
171
			}
172
			arcLength = (arcEnd) - (arcStart);
172
173
			if (inverse) {
173
			// make sure that we are between the two nodes.
174
				arcLength = (2 * PI - arcLength);
174
			arcLength = arcEnd - arcStart;
175
			}
175
			float pad = PI / Math.abs(r);
176
		}
176
			arcStart += pad;
177
		// calculate the points
177
			arcEnd -= pad;
178
		r = Math.abs(r);
178
			arcLength = (arcEnd) - (arcStart);
179
		float x = 0, y = 0;
179
			if (inverse) {
180
		Point p = null;
180
				arcLength = (2 * PI - arcLength);
181
		points.addPoint(start);
181
			}
182
		float length = arcLength * r;
182
		}
183
183
		// calculate the points
184
		int steps = (int) length / 16;
184
		r = Math.abs(r);
185
		if (steps < 10 && length > 10) {
185
		float x = 0, y = 0;
186
			steps = 10;
186
		Point p = null;
187
		}
187
		points.addPoint(start);
188
		if (arcLength < PI / 4 && steps > 6) {
188
		float length = arcLength * r;
189
			steps = 6;
189
190
		}
190
		int steps = (int) length / 16;
191
		if (steps < 4 && length > 4) {
191
		if (steps < 10 && length > 10) {
192
			steps = 4;
192
			steps = 10;
193
		}
193
		}
194
		float stepSize = arcLength / steps;
194
		if (arcLength < PI / 4 && steps > 6) {
195
		if (inverse) {
195
			steps = 6;
196
			float step = arcStart - stepSize;
196
		}
197
			for (int i = 1; i < steps; i++, step -= stepSize) {
197
		if (steps < 4 && length > 4) {
198
				x = (r) * (float) Math.cos(step) + cartCenterX;
198
			steps = 4;
199
				y = (r) * (float) Math.sin(step) + cartCenterY;
199
		}
200
				p = new Point(Math.round(x), Math.round(-y));
200
		float stepSize = arcLength / steps;
201
				points.addPoint(p);
201
		if (inverse) {
202
			}
202
			float step = arcStart - stepSize;
203
		} else {
203
			for (int i = 1; i < steps; i++, step -= stepSize) {
204
			float step = stepSize + arcStart;
204
				x = (r) * (float) Math.cos(step) + cartCenterX;
205
			for (int i = 1; i < steps; i++, step += stepSize) {
205
				y = (r) * (float) Math.sin(step) + cartCenterY;
206
				x = (r) * (float) Math.cos(step) + cartCenterX;
206
				p = new Point(Math.round(x), Math.round(-y));
207
				y = (r) * (float) Math.sin(step) + cartCenterY;
207
				points.addPoint(p);
208
				p = new Point(Math.round(x), Math.round(-y));
208
			}
209
				points.addPoint(p);
209
		} else {
210
			}
210
			float step = stepSize + arcStart;
211
		}
211
			for (int i = 1; i < steps; i++, step += stepSize) {
212
		points.addPoint(end);
212
				x = (r) * (float) Math.cos(step) + cartCenterX;
213
213
				y = (r) * (float) Math.sin(step) + cartCenterY;
214
		super.setPoints(points);
214
				p = new Point(Math.round(x), Math.round(-y));
215
	}
215
				points.addPoint(p);
216
216
			}
217
	/*
217
		}
218
	 * Gets an angle in radians for the x, y coordinates. The angle will be
218
		points.addPoint(end);
219
	 * between 0 and 2PI.
219
220
	 */
220
		super.setPoints(points);
221
	float angleRadians(float x, float y) {
221
	}
222
		float theta = (float) Math.atan(y / x);
222
223
		switch (findQuadrant(x, y)) {
223
	/*
224
		case 1:
224
	 * Gets an angle in radians for the x, y coordinates. The angle will be
225
			return theta;
225
	 * between 0 and 2PI.
226
		case 2:
226
	 */
227
			return (theta + PI);
227
	float angleRadians(float x, float y) {
228
		case 4:
228
		float theta = (float) Math.atan(y / x);
229
			theta = (theta + PI);
229
		switch (findQuadrant(x, y)) {
230
		case 3:
230
		case 1:
231
			return (theta + PI);
231
			return theta;
232
		default:
232
		case 2:
233
			return theta;
233
			return (theta + PI);
234
		}
234
		case 4:
235
235
			theta = (theta + PI);
236
	}
236
		case 3:
237
237
			return (theta + PI);
238
	// find the quadrant, assume points are centered at 0,0
238
		default:
239
	protected int findQuadrant(float x, float y) {
239
			return theta;
240
		if (y > 0) {
240
		}
241
			if (x > 0) {
241
242
				return 1;
242
	}
243
			} else {
243
244
				return 2;
244
	// find the quadrant, assume points are centered at 0,0
245
			}
245
	protected int findQuadrant(float x, float y) {
246
		} else {
246
		if (y > 0) {
247
			if (x > 0) {
247
			if (x > 0) {
248
				return 4;
248
				return 1;
249
			} else {
249
			} else {
250
				return 3;
250
				return 2;
251
			}
251
			}
252
		}
252
		} else {
253
	}
253
			if (x > 0) {
254
}
254
				return 4;
255
			} else {
256
				return 3;
257
			}
258
		}
259
	}
260
}
(-)src/org/eclipse/zest/core/widgets/internal/RevealListener.java (-25 / +26 lines)
Lines 1-25 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.widgets.internal;
11
package org.eclipse.zest.core.widgets.internal;
12
12
13
import org.eclipse.swt.widgets.Control;
13
import org.eclipse.swt.widgets.Control;
14
14
15
/**
15
/**
16
 * 
16
 * 
17
 * A Listener to indicate when a view has become visible.
17
 * A Listener to indicate when a view has become visible.
18
 * @author Ian Bull
18
 * 
19
 *
19
 * @author Ian Bull
20
 */
20
 * 
21
public interface RevealListener {
21
 */
22
	
22
public interface RevealListener {
23
	public void revealed( Control c );
23
24
24
	public void revealed(Control c);
25
}
25
26
}
(-)src/org/eclipse/zest/core/widgets/internal/RoundedChopboxAnchor.java (-78 / +88 lines)
Lines 1-78 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright 2005-2010, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.zest.core.widgets.internal;
11
package org.eclipse.zest.core.widgets.internal;
12
12
13
import org.eclipse.draw2d.ChopboxAnchor;
13
import org.eclipse.draw2d.ChopboxAnchor;
14
import org.eclipse.draw2d.IFigure;
14
import org.eclipse.draw2d.IFigure;
15
import org.eclipse.draw2d.geometry.Point;
15
import org.eclipse.draw2d.geometry.Point;
16
import org.eclipse.draw2d.geometry.Rectangle;
16
import org.eclipse.draw2d.geometry.Rectangle;
17
17
18
/**
18
/**
19
 * A slight modification to the ChopboxAnchor class to account for the rounded corners.
19
 * A slight modification to the ChopboxAnchor class to account for the rounded
20
 * 
20
 * corners.
21
 * @author Chris Callendar
21
 * 
22
 */
22
 * @author Chris Callendar
23
public class RoundedChopboxAnchor extends ChopboxAnchor {
23
 */
24
24
public class RoundedChopboxAnchor extends ChopboxAnchor {
25
	private int arcRadius = 10;
25
26
	private int shift = 7;
26
	private int arcRadius = 10;
27
	
27
	private int shift = 7;
28
	public RoundedChopboxAnchor(int arcRadius) {
28
29
		super();
29
	public RoundedChopboxAnchor(int arcRadius) {
30
		this.arcRadius = arcRadius;
30
		super();
31
		this.shift = arcRadius - (int)(0.707 * arcRadius);
31
		this.arcRadius = arcRadius;
32
	}
32
		this.shift = arcRadius - (int) (0.707 * arcRadius);
33
33
	}
34
	public RoundedChopboxAnchor(IFigure owner, int arcRadius) {
34
35
		super(owner);
35
	public RoundedChopboxAnchor(IFigure owner, int arcRadius) {
36
		this.arcRadius = arcRadius;
36
		super(owner);
37
		this.shift = arcRadius - (int)(0.707 * arcRadius);
37
		this.arcRadius = arcRadius;
38
	}
38
		this.shift = arcRadius - (int) (0.707 * arcRadius);
39
39
	}
40
	/**
40
41
	 * Modifies the point slightly for the rounded corners.
41
	/**
42
	 * @return Point
42
	 * Modifies the point slightly for the rounded corners.
43
	 */
43
	 * 
44
	public Point getLocation(Point reference) {
44
	 * @return Point
45
		Point p = super.getLocation(reference);
45
	 */
46
		Rectangle bounds = getBox();
46
	public Point getLocation(Point reference) {
47
		
47
		Point p = super.getLocation(reference);
48
		boolean done = getTranslatedPoint(bounds.getTopLeft(), p, shift, shift);
48
		Rectangle bounds = getBox();
49
		if (!done)
49
50
			done = getTranslatedPoint(bounds.getTopRight(), p, -shift, shift);
50
		boolean done = getTranslatedPoint(bounds.getTopLeft(), p, shift, shift);
51
		if (!done)
51
		if (!done) {
52
			done = getTranslatedPoint(bounds.getBottomLeft(), p, shift, -shift);
52
			done = getTranslatedPoint(bounds.getTopRight(), p, -shift, shift);
53
		if (!done)
53
		}
54
			done = getTranslatedPoint(bounds.getBottomRight(), p, -shift, -shift);
54
		if (!done) {
55
		return p;
55
			done = getTranslatedPoint(bounds.getBottomLeft(), p, shift, -shift);
56
	}
56
		}
57
	
57
		if (!done) {
58
	/**
58
			done = getTranslatedPoint(bounds.getBottomRight(), p, -shift,
59
	 * Calculates the distance from the corner to the Point p.  
59
					-shift);
60
	 * If it is less than the minimum then it translates it and returns the new Point.
60
		}
61
	 * @param corner	The corner Point.
61
		return p;
62
	 * @param p			The point to translate if close to the corner.
62
	}
63
	 * @param dx		The amount to translate in the x direcion.
63
64
	 * @param dy		The amount to translate in the y direcion.
64
	/**
65
	 * @return boolean 	If the translation occured.
65
	 * Calculates the distance from the corner to the Point p. If it is less
66
	 */
66
	 * than the minimum then it translates it and returns the new Point.
67
	private boolean getTranslatedPoint(Point corner, Point p, int dx, int dy) {
67
	 * 
68
		int diff = (int)corner.getDistance(p);
68
	 * @param corner
69
		if (diff < arcRadius) {
69
	 *            The corner Point.
70
			Point t = corner.getTranslated(dx, dy);
70
	 * @param p
71
			p.setLocation(t.x, t.y);
71
	 *            The point to translate if close to the corner.
72
			return true;
72
	 * @param dx
73
		}
73
	 *            The amount to translate in the x direcion.
74
		return false;
74
	 * @param dy
75
	}
75
	 *            The amount to translate in the y direcion.
76
	
76
	 * @return boolean If the translation occured.
77
	
77
	 */
78
}
78
	private boolean getTranslatedPoint(Point corner, Point p, int dx, int dy) {
79
		int diff = (int) corner.getDistance(p);
80
		if (diff < arcRadius) {
81
			Point t = corner.getTranslated(dx, dy);
82
			p.setLocation(t.x, t.y);
83
			return true;
84
		}
85
		return false;
86
	}
87
88
}
(-)src/org/eclipse/zest/core/widgets/internal/XYScaledGraphics.java (-832 / +870 lines)
Lines 1-832 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others. All rights reserved.
2
 * Copyright (c) 2005-2010, IBM Corporation and others. All rights reserved.
3
 * This program and the accompanying materials are made available under the
3
 * This program and the accompanying materials are made available under the
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
6
 * 
7
 * Contributors: IBM Corporation - initial API and implementation Chisel Group,
7
 * Contributors: IBM Corporation - initial API and implementation Chisel Group,
8
 * University of Victoria - Adapted for XY Scaled Graphics
8
 * University of Victoria - Adapted for XY Scaled Graphics
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.internal;
10
package org.eclipse.zest.core.widgets.internal;
11
11
12
import java.util.ArrayList;
12
import java.util.ArrayList;
13
import java.util.HashMap;
13
import java.util.HashMap;
14
import java.util.Iterator;
14
import java.util.Iterator;
15
import java.util.List;
15
import java.util.List;
16
import java.util.Map;
16
import java.util.Map;
17
17
18
import org.eclipse.draw2d.FigureUtilities;
18
import org.eclipse.draw2d.FigureUtilities;
19
import org.eclipse.draw2d.Graphics;
19
import org.eclipse.draw2d.Graphics;
20
import org.eclipse.draw2d.ScaledGraphics;
20
import org.eclipse.draw2d.ScaledGraphics;
21
import org.eclipse.draw2d.geometry.Point;
21
import org.eclipse.draw2d.geometry.Point;
22
import org.eclipse.draw2d.geometry.PointList;
22
import org.eclipse.draw2d.geometry.PointList;
23
import org.eclipse.draw2d.geometry.Rectangle;
23
import org.eclipse.draw2d.geometry.Rectangle;
24
import org.eclipse.swt.graphics.Color;
24
import org.eclipse.swt.graphics.Color;
25
import org.eclipse.swt.graphics.Font;
25
import org.eclipse.swt.graphics.Font;
26
import org.eclipse.swt.graphics.FontData;
26
import org.eclipse.swt.graphics.FontData;
27
import org.eclipse.swt.graphics.FontMetrics;
27
import org.eclipse.swt.graphics.FontMetrics;
28
import org.eclipse.swt.graphics.Image;
28
import org.eclipse.swt.graphics.Image;
29
import org.eclipse.swt.graphics.TextLayout;
29
import org.eclipse.swt.graphics.TextLayout;
30
import org.eclipse.swt.graphics.TextStyle;
30
import org.eclipse.swt.graphics.TextStyle;
31
import org.eclipse.swt.widgets.Display;
31
import org.eclipse.swt.widgets.Display;
32
32
33
/**
33
/**
34
 * This was adapted from the ScaledGraphics class to allow X and Y to scale
34
 * This was adapted from the ScaledGraphics class to allow X and Y to scale
35
 * independently. It won't require this level of coupling if some of these
35
 * independently. It won't require this level of coupling if some of these
36
 * private methods were made protected. I will open a bug report on this.
36
 * private methods were made protected. I will open a bug report on this.
37
 * 
37
 * 
38
 * @author irbull
38
 * @author irbull
39
 */
39
 */
40
public class XYScaledGraphics extends ScaledGraphics {
40
public class XYScaledGraphics extends ScaledGraphics {
41
41
42
	public static final double MAX_TEXT_SIZE = 0.45; // MAX size, when to stop zooming text
42
	public static final double MAX_TEXT_SIZE = 0.45; // MAX size, when to stop
43
43
44
	private static class FontHeightCache {
44
	// zooming text
45
		Font font;
45
46
		int height;
46
	private static class FontHeightCache {
47
	}
47
		Font font;
48
48
		int height;
49
	static class FontKey {
49
	}
50
		Font font;
50
51
		int height;
51
	static class FontKey {
52
52
		Font font;
53
		protected FontKey() {
53
		int height;
54
		}
54
55
55
		protected FontKey() {
56
		protected FontKey(Font font, int height) {
56
		}
57
			this.font = font;
57
58
			this.height = height;
58
		protected FontKey(Font font, int height) {
59
		}
59
			this.font = font;
60
60
			this.height = height;
61
		public boolean equals(Object obj) {
61
		}
62
			return (((FontKey) obj).font.equals(font) && ((FontKey) obj).height == height);
62
63
		}
63
		public boolean equals(Object obj) {
64
64
			return (((FontKey) obj).font.equals(font) && ((FontKey) obj).height == height);
65
		public int hashCode() {
65
		}
66
			return font.hashCode() ^ height;
66
67
		}
67
		public int hashCode() {
68
68
			return font.hashCode() ^ height;
69
		protected void setValues(Font font, int height) {
69
		}
70
			this.font = font;
70
71
			this.height = height;
71
		protected void setValues(Font font, int height) {
72
		}
72
			this.font = font;
73
	}
73
			this.height = height;
74
74
		}
75
	/**
75
	}
76
	 * The internal state of the scaled graphics.
76
77
	 */
77
	/**
78
	protected static class State {
78
	 * The internal state of the scaled graphics.
79
		private double appliedX;
79
	 */
80
		private double appliedY;
80
	protected static class State {
81
		private Font font;
81
		private double appliedX;
82
		private int lineWidth;
82
		private double appliedY;
83
		//private double zoom;  // This has been replaced with xZoom and yZoom
83
		private Font font;
84
		private double xZoom;
84
		private int lineWidth;
85
		private double yZoom;
85
		// private double zoom; // This has been replaced with xZoom and yZoom
86
86
		private double xZoom;
87
		/**
87
		private double yZoom;
88
		 * Constructs a new, uninitialized State object.
88
89
		 */
89
		/**
90
		protected State() {
90
		 * Constructs a new, uninitialized State object.
91
		}
91
		 */
92
92
		protected State() {
93
		/**
93
		}
94
		 * Constructs a new State object and initializes the properties based on
94
95
		 * the given values.
95
		/**
96
		 * 
96
		 * Constructs a new State object and initializes the properties based on
97
		 * @param zoom
97
		 * the given values.
98
		 *            the zoom factor
98
		 * 
99
		 * @param x
99
		 * @param zoom
100
		 *            the x offset
100
		 *            the zoom factor
101
		 * @param y
101
		 * @param x
102
		 *            the y offset
102
		 *            the x offset
103
		 * @param font
103
		 * @param y
104
		 *            the font
104
		 *            the y offset
105
		 * @param lineWidth
105
		 * @param font
106
		 *            the line width
106
		 *            the font
107
		 */
107
		 * @param lineWidth
108
		protected State(double xZoom, double yZoom, double x, double y, Font font, int lineWidth) {
108
		 *            the line width
109
			this.xZoom = xZoom;
109
		 */
110
			this.yZoom = yZoom;
110
		protected State(double xZoom, double yZoom, double x, double y,
111
			this.appliedX = x;
111
				Font font, int lineWidth) {
112
			this.appliedY = y;
112
			this.xZoom = xZoom;
113
			this.font = font;
113
			this.yZoom = yZoom;
114
			this.lineWidth = lineWidth;
114
			this.appliedX = x;
115
		}
115
			this.appliedY = y;
116
116
			this.font = font;
117
		/**
117
			this.lineWidth = lineWidth;
118
		 * Sets all the properties of the state object.
118
		}
119
		 * 
119
120
		 * @param zoom
120
		/**
121
		 *            the zoom factor
121
		 * Sets all the properties of the state object.
122
		 * @param x
122
		 * 
123
		 *            the x offset
123
		 * @param zoom
124
		 * @param y
124
		 *            the zoom factor
125
		 *            the y offset
125
		 * @param x
126
		 * @param font
126
		 *            the x offset
127
		 *            the font
127
		 * @param y
128
		 * @param lineWidth
128
		 *            the y offset
129
		 *            the line width
129
		 * @param font
130
		 */
130
		 *            the font
131
		protected void setValues(double xZoom, double yZoom, double x, double y, Font font, int lineWidth) {
131
		 * @param lineWidth
132
			this.xZoom = xZoom;
132
		 *            the line width
133
			this.yZoom = yZoom;
133
		 */
134
			this.appliedX = x;
134
		protected void setValues(double xZoom, double yZoom, double x,
135
			this.appliedY = y;
135
				double y, Font font, int lineWidth) {
136
			this.font = font;
136
			this.xZoom = xZoom;
137
			this.lineWidth = lineWidth;
137
			this.yZoom = yZoom;
138
		}
138
			this.appliedX = x;
139
	}
139
			this.appliedY = y;
140
140
			this.font = font;
141
	private static int[][] intArrayCache = new int[8][];
141
			this.lineWidth = lineWidth;
142
	private final Rectangle tempRECT = new Rectangle();
142
		}
143
143
	}
144
	static {
144
145
		for (int i = 0; i < intArrayCache.length; i++) {
145
	private static int[][] intArrayCache = new int[8][];
146
			intArrayCache[i] = new int[i + 1];
146
	private final Rectangle tempRECT = new Rectangle();
147
		}
147
148
	}
148
	static {
149
149
		for (int i = 0; i < intArrayCache.length; i++) {
150
	private boolean allowText = true;
150
			intArrayCache[i] = new int[i + 1];
151
	//	private static final Point PT = new Point();
151
		}
152
	private Map fontCache = new HashMap();
152
	}
153
	private Map fontDataCache = new HashMap();
153
154
	private FontKey fontKey = new FontKey();
154
	private boolean allowText = true;
155
	private double fractionalX;
155
	// private static final Point PT = new Point();
156
	private double fractionalY;
156
	private Map fontCache = new HashMap();
157
	private Graphics graphics;
157
	private Map fontDataCache = new HashMap();
158
	private FontHeightCache localCache = new FontHeightCache();
158
	private FontKey fontKey = new FontKey();
159
	private Font localFont;
159
	private double fractionalX;
160
	private int localLineWidth;
160
	private double fractionalY;
161
	private List stack = new ArrayList();
161
	private Graphics graphics;
162
	private int stackPointer = 0;
162
	private FontHeightCache localCache = new FontHeightCache();
163
	private FontHeightCache targetCache = new FontHeightCache();
163
	private Font localFont;
164
164
	private int localLineWidth;
165
	double xZoom = 1.0;
165
	private List stack = new ArrayList();
166
	double yZoom = 1.0;
166
	private int stackPointer = 0;
167
167
	private FontHeightCache targetCache = new FontHeightCache();
168
	/**
168
169
	 * Constructs a new ScaledGraphics based on the given Graphics object.
169
	double xZoom = 1.0;
170
	 * 
170
	double yZoom = 1.0;
171
	 * @param g
171
172
	 *            the base graphics object
172
	/**
173
	 */
173
	 * Constructs a new ScaledGraphics based on the given Graphics object.
174
	public XYScaledGraphics(Graphics g) {
174
	 * 
175
		super(g);
175
	 * @param g
176
		graphics = g;
176
	 *            the base graphics object
177
		localFont = g.getFont();
177
	 */
178
		localLineWidth = g.getLineWidth();
178
	public XYScaledGraphics(Graphics g) {
179
	}
179
		super(g);
180
180
		graphics = g;
181
	/** @see Graphics#clipRect(Rectangle) */
181
		localFont = g.getFont();
182
	public void clipRect(Rectangle r) {
182
		localLineWidth = g.getLineWidth();
183
		graphics.clipRect(zoomClipRect(r));
183
	}
184
	}
184
185
185
	/** @see Graphics#clipRect(Rectangle) */
186
	Font createFont(FontData data) {
186
	public void clipRect(Rectangle r) {
187
		return new Font(Display.getCurrent(), data);
187
		graphics.clipRect(zoomClipRect(r));
188
	}
188
	}
189
189
190
	/** @see Graphics#dispose() */
190
	Font createFont(FontData data) {
191
	public void dispose() {
191
		return new Font(Display.getCurrent(), data);
192
		//Remove all states from the stack
192
	}
193
		while (stackPointer > 0) {
193
194
			popState();
194
	/** @see Graphics#dispose() */
195
		}
195
	public void dispose() {
196
196
		// Remove all states from the stack
197
		//Dispose fonts
197
		while (stackPointer > 0) {
198
		Iterator iter = fontCache.values().iterator();
198
			popState();
199
		while (iter.hasNext()) {
199
		}
200
			Font font = ((Font) iter.next());
200
201
			font.dispose();
201
		// Dispose fonts
202
		}
202
		Iterator iter = fontCache.values().iterator();
203
203
		while (iter.hasNext()) {
204
	}
204
			Font font = ((Font) iter.next());
205
205
			font.dispose();
206
	/** @see Graphics#drawArc(int, int, int, int, int, int) */
206
		}
207
	public void drawArc(int x, int y, int w, int h, int offset, int sweep) {
207
208
		Rectangle z = zoomRect(x, y, w, h);
208
	}
209
		if (z.isEmpty() || sweep == 0) {
209
210
			return;
210
	/** @see Graphics#drawArc(int, int, int, int, int, int) */
211
		}
211
	public void drawArc(int x, int y, int w, int h, int offset, int sweep) {
212
		graphics.drawArc(z, offset, sweep);
212
		Rectangle z = zoomRect(x, y, w, h);
213
	}
213
		if (z.isEmpty() || sweep == 0) {
214
214
			return;
215
	/** @see Graphics#drawFocus(int, int, int, int) */
215
		}
216
	public void drawFocus(int x, int y, int w, int h) {
216
		graphics.drawArc(z, offset, sweep);
217
		graphics.drawFocus(zoomRect(x, y, w, h));
217
	}
218
	}
218
219
219
	/** @see Graphics#drawFocus(int, int, int, int) */
220
	/** @see Graphics#drawImage(Image, int, int) */
220
	public void drawFocus(int x, int y, int w, int h) {
221
	public void drawImage(Image srcImage, int x, int y) {
221
		graphics.drawFocus(zoomRect(x, y, w, h));
222
		org.eclipse.swt.graphics.Rectangle size = srcImage.getBounds();
222
	}
223
		double imageZoom = Math.min(xZoom, yZoom);
223
224
		graphics.drawImage(srcImage, 0, 0, size.width, size.height, (int) (Math.floor((x * xZoom + fractionalX))), (int) (Math.floor((y * yZoom + fractionalY))), (int) (Math.floor((size.width * imageZoom + fractionalX))), (int) (Math.floor((size.height * imageZoom + fractionalY))));
224
	/** @see Graphics#drawImage(Image, int, int) */
225
	}
225
	public void drawImage(Image srcImage, int x, int y) {
226
226
		org.eclipse.swt.graphics.Rectangle size = srcImage.getBounds();
227
	/** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */
227
		double imageZoom = Math.min(xZoom, yZoom);
228
	public void drawImage(Image srcImage, int sx, int sy, int sw, int sh, int tx, int ty, int tw, int th) {
228
		graphics.drawImage(srcImage, 0, 0, size.width, size.height, (int) (Math
229
		//"t" == target rectangle, "s" = source
229
				.floor((x * xZoom + fractionalX))), (int) (Math.floor((y
230
230
				* yZoom + fractionalY))), (int) (Math.floor((size.width
231
		Rectangle t = zoomRect(tx, ty, tw, th);
231
				* imageZoom + fractionalX))), (int) (Math.floor((size.height
232
		if (!t.isEmpty()) {
232
				* imageZoom + fractionalY))));
233
			graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width, t.height);
233
	}
234
		}
234
235
	}
235
	/** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */
236
236
	public void drawImage(Image srcImage, int sx, int sy, int sw, int sh,
237
	/** @see Graphics#drawLine(int, int, int, int) */
237
			int tx, int ty, int tw, int th) {
238
	public void drawLine(int x1, int y1, int x2, int y2) {
238
		// "t" == target rectangle, "s" = source
239
		graphics.drawLine((int) (Math.floor((x1 * xZoom + fractionalX))), (int) (Math.floor((y1 * yZoom + fractionalY))), (int) (Math.floor((x2 * xZoom + fractionalX))), (int) (Math.floor((y2 * yZoom + fractionalY))));
239
240
	}
240
		Rectangle t = zoomRect(tx, ty, tw, th);
241
241
		if (!t.isEmpty()) {
242
	/** @see Graphics#drawOval(int, int, int, int) */
242
			graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width,
243
	public void drawOval(int x, int y, int w, int h) {
243
					t.height);
244
		graphics.drawOval(zoomRect(x, y, w, h));
244
		}
245
	}
245
	}
246
246
247
	/** @see Graphics#drawPoint(int, int) */
247
	/** @see Graphics#drawLine(int, int, int, int) */
248
	public void drawPoint(int x, int y) {
248
	public void drawLine(int x1, int y1, int x2, int y2) {
249
		graphics.drawPoint((int) Math.floor(x * xZoom + fractionalX), (int) Math.floor(y * yZoom + fractionalY));
249
		graphics.drawLine((int) (Math.floor((x1 * xZoom + fractionalX))),
250
	}
250
				(int) (Math.floor((y1 * yZoom + fractionalY))), (int) (Math
251
251
						.floor((x2 * xZoom + fractionalX))), (int) (Math
252
	/**
252
						.floor((y2 * yZoom + fractionalY))));
253
	 * @see Graphics#drawPolygon(int[])
253
	}
254
	 */
254
255
	public void drawPolygon(int[] points) {
255
	/** @see Graphics#drawOval(int, int, int, int) */
256
		graphics.drawPolygon(zoomPointList(points));
256
	public void drawOval(int x, int y, int w, int h) {
257
	}
257
		graphics.drawOval(zoomRect(x, y, w, h));
258
258
	}
259
	/** @see Graphics#drawPolygon(PointList) */
259
260
	public void drawPolygon(PointList points) {
260
	/** @see Graphics#drawPoint(int, int) */
261
		graphics.drawPolygon(zoomPointList(points.toIntArray()));
261
	public void drawPoint(int x, int y) {
262
	}
262
		graphics.drawPoint((int) Math.floor(x * xZoom + fractionalX),
263
263
				(int) Math.floor(y * yZoom + fractionalY));
264
	/**
264
	}
265
	 * @see Graphics#drawPolyline(int[])
265
266
	 */
266
	/**
267
	public void drawPolyline(int[] points) {
267
	 * @see Graphics#drawPolygon(int[])
268
		graphics.drawPolyline(zoomPointList(points));
268
	 */
269
	}
269
	public void drawPolygon(int[] points) {
270
270
		graphics.drawPolygon(zoomPointList(points));
271
	/** @see Graphics#drawPolyline(PointList) */
271
	}
272
	public void drawPolyline(PointList points) {
272
273
		graphics.drawPolyline(zoomPointList(points.toIntArray()));
273
	/** @see Graphics#drawPolygon(PointList) */
274
	}
274
	public void drawPolygon(PointList points) {
275
275
		graphics.drawPolygon(zoomPointList(points.toIntArray()));
276
	/** @see Graphics#drawRectangle(int, int, int, int) */
276
	}
277
	public void drawRectangle(int x, int y, int w, int h) {
277
278
		graphics.drawRectangle(zoomRect(x, y, w, h));
278
	/**
279
	}
279
	 * @see Graphics#drawPolyline(int[])
280
280
	 */
281
	/** @see Graphics#drawRoundRectangle(Rectangle, int, int) */
281
	public void drawPolyline(int[] points) {
282
	public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
282
		graphics.drawPolyline(zoomPointList(points));
283
		graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height), (int) (arcWidth * xZoom), (int) (arcHeight * yZoom));
283
	}
284
	}
284
285
285
	/** @see Graphics#drawPolyline(PointList) */
286
	/** @see Graphics#drawString(String, int, int) */
286
	public void drawPolyline(PointList points) {
287
	public void drawString(String s, int x, int y) {
287
		graphics.drawPolyline(zoomPointList(points.toIntArray()));
288
		if (allowText) {
288
	}
289
			graphics.drawString(s, zoomTextPoint(x, y));
289
290
		}
290
	/** @see Graphics#drawRectangle(int, int, int, int) */
291
	}
291
	public void drawRectangle(int x, int y, int w, int h) {
292
292
		graphics.drawRectangle(zoomRect(x, y, w, h));
293
	/** @see Graphics#drawText(String, int, int) */
293
	}
294
	public void drawText(String s, int x, int y) {
294
295
		if (allowText) {
295
	/** @see Graphics#drawRoundRectangle(Rectangle, int, int) */
296
			graphics.drawText(s, zoomTextPoint(x, y));
296
	public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
297
		}
297
		graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height),
298
	}
298
				(int) (arcWidth * xZoom), (int) (arcHeight * yZoom));
299
299
	}
300
	/**
300
301
	 * @see Graphics#drawText(String, int, int, int)
301
	/** @see Graphics#drawString(String, int, int) */
302
	 */
302
	public void drawString(String s, int x, int y) {
303
	public void drawText(String s, int x, int y, int style) {
303
		if (allowText) {
304
		if (allowText) {
304
			graphics.drawString(s, zoomTextPoint(x, y));
305
			graphics.drawText(s, zoomTextPoint(x, y), style);
305
		}
306
		}
306
	}
307
	}
307
308
308
	/** @see Graphics#drawText(String, int, int) */
309
	/**
309
	public void drawText(String s, int x, int y) {
310
	 * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color,
310
		if (allowText) {
311
	 *      Color)
311
			graphics.drawText(s, zoomTextPoint(x, y));
312
	 */
312
		}
313
	public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) {
313
	}
314
		TextLayout scaled = zoomTextLayout(layout);
314
315
		graphics.drawTextLayout(scaled, (int) Math.floor(x * xZoom + fractionalX), (int) Math.floor(y * yZoom + fractionalY), selectionStart, selectionEnd, selectionBackground, selectionForeground);
315
	/**
316
		scaled.dispose();
316
	 * @see Graphics#drawText(String, int, int, int)
317
	}
317
	 */
318
318
	public void drawText(String s, int x, int y, int style) {
319
	/** @see Graphics#fillArc(int, int, int, int, int, int) */
319
		if (allowText) {
320
	public void fillArc(int x, int y, int w, int h, int offset, int sweep) {
320
			graphics.drawText(s, zoomTextPoint(x, y), style);
321
		Rectangle z = zoomFillRect(x, y, w, h);
321
		}
322
		if (z.isEmpty() || sweep == 0) {
322
	}
323
			return;
323
324
		}
324
	/**
325
		graphics.fillArc(z, offset, sweep);
325
	 * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color,
326
	}
326
	 *      Color)
327
327
	 */
328
	/** @see Graphics#fillGradient(int, int, int, int, boolean) */
328
	public void drawTextLayout(TextLayout layout, int x, int y,
329
	public void fillGradient(int x, int y, int w, int h, boolean vertical) {
329
			int selectionStart, int selectionEnd, Color selectionForeground,
330
		graphics.fillGradient(zoomFillRect(x, y, w, h), vertical);
330
			Color selectionBackground) {
331
	}
331
		TextLayout scaled = zoomTextLayout(layout);
332
332
		graphics.drawTextLayout(scaled, (int) Math.floor(x * xZoom
333
	/** @see Graphics#fillOval(int, int, int, int) */
333
				+ fractionalX), (int) Math.floor(y * yZoom + fractionalY),
334
	public void fillOval(int x, int y, int w, int h) {
334
				selectionStart, selectionEnd, selectionBackground,
335
		graphics.fillOval(zoomFillRect(x, y, w, h));
335
				selectionForeground);
336
	}
336
		scaled.dispose();
337
337
	}
338
	/**
338
339
	 * @see Graphics#fillPolygon(int[])
339
	/** @see Graphics#fillArc(int, int, int, int, int, int) */
340
	 */
340
	public void fillArc(int x, int y, int w, int h, int offset, int sweep) {
341
	public void fillPolygon(int[] points) {
341
		Rectangle z = zoomFillRect(x, y, w, h);
342
		graphics.fillPolygon(zoomPointList(points));
342
		if (z.isEmpty() || sweep == 0) {
343
	}
343
			return;
344
344
		}
345
	/** @see Graphics#fillPolygon(PointList) */
345
		graphics.fillArc(z, offset, sweep);
346
	public void fillPolygon(PointList points) {
346
	}
347
		graphics.fillPolygon(zoomPointList(points.toIntArray()));
347
348
	}
348
	/** @see Graphics#fillGradient(int, int, int, int, boolean) */
349
349
	public void fillGradient(int x, int y, int w, int h, boolean vertical) {
350
	/** @see Graphics#fillRectangle(int, int, int, int) */
350
		graphics.fillGradient(zoomFillRect(x, y, w, h), vertical);
351
	public void fillRectangle(int x, int y, int w, int h) {
351
	}
352
		graphics.fillRectangle(zoomFillRect(x, y, w, h));
352
353
	}
353
	/** @see Graphics#fillOval(int, int, int, int) */
354
354
	public void fillOval(int x, int y, int w, int h) {
355
	/** @see Graphics#fillRoundRectangle(Rectangle, int, int) */
355
		graphics.fillOval(zoomFillRect(x, y, w, h));
356
	public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
356
	}
357
		graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height), (int) (arcWidth * xZoom), (int) (arcHeight * yZoom));
357
358
	}
358
	/**
359
359
	 * @see Graphics#fillPolygon(int[])
360
	/** @see Graphics#fillString(String, int, int) */
360
	 */
361
	public void fillString(String s, int x, int y) {
361
	public void fillPolygon(int[] points) {
362
		if (allowText) {
362
		graphics.fillPolygon(zoomPointList(points));
363
			graphics.fillString(s, zoomTextPoint(x, y));
363
	}
364
		}
364
365
	}
365
	/** @see Graphics#fillPolygon(PointList) */
366
366
	public void fillPolygon(PointList points) {
367
	/** @see Graphics#fillText(String, int, int) */
367
		graphics.fillPolygon(zoomPointList(points.toIntArray()));
368
	public void fillText(String s, int x, int y) {
368
	}
369
		if (allowText) {
369
370
			graphics.fillText(s, zoomTextPoint(x, y));
370
	/** @see Graphics#fillRectangle(int, int, int, int) */
371
		}
371
	public void fillRectangle(int x, int y, int w, int h) {
372
	}
372
		graphics.fillRectangle(zoomFillRect(x, y, w, h));
373
373
	}
374
	/**
374
375
	 * @see Graphics#getAbsoluteScale()
375
	/** @see Graphics#fillRoundRectangle(Rectangle, int, int) */
376
	 */
376
	public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
377
	public double getAbsoluteScale() {
377
		graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height),
378
		return xZoom * graphics.getAbsoluteScale();
378
				(int) (arcWidth * xZoom), (int) (arcHeight * yZoom));
379
	}
379
	}
380
380
381
	/**
381
	/** @see Graphics#fillString(String, int, int) */
382
	 * @see Graphics#getAlpha()
382
	public void fillString(String s, int x, int y) {
383
	 */
383
		if (allowText) {
384
	public int getAlpha() {
384
			graphics.fillString(s, zoomTextPoint(x, y));
385
		return graphics.getAlpha();
385
		}
386
	}
386
	}
387
387
388
	/**
388
	/** @see Graphics#fillText(String, int, int) */
389
	 * @see Graphics#getAntialias()
389
	public void fillText(String s, int x, int y) {
390
	 */
390
		if (allowText) {
391
	public int getAntialias() {
391
			graphics.fillText(s, zoomTextPoint(x, y));
392
		return graphics.getAntialias();
392
		}
393
	}
393
	}
394
394
395
	/** @see Graphics#getBackgroundColor() */
395
	/**
396
	public Color getBackgroundColor() {
396
	 * @see Graphics#getAbsoluteScale()
397
		return graphics.getBackgroundColor();
397
	 */
398
	}
398
	public double getAbsoluteScale() {
399
399
		return xZoom * graphics.getAbsoluteScale();
400
	Font getCachedFont(FontKey key) {
400
	}
401
		Font font = (Font) fontCache.get(key);
401
402
		if (font != null) {
402
	/**
403
			return font;
403
	 * @see Graphics#getAlpha()
404
		}
404
	 */
405
		key = new FontKey(key.font, key.height);
405
	public int getAlpha() {
406
		FontData data = key.font.getFontData()[0];
406
		return graphics.getAlpha();
407
		data.setHeight(key.height);
407
	}
408
		Font zoomedFont = createFont(data);
408
409
		fontCache.put(key, zoomedFont);
409
	/**
410
		return zoomedFont;
410
	 * @see Graphics#getAntialias()
411
	}
411
	 */
412
412
	public int getAntialias() {
413
	FontData getCachedFontData(Font f) {
413
		return graphics.getAntialias();
414
		FontData data = (FontData) fontDataCache.get(f);
414
	}
415
		if (data != null) {
415
416
			return data;
416
	/** @see Graphics#getBackgroundColor() */
417
		}
417
	public Color getBackgroundColor() {
418
		data = getLocalFont().getFontData()[0];
418
		return graphics.getBackgroundColor();
419
		fontDataCache.put(f, data);
419
	}
420
		return data;
420
421
	}
421
	Font getCachedFont(FontKey key) {
422
422
		Font font = (Font) fontCache.get(key);
423
	/** @see Graphics#getClip(Rectangle) */
423
		if (font != null) {
424
	public Rectangle getClip(Rectangle rect) {
424
			return font;
425
		graphics.getClip(rect);
425
		}
426
		int x = (int) (rect.x / xZoom);
426
		key = new FontKey(key.font, key.height);
427
		int y = (int) (rect.y / yZoom);
427
		FontData data = key.font.getFontData()[0];
428
		/*
428
		data.setHeight(key.height);
429
		 * If the clip rectangle is queried, perform an inverse zoom, and take
429
		Font zoomedFont = createFont(data);
430
		 * the ceiling of the resulting double. This is necessary because
430
		fontCache.put(key, zoomedFont);
431
		 * forward scaling essentially performs a floor() function. Without
431
		return zoomedFont;
432
		 * this, figures will think that they don't need to paint when actually
432
	}
433
		 * they do.
433
434
		 */
434
	FontData getCachedFontData(Font f) {
435
		rect.width = (int) Math.ceil(rect.right() / xZoom) - x;
435
		FontData data = (FontData) fontDataCache.get(f);
436
		rect.height = (int) Math.ceil(rect.bottom() / yZoom) - y;
436
		if (data != null) {
437
		rect.x = x;
437
			return data;
438
		rect.y = y;
438
		}
439
		return rect;
439
		data = getLocalFont().getFontData()[0];
440
	}
440
		fontDataCache.put(f, data);
441
441
		return data;
442
	/**
442
	}
443
	 * @see Graphics#getFillRule()
443
444
	 */
444
	/** @see Graphics#getClip(Rectangle) */
445
	public int getFillRule() {
445
	public Rectangle getClip(Rectangle rect) {
446
		return graphics.getFillRule();
446
		graphics.getClip(rect);
447
	}
447
		int x = (int) (rect.x / xZoom);
448
448
		int y = (int) (rect.y / yZoom);
449
	/** @see Graphics#getFont() */
449
		/*
450
	public Font getFont() {
450
		 * If the clip rectangle is queried, perform an inverse zoom, and take
451
		return getLocalFont();
451
		 * the ceiling of the resulting double. This is necessary because
452
	}
452
		 * forward scaling essentially performs a floor() function. Without
453
453
		 * this, figures will think that they don't need to paint when actually
454
	/** @see Graphics#getFontMetrics() */
454
		 * they do.
455
	public FontMetrics getFontMetrics() {
455
		 */
456
		return FigureUtilities.getFontMetrics(localFont);
456
		rect.width = (int) Math.ceil(rect.right() / xZoom) - x;
457
	}
457
		rect.height = (int) Math.ceil(rect.bottom() / yZoom) - y;
458
458
		rect.x = x;
459
	/** @see Graphics#getForegroundColor() */
459
		rect.y = y;
460
	public Color getForegroundColor() {
460
		return rect;
461
		return graphics.getForegroundColor();
461
	}
462
	}
462
463
463
	/**
464
	/**
464
	 * @see Graphics#getFillRule()
465
	 * @see Graphics#getInterpolation()
465
	 */
466
	 */
466
	public int getFillRule() {
467
	public int getInterpolation() {
467
		return graphics.getFillRule();
468
		return graphics.getInterpolation();
468
	}
469
	}
469
470
470
	/** @see Graphics#getFont() */
471
	/**
471
	public Font getFont() {
472
	 * @see Graphics#getLineCap()
472
		return getLocalFont();
473
	 */
473
	}
474
	public int getLineCap() {
474
475
		return graphics.getLineCap();
475
	/** @see Graphics#getFontMetrics() */
476
	}
476
	public FontMetrics getFontMetrics() {
477
477
		return FigureUtilities.getFontMetrics(localFont);
478
	/**
478
	}
479
	 * @see Graphics#getLineJoin()
479
480
	 */
480
	/** @see Graphics#getForegroundColor() */
481
	public int getLineJoin() {
481
	public Color getForegroundColor() {
482
		return graphics.getLineJoin();
482
		return graphics.getForegroundColor();
483
	}
483
	}
484
484
485
	/** @see Graphics#getLineStyle() */
485
	/**
486
	public int getLineStyle() {
486
	 * @see Graphics#getInterpolation()
487
		return graphics.getLineStyle();
487
	 */
488
	}
488
	public int getInterpolation() {
489
489
		return graphics.getInterpolation();
490
	/** @see Graphics#getLineWidth() */
490
	}
491
	public int getLineWidth() {
491
492
		return getLocalLineWidth();
492
	/**
493
	}
493
	 * @see Graphics#getLineCap()
494
494
	 */
495
	private Font getLocalFont() {
495
	public int getLineCap() {
496
		return localFont;
496
		return graphics.getLineCap();
497
	}
497
	}
498
498
499
	private int getLocalLineWidth() {
499
	/**
500
		return localLineWidth;
500
	 * @see Graphics#getLineJoin()
501
	}
501
	 */
502
502
	public int getLineJoin() {
503
	/**
503
		return graphics.getLineJoin();
504
	 * @see Graphics#getTextAntialias()
504
	}
505
	 */
505
506
	public int getTextAntialias() {
506
	/** @see Graphics#getLineStyle() */
507
		return graphics.getTextAntialias();
507
	public int getLineStyle() {
508
	}
508
		return graphics.getLineStyle();
509
509
	}
510
	/** @see Graphics#getXORMode() */
510
511
	public boolean getXORMode() {
511
	/** @see Graphics#getLineWidth() */
512
		return graphics.getXORMode();
512
	public int getLineWidth() {
513
	}
513
		return getLocalLineWidth();
514
514
	}
515
	/** @see Graphics#popState() */
515
516
	public void popState() {
516
	private Font getLocalFont() {
517
		graphics.popState();
517
		return localFont;
518
		stackPointer--;
518
	}
519
		restoreLocalState((State) stack.get(stackPointer));
519
520
	}
520
	private int getLocalLineWidth() {
521
521
		return localLineWidth;
522
	/** @see Graphics#pushState() */
522
	}
523
	public void pushState() {
523
524
		State s;
524
	/**
525
		if (stack.size() > stackPointer) {
525
	 * @see Graphics#getTextAntialias()
526
			s = (State) stack.get(stackPointer);
526
	 */
527
			s.setValues(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(), localLineWidth);
527
	public int getTextAntialias() {
528
		} else {
528
		return graphics.getTextAntialias();
529
			stack.add(new State(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(), localLineWidth));
529
	}
530
		}
530
531
		stackPointer++;
531
	/** @see Graphics#getXORMode() */
532
532
	public boolean getXORMode() {
533
		graphics.pushState();
533
		return graphics.getXORMode();
534
	}
534
	}
535
535
536
	private void restoreLocalState(State state) {
536
	/** @see Graphics#popState() */
537
		this.fractionalX = state.appliedX;
537
	public void popState() {
538
		this.fractionalY = state.appliedY;
538
		graphics.popState();
539
		setScale(state.xZoom, state.yZoom);
539
		stackPointer--;
540
		setLocalFont(state.font);
540
		restoreLocalState((State) stack.get(stackPointer));
541
		setLocalLineWidth(state.lineWidth);
541
	}
542
	}
542
543
543
	/** @see Graphics#pushState() */
544
	/** @see Graphics#restoreState() */
544
	public void pushState() {
545
	public void restoreState() {
545
		State s;
546
		graphics.restoreState();
546
		if (stack.size() > stackPointer) {
547
		restoreLocalState((State) stack.get(stackPointer - 1));
547
			s = (State) stack.get(stackPointer);
548
	}
548
			s.setValues(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(),
549
549
					localLineWidth);
550
	public void scale(double xAmount, double yAmount) {
550
		} else {
551
		setScale(xZoom * xAmount, yZoom * yAmount);
551
			stack.add(new State(xZoom, yZoom, fractionalX, fractionalY,
552
552
					getLocalFont(), localLineWidth));
553
	}
553
		}
554
554
		stackPointer++;
555
	/** @see Graphics#scale(double) */
555
556
	public void scale(double amount) {
556
		graphics.pushState();
557
		//setScale(zoom * amount);
557
	}
558
		throw new RuntimeException("Operation not supported, use scale(x, y)");
558
559
	}
559
	private void restoreLocalState(State state) {
560
560
		this.fractionalX = state.appliedX;
561
	/**
561
		this.fractionalY = state.appliedY;
562
	 * @see Graphics#setAlpha(int)
562
		setScale(state.xZoom, state.yZoom);
563
	 */
563
		setLocalFont(state.font);
564
	public void setAlpha(int alpha) {
564
		setLocalLineWidth(state.lineWidth);
565
		graphics.setAlpha(alpha);
565
	}
566
	}
566
567
567
	/** @see Graphics#restoreState() */
568
	/**
568
	public void restoreState() {
569
	 * @see Graphics#setAntialias(int)
569
		graphics.restoreState();
570
	 */
570
		restoreLocalState((State) stack.get(stackPointer - 1));
571
	public void setAntialias(int value) {
571
	}
572
		graphics.setAntialias(value);
572
573
	}
573
	public void scale(double xAmount, double yAmount) {
574
574
		setScale(xZoom * xAmount, yZoom * yAmount);
575
	/** @see Graphics#setBackgroundColor(Color) */
575
576
	public void setBackgroundColor(Color rgb) {
576
	}
577
		graphics.setBackgroundColor(rgb);
577
578
	}
578
	/** @see Graphics#scale(double) */
579
579
	public void scale(double amount) {
580
	/** @see Graphics#setClip(Rectangle) */
580
		// setScale(zoom * amount);
581
	public void setClip(Rectangle r) {
581
		throw new RuntimeException("Operation not supported, use scale(x, y)");
582
		graphics.setClip(zoomClipRect(r));
582
	}
583
	}
583
584
584
	/**
585
	/**
585
	 * @see Graphics#setAlpha(int)
586
	 * @see Graphics#setFillRule(int)
586
	 */
587
	 */
587
	public void setAlpha(int alpha) {
588
	public void setFillRule(int rule) {
588
		graphics.setAlpha(alpha);
589
		graphics.setFillRule(rule);
589
	}
590
	}
590
591
591
	/**
592
	/** @see Graphics#setFont(Font) */
592
	 * @see Graphics#setAntialias(int)
593
	public void setFont(Font f) {
593
	 */
594
		setLocalFont(f);
594
	public void setAntialias(int value) {
595
	}
595
		graphics.setAntialias(value);
596
596
	}
597
	/** @see Graphics#setForegroundColor(Color) */
597
598
	public void setForegroundColor(Color rgb) {
598
	/** @see Graphics#setBackgroundColor(Color) */
599
		graphics.setForegroundColor(rgb);
599
	public void setBackgroundColor(Color rgb) {
600
	}
600
		graphics.setBackgroundColor(rgb);
601
601
	}
602
	/**
602
603
	 * @see org.eclipse.draw2d.Graphics#setInterpolation(int)
603
	/** @see Graphics#setClip(Rectangle) */
604
	 */
604
	public void setClip(Rectangle r) {
605
	public void setInterpolation(int interpolation) {
605
		graphics.setClip(zoomClipRect(r));
606
		graphics.setInterpolation(interpolation);
606
	}
607
	}
607
608
608
	/**
609
	/**
609
	 * @see Graphics#setFillRule(int)
610
	 * @see Graphics#setLineCap(int)
610
	 */
611
	 */
611
	public void setFillRule(int rule) {
612
	public void setLineCap(int cap) {
612
		graphics.setFillRule(rule);
613
		graphics.setLineCap(cap);
613
	}
614
	}
614
615
615
	/** @see Graphics#setFont(Font) */
616
	/**
616
	public void setFont(Font f) {
617
	 * @see Graphics#setLineDash(int[])
617
		setLocalFont(f);
618
	 */
618
	}
619
	public void setLineDash(int[] dash) {
619
620
		graphics.setLineDash(dash);
620
	/** @see Graphics#setForegroundColor(Color) */
621
	}
621
	public void setForegroundColor(Color rgb) {
622
622
		graphics.setForegroundColor(rgb);
623
	/**
623
	}
624
	 * @see Graphics#setLineJoin(int)
624
625
	 */
625
	/**
626
	public void setLineJoin(int join) {
626
	 * @see org.eclipse.draw2d.Graphics#setInterpolation(int)
627
		graphics.setLineJoin(join);
627
	 */
628
	}
628
	public void setInterpolation(int interpolation) {
629
629
		graphics.setInterpolation(interpolation);
630
	/** @see Graphics#setLineStyle(int) */
630
	}
631
	public void setLineStyle(int style) {
631
632
		graphics.setLineStyle(style);
632
	/**
633
	}
633
	 * @see Graphics#setLineCap(int)
634
634
	 */
635
	/** @see Graphics#setLineWidth(int) */
635
	public void setLineCap(int cap) {
636
	public void setLineWidth(int width) {
636
		graphics.setLineCap(cap);
637
		setLocalLineWidth(width);
637
	}
638
	}
638
639
639
	/**
640
	private void setLocalFont(Font f) {
640
	 * @see Graphics#setLineDash(int[])
641
		localFont = f;
641
	 */
642
		graphics.setFont(zoomFont(f));
642
	public void setLineDash(int[] dash) {
643
	}
643
		graphics.setLineDash(dash);
644
644
	}
645
	private void setLocalLineWidth(int width) {
645
646
		localLineWidth = width;
646
	/**
647
		graphics.setLineWidth(zoomLineWidth(width));
647
	 * @see Graphics#setLineJoin(int)
648
	}
648
	 */
649
649
	public void setLineJoin(int join) {
650
	public void setScale(double xValue, double yValue) {
650
		graphics.setLineJoin(join);
651
		if (xValue == xZoom && yValue == yZoom) {
651
	}
652
			return;
652
653
		}
653
	/** @see Graphics#setLineStyle(int) */
654
		this.xZoom = xValue;
654
	public void setLineStyle(int style) {
655
		this.yZoom = yValue;
655
		graphics.setLineStyle(style);
656
		graphics.setFont(zoomFont(getLocalFont()));
656
	}
657
		graphics.setLineWidth(zoomLineWidth(localLineWidth));
657
658
	}
658
	/** @see Graphics#setLineWidth(int) */
659
659
	public void setLineWidth(int width) {
660
	void setScale(double value) {
660
		setLocalLineWidth(width);
661
		throw new RuntimeException("Operation not supported, use setScale(x,y)");
661
	}
662
662
663
		/*
663
	private void setLocalFont(Font f) {
664
		 * if (zoom == value) return; this.zoom = value;
664
		localFont = f;
665
		 * graphics.setFont(zoomFont(getLocalFont()));
665
		graphics.setFont(zoomFont(f));
666
		 * graphics.setLineWidth(zoomLineWidth(localLineWidth));
666
	}
667
		 */
667
668
	}
668
	private void setLocalLineWidth(int width) {
669
669
		localLineWidth = width;
670
	/**
670
		graphics.setLineWidth(zoomLineWidth(width));
671
	 * @see Graphics#setTextAntialias(int)
671
	}
672
	 */
672
673
	public void setTextAntialias(int value) {
673
	public void setScale(double xValue, double yValue) {
674
		graphics.setTextAntialias(value);
674
		if (xValue == xZoom && yValue == yZoom) {
675
	}
675
			return;
676
676
		}
677
	/** @see Graphics#setXORMode(boolean) */
677
		this.xZoom = xValue;
678
	public void setXORMode(boolean b) {
678
		this.yZoom = yValue;
679
		graphics.setXORMode(b);
679
		graphics.setFont(zoomFont(getLocalFont()));
680
	}
680
		graphics.setLineWidth(zoomLineWidth(localLineWidth));
681
681
	}
682
	/** @see Graphics#translate(int, int) */
682
683
	public void translate(int dx, int dy) {
683
	void setScale(double value) {
684
		// fractionalX/Y is the fractional part left over from previous 
684
		throw new RuntimeException("Operation not supported, use setScale(x,y)");
685
		// translates that gets lost in the integer approximation.
685
686
		double dxFloat = dx * xZoom + fractionalX;
686
		/*
687
		double dyFloat = dy * yZoom + fractionalY;
687
		 * if (zoom == value) return; this.zoom = value;
688
		fractionalX = dxFloat - Math.floor(dxFloat);
688
		 * graphics.setFont(zoomFont(getLocalFont()));
689
		fractionalY = dyFloat - Math.floor(dyFloat);
689
		 * graphics.setLineWidth(zoomLineWidth(localLineWidth));
690
		graphics.translate((int) Math.floor(dxFloat), (int) Math.floor(dyFloat));
690
		 */
691
	}
691
	}
692
692
693
	private Rectangle zoomClipRect(Rectangle r) {
693
	/**
694
		tempRECT.x = (int) (Math.floor(r.x * xZoom + fractionalX));
694
	 * @see Graphics#setTextAntialias(int)
695
		tempRECT.y = (int) (Math.floor(r.y * yZoom + fractionalY));
695
	 */
696
		tempRECT.width = (int) (Math.ceil(((r.x + r.width) * xZoom + fractionalX))) - tempRECT.x;
696
	public void setTextAntialias(int value) {
697
		tempRECT.height = (int) (Math.ceil(((r.y + r.height) * yZoom + fractionalY))) - tempRECT.y;
697
		graphics.setTextAntialias(value);
698
		return tempRECT;
698
	}
699
	}
699
700
700
	/** @see Graphics#setXORMode(boolean) */
701
	private Rectangle zoomFillRect(int x, int y, int w, int h) {
701
	public void setXORMode(boolean b) {
702
		tempRECT.x = (int) (Math.floor((x * xZoom + fractionalX)));
702
		graphics.setXORMode(b);
703
		tempRECT.y = (int) (Math.floor((y * yZoom + fractionalY)));
703
	}
704
		tempRECT.width = (int) (Math.floor(((x + w - 1) * xZoom + fractionalX))) - tempRECT.x + 1;
704
705
		tempRECT.height = (int) (Math.floor(((y + h - 1) * yZoom + fractionalY))) - tempRECT.y + 1;
705
	/** @see Graphics#translate(int, int) */
706
		return tempRECT;
706
	public void translate(int dx, int dy) {
707
	}
707
		// fractionalX/Y is the fractional part left over from previous
708
708
		// translates that gets lost in the integer approximation.
709
	Font zoomFont(Font f) {
709
		double dxFloat = dx * xZoom + fractionalX;
710
		if (f == null) {
710
		double dyFloat = dy * yZoom + fractionalY;
711
			f = Display.getCurrent().getSystemFont();
711
		fractionalX = dxFloat - Math.floor(dxFloat);
712
		}
712
		fractionalY = dyFloat - Math.floor(dyFloat);
713
		FontData data = getCachedFontData(f);
713
		graphics.translate((int) Math.floor(dxFloat), (int) Math.floor(dyFloat));
714
		int zoomedFontHeight = zoomFontHeight(data.getHeight());
714
	}
715
		allowText = zoomedFontHeight > 0;
715
716
		fontKey.setValues(f, zoomedFontHeight);
716
	private Rectangle zoomClipRect(Rectangle r) {
717
		return getCachedFont(fontKey);
717
		tempRECT.x = (int) (Math.floor(r.x * xZoom + fractionalX));
718
	}
718
		tempRECT.y = (int) (Math.floor(r.y * yZoom + fractionalY));
719
719
		tempRECT.width = (int) (Math
720
	int zoomFontHeight(int height) {
720
				.ceil(((r.x + r.width) * xZoom + fractionalX)))
721
		double tmp = Math.min(yZoom, xZoom);
721
				- tempRECT.x;
722
		if (tmp < MAX_TEXT_SIZE) {
722
		tempRECT.height = (int) (Math
723
			return (int) (tmp * height);
723
				.ceil(((r.y + r.height) * yZoom + fractionalY)))
724
		} else {
724
				- tempRECT.y;
725
			return (int) (height * tmp);
725
		return tempRECT;
726
		}
726
	}
727
	}
727
728
728
	private Rectangle zoomFillRect(int x, int y, int w, int h) {
729
	int zoomLineWidth(int w) {
729
		tempRECT.x = (int) (Math.floor((x * xZoom + fractionalX)));
730
		return w;
730
		tempRECT.y = (int) (Math.floor((y * yZoom + fractionalY)));
731
	}
731
		tempRECT.width = (int) (Math.floor(((x + w - 1) * xZoom + fractionalX)))
732
732
				- tempRECT.x + 1;
733
	private int[] zoomPointList(int[] points) {
733
		tempRECT.height = (int) (Math
734
		int[] scaled = null;
734
				.floor(((y + h - 1) * yZoom + fractionalY)))
735
735
				- tempRECT.y + 1;
736
		// Look in cache for a integer array with the same length as 'points'
736
		return tempRECT;
737
		for (int i = 0; i < intArrayCache.length; i++) {
737
	}
738
			if (intArrayCache[i].length == points.length) {
738
739
				scaled = intArrayCache[i];
739
	Font zoomFont(Font f) {
740
740
		if (f == null) {
741
				// Move this integer array up one notch in the array
741
			f = Display.getCurrent().getSystemFont();
742
				if (i != 0) {
742
		}
743
					int[] temp = intArrayCache[i - 1];
743
		FontData data = getCachedFontData(f);
744
					intArrayCache[i - 1] = scaled;
744
		int zoomedFontHeight = zoomFontHeight(data.getHeight());
745
					intArrayCache[i] = temp;
745
		allowText = zoomedFontHeight > 0;
746
				}
746
		fontKey.setValues(f, zoomedFontHeight);
747
			}
747
		return getCachedFont(fontKey);
748
		}
748
	}
749
749
750
		// If no match is found, take the one that is last and resize it.
750
	int zoomFontHeight(int height) {
751
		if (scaled == null) {
751
		double tmp = Math.min(yZoom, xZoom);
752
			intArrayCache[intArrayCache.length - 1] = new int[points.length];
752
		if (tmp < MAX_TEXT_SIZE) {
753
			scaled = intArrayCache[intArrayCache.length - 1];
753
			return (int) (tmp * height);
754
		}
754
		} else {
755
755
			return (int) (height * tmp);
756
		// Scale the points
756
		}
757
		for (int i = 0; (i + 1) < points.length; i += 2) {
757
	}
758
			scaled[i] = (int) (Math.floor((points[i] * xZoom + fractionalX)));
758
759
			scaled[i + 1] = (int) (Math.floor((points[i + 1] * yZoom + fractionalY)));
759
	int zoomLineWidth(int w) {
760
		}
760
		return w;
761
		return scaled;
761
	}
762
	}
762
763
763
	private int[] zoomPointList(int[] points) {
764
	private Rectangle zoomRect(int x, int y, int w, int h) {
764
		int[] scaled = null;
765
		tempRECT.x = (int) (Math.floor(x * xZoom + fractionalX));
765
766
		tempRECT.y = (int) (Math.floor(y * yZoom + fractionalY));
766
		// Look in cache for a integer array with the same length as 'points'
767
		tempRECT.width = (int) (Math.floor(((x + w) * xZoom + fractionalX))) - tempRECT.x;
767
		for (int i = 0; i < intArrayCache.length; i++) {
768
		tempRECT.height = (int) (Math.floor(((y + h) * yZoom + fractionalY))) - tempRECT.y;
768
			if (intArrayCache[i].length == points.length) {
769
		return tempRECT;
769
				scaled = intArrayCache[i];
770
	}
770
771
771
				// Move this integer array up one notch in the array
772
	private TextLayout zoomTextLayout(TextLayout layout) {
772
				if (i != 0) {
773
		TextLayout zoomed = new TextLayout(Display.getCurrent());
773
					int[] temp = intArrayCache[i - 1];
774
		zoomed.setText(layout.getText());
774
					intArrayCache[i - 1] = scaled;
775
775
					intArrayCache[i] = temp;
776
		int zoomWidth = -1;
776
				}
777
777
			}
778
		if (layout.getWidth() != -1) {
778
		}
779
			zoomWidth = ((int) (layout.getWidth() * xZoom));
779
780
		}
780
		// If no match is found, take the one that is last and resize it.
781
781
		if (scaled == null) {
782
		if (zoomWidth < -1 || zoomWidth == 0) {
782
			intArrayCache[intArrayCache.length - 1] = new int[points.length];
783
			return null;
783
			scaled = intArrayCache[intArrayCache.length - 1];
784
		}
784
		}
785
785
786
		zoomed.setFont(zoomFont(layout.getFont()));
786
		// Scale the points
787
		zoomed.setAlignment(layout.getAlignment());
787
		for (int i = 0; (i + 1) < points.length; i += 2) {
788
		zoomed.setAscent(layout.getAscent());
788
			scaled[i] = (int) (Math.floor((points[i] * xZoom + fractionalX)));
789
		zoomed.setDescent(layout.getDescent());
789
			scaled[i + 1] = (int) (Math
790
		zoomed.setOrientation(layout.getOrientation());
790
					.floor((points[i + 1] * yZoom + fractionalY)));
791
		zoomed.setSegments(layout.getSegments());
791
		}
792
		zoomed.setSpacing(layout.getSpacing());
792
		return scaled;
793
		zoomed.setTabs(layout.getTabs());
793
	}
794
794
795
		zoomed.setWidth(zoomWidth);
795
	private Rectangle zoomRect(int x, int y, int w, int h) {
796
		int length = layout.getText().length();
796
		tempRECT.x = (int) (Math.floor(x * xZoom + fractionalX));
797
		if (length > 0) {
797
		tempRECT.y = (int) (Math.floor(y * yZoom + fractionalY));
798
			int start = 0, offset = 1;
798
		tempRECT.width = (int) (Math.floor(((x + w) * xZoom + fractionalX)))
799
			TextStyle style = null, lastStyle = layout.getStyle(0);
799
				- tempRECT.x;
800
			for (; offset <= length; offset++) {
800
		tempRECT.height = (int) (Math.floor(((y + h) * yZoom + fractionalY)))
801
				if (offset != length && (style = layout.getStyle(offset)) == lastStyle) {
801
				- tempRECT.y;
802
					continue;
802
		return tempRECT;
803
				}
803
	}
804
				int end = offset - 1;
804
805
805
	private TextLayout zoomTextLayout(TextLayout layout) {
806
				if (lastStyle != null) {
806
		TextLayout zoomed = new TextLayout(Display.getCurrent());
807
					TextStyle zoomedStyle = new TextStyle(zoomFont(lastStyle.font), lastStyle.foreground, lastStyle.background);
807
		zoomed.setText(layout.getText());
808
					zoomed.setStyle(zoomedStyle, start, end);
808
809
				}
809
		int zoomWidth = -1;
810
				lastStyle = style;
810
811
				start = offset;
811
		if (layout.getWidth() != -1) {
812
			}
812
			zoomWidth = ((int) (layout.getWidth() * xZoom));
813
		}
813
		}
814
		return zoomed;
814
815
	}
815
		if (zoomWidth < -1 || zoomWidth == 0) {
816
816
			return null;
817
	private Point zoomTextPoint(int x, int y) {
817
		}
818
		if (localCache.font != localFont) {
818
819
			//Font is different, re-calculate its height
819
		zoomed.setFont(zoomFont(layout.getFont()));
820
			FontMetrics metric = FigureUtilities.getFontMetrics(localFont);
820
		zoomed.setAlignment(layout.getAlignment());
821
			localCache.height = metric.getHeight() - metric.getDescent();
821
		zoomed.setAscent(layout.getAscent());
822
			localCache.font = localFont;
822
		zoomed.setDescent(layout.getDescent());
823
		}
823
		zoomed.setOrientation(layout.getOrientation());
824
		if (targetCache.font != graphics.getFont()) {
824
		zoomed.setSegments(layout.getSegments());
825
			FontMetrics metric = graphics.getFontMetrics();
825
		zoomed.setSpacing(layout.getSpacing());
826
			targetCache.font = graphics.getFont();
826
		zoomed.setTabs(layout.getTabs());
827
			targetCache.height = metric.getHeight() - metric.getDescent();
827
828
		}
828
		zoomed.setWidth(zoomWidth);
829
		return new Point(((int) (Math.floor((x * xZoom) + fractionalX))), (int) (Math.floor((y + localCache.height - 1) * yZoom - targetCache.height + 1 + fractionalY)));
829
		int length = layout.getText().length();
830
	}
830
		if (length > 0) {
831
831
			int start = 0, offset = 1;
832
}
832
			TextStyle style = null, lastStyle = layout.getStyle(0);
833
			for (; offset <= length; offset++) {
834
				if (offset != length
835
						&& (style = layout.getStyle(offset)) == lastStyle) {
836
					continue;
837
				}
838
				int end = offset - 1;
839
840
				if (lastStyle != null) {
841
					TextStyle zoomedStyle = new TextStyle(
842
							zoomFont(lastStyle.font), lastStyle.foreground,
843
							lastStyle.background);
844
					zoomed.setStyle(zoomedStyle, start, end);
845
				}
846
				lastStyle = style;
847
				start = offset;
848
			}
849
		}
850
		return zoomed;
851
	}
852
853
	private Point zoomTextPoint(int x, int y) {
854
		if (localCache.font != localFont) {
855
			// Font is different, re-calculate its height
856
			FontMetrics metric = FigureUtilities.getFontMetrics(localFont);
857
			localCache.height = metric.getHeight() - metric.getDescent();
858
			localCache.font = localFont;
859
		}
860
		if (targetCache.font != graphics.getFont()) {
861
			FontMetrics metric = graphics.getFontMetrics();
862
			targetCache.font = graphics.getFont();
863
			targetCache.height = metric.getHeight() - metric.getDescent();
864
		}
865
		return new Point(((int) (Math.floor((x * xZoom) + fractionalX))),
866
				(int) (Math.floor((y + localCache.height - 1) * yZoom
867
						- targetCache.height + 1 + fractionalY)));
868
	}
869
870
}
(-)src/org/eclipse/zest/core/widgets/internal/ZestRootLayer.java (-189 / +212 lines)
Lines 1-189 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others. All rights reserved.
2
 * Copyright (c) 2005-2010 IBM Corporation and others. All rights reserved.
3
 * This program and the accompanying materials are made available under the
3
 * This program and the accompanying materials are made available under the
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
4
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
5
 * and is available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
6
 * 
7
 * Contributors: IBM Corporation - initial API and implementation Chisel Group,
7
 * Contributors: IBM Corporation - initial API and implementation Chisel Group,
8
 * University of Victoria - Adapted for XY Scaled Graphics
8
 * University of Victoria - Adapted for XY Scaled Graphics
9
 ******************************************************************************/
9
 ******************************************************************************/
10
package org.eclipse.zest.core.widgets.internal;
10
package org.eclipse.zest.core.widgets.internal;
11
11
12
import org.eclipse.draw2d.FreeformLayer;
12
import java.util.ArrayList;
13
import org.eclipse.draw2d.IFigure;
13
import java.util.HashSet;
14
14
import java.util.Iterator;
15
/**
15
16
 * The root figure for Zest.  The figure is broken up into four segments, 
16
import org.eclipse.draw2d.FreeformLayer;
17
 * 1. The Connections
17
import org.eclipse.draw2d.IFigure;
18
 * 2. The Nodes
18
19
 * 3. The Highlighted Connections
19
/**
20
 * 4. The Highlighted Nodes
20
 * The root figure for Zest. The figure is broken up into following segments:
21
 * 
21
 * <ol>
22
 * @author Ian Bull
22
 * <li>The Connections</li>
23
 * 
23
 * <li>The Subgraphs</li>
24
 */
24
 * <li>The Nodes</li>
25
public class ZestRootLayer extends FreeformLayer {
25
 * <li>The Highlighted Connections</li>
26
26
 * <li>The Highlighted Nodes</li>
27
	public static final boolean EDGES_ON_TOP = false;
27
 * </ol>
28
	private int numberOfNodes = 0;
28
 * 
29
	private int numberOfConnections = 0;
29
 */
30
	private int numberOfHighlightedNodes = 0;
30
public class ZestRootLayer extends FreeformLayer {
31
	private int numberOfHighlightedConnections = 0;
31
32
32
	public static final int CONNECTIONS_LAYER = 0;
33
	/**
33
34
	 * Adds a node to the ZestRootLayer
34
	public static final int SUBGRAPHS_LAYER = 1;
35
	 * @param nodeFigure The figure representing the node
35
36
	 */
36
	public static final int NODES_LAYER = 2;
37
	public void addNode(IFigure nodeFigure) {
37
38
		int nodePosition = getNodePosition();
38
	public static final int CONNECTIONS_HIGHLIGHTED_LAYER = 3;
39
		numberOfNodes++;
39
40
		add(nodeFigure, nodePosition);
40
	public static final int NODES_HIGHLIGHTED_LAYER = 4;
41
	}
41
42
42
	public static final int TOP_LAYER = 5;
43
	/**
43
44
	 * Removes a node from the layer
44
	public static final int NUMBER_OF_LAYERS = 6;
45
	 * @param nodeFigure
45
46
	 */
46
	private final int[] itemsInLayer = new int[NUMBER_OF_LAYERS];
47
	public void removeNode(IFigure nodeFigure) {
47
48
		if (!this.getChildren().contains(nodeFigure)) {
48
	/**
49
			throw new RuntimeException("Node not contained on the ZestRootLayer");
49
	 * Set of all figures that are decorations for other figures. A decoration
50
		}
50
	 * figure is always put one position (or more if there's more than one
51
		int nodePosition = this.getChildren().indexOf(nodeFigure);
51
	 * decoration for the same figure) after the decorated figure in children
52
		if (nodePosition > getHighlightNodeStartPosition()) {
52
	 * list.
53
			// The node is in the highlight node area
53
	 */
54
			numberOfHighlightedNodes--;
54
	private HashSet decoratingFigures = new HashSet();
55
		} else {
55
56
			// The node is in the node area
56
	/**
57
			numberOfNodes--;
57
	 * If true, it indicates that a figure is added using a proper method and
58
		}
58
	 * its layer is known. Otherwise, Figure#add() method was used and layer
59
		this.remove(nodeFigure);
59
	 * must be guessed
60
	}
60
	 */
61
61
	private boolean isLayerKnown = false;
62
	public void removeConnection(IFigure connectionFigure) {
62
63
		int connectionPosition = this.getChildren().indexOf(connectionFigure);
63
	/**
64
		if (connectionPosition > getHighlightConnectionStartPosition()) {
64
	 * Adds a node to the ZestRootLayer
65
			// The connection is in the highlight connection area
65
	 * 
66
			numberOfHighlightedConnections--;
66
	 * @param nodeFigure
67
		} else {
67
	 *            The figure representing the node
68
			// The connection is in the connection area
68
	 */
69
			numberOfConnections--;
69
	public void addNode(IFigure nodeFigure) {
70
		}
70
		addFigure(nodeFigure, NODES_LAYER);
71
		this.remove(connectionFigure);
71
	}
72
	}
72
73
73
	public void addConnection(IFigure connectionFigure) {
74
	public void addConnection(IFigure connectionFigure) {
74
		addFigure(connectionFigure, CONNECTIONS_LAYER);
75
		int connectionPosition = getConnectionPosition();
75
	}
76
		numberOfConnections++;
76
77
		add(connectionFigure, connectionPosition);
77
	public void addSubgraph(IFigure subgraphFigrue) {
78
	}
78
		addFigure(subgraphFigrue, SUBGRAPHS_LAYER);
79
79
	}
80
	public void highlightNode(IFigure nodeFigure) {
80
81
		this.numberOfNodes--;
81
	public void highlightNode(IFigure nodeFigure) {
82
		int highlightNodePosition = getHighlightNodePosition();
82
		changeFigureLayer(nodeFigure, NODES_HIGHLIGHTED_LAYER);
83
		this.numberOfHighlightedNodes++;
83
	}
84
		this.getChildren().remove(nodeFigure);
84
85
		this.getChildren().add(highlightNodePosition, nodeFigure);
85
	public void highlightConnection(IFigure connectionFigure) {
86
		this.invalidate();
86
		changeFigureLayer(connectionFigure, CONNECTIONS_HIGHLIGHTED_LAYER);
87
		this.repaint();
87
	}
88
	}
88
89
89
	public void unHighlightNode(IFigure nodeFigure) {
90
	public void highlightConnection(IFigure connectionFigure) {
90
		changeFigureLayer(nodeFigure, NODES_LAYER);
91
		this.numberOfConnections--;
91
	}
92
		int highlightConnectionPosition = getHighlightConnectionPosition();
92
93
		this.numberOfHighlightedConnections++;
93
	public void unHighlightConnection(IFigure connectionFigure) {
94
		this.getChildren().remove(connectionFigure);
94
		changeFigureLayer(connectionFigure, CONNECTIONS_LAYER);
95
		this.getChildren().add(highlightConnectionPosition, connectionFigure);
95
	}
96
		this.invalidate();
96
97
		this.repaint();
97
	private void changeFigureLayer(IFigure figure, int newLayer) {
98
	}
98
		ArrayList decorations = getDecorations(figure);
99
99
		remove(figure);
100
	public void unHighlightNode(IFigure nodeFigure) {
100
101
		int nodePosition = this.getChildren().indexOf(nodeFigure);
101
		addFigure(figure, newLayer);
102
		if (nodePosition > getHighlightNodePosition()) {
102
		for (Iterator iterator = decorations.iterator(); iterator.hasNext();) {
103
			//throw new RuntimeException("Node: " + nodeFigure + " not currently Highlighted");
103
			addDecoration(figure, (IFigure) iterator.next());
104
			return;
104
		}
105
		}
105
106
		this.numberOfHighlightedNodes--;
106
		this.invalidate();
107
		nodePosition = getNodePosition();
107
		this.repaint();
108
		this.numberOfNodes++;
108
	}
109
		this.getChildren().remove(nodeFigure);
109
110
		this.getChildren().add(nodePosition, nodeFigure);
110
	private ArrayList getDecorations(IFigure figure) {
111
		this.invalidate();
111
		ArrayList result = new ArrayList();
112
		this.repaint();
112
		int index = getChildren().indexOf(figure);
113
	}
113
		if (index == -1) {
114
114
			return result;
115
	public void unHighlightConnection(IFigure connectionFigure) {
115
		}
116
		int connectionPosition = this.getChildren().indexOf(connectionFigure);
116
		for (index++; index < getChildren().size(); index++) {
117
		if (connectionPosition > getHighlightConnectionPosition()) {
117
			Object nextFigure = getChildren().get(index);
118
			//throw new RuntimeException("Connection: " + connectionFigure + " not currently Highlighted");
118
			if (decoratingFigures.contains(nextFigure)) {
119
			return;
119
				result.add(nextFigure);
120
		}
120
			} else {
121
		this.numberOfHighlightedConnections--;
121
				break;
122
		this.numberOfConnections++;
122
			}
123
		connectionPosition = getConnectionPosition();
123
		}
124
		this.getChildren().remove(connectionFigure);
124
		return result;
125
		if (connectionPosition > this.getChildren().size()) {
125
	}
126
			this.getChildren().add(connectionFigure);
126
127
		} else {
127
	/**
128
			this.getChildren().add(connectionPosition, connectionFigure);
128
	 * 
129
		}
129
	 * @param layer
130
		this.invalidate();
130
	 * @return position after the last element in given layer
131
		this.repaint();
131
	 */
132
	}
132
	private int getPosition(int layer) {
133
133
		int result = 0;
134
	/*
134
		for (int i = 0; i <= layer; i++) {
135
	 * Node position is at the end of the list of nodes
135
			result += itemsInLayer[i];
136
	 */
136
		}
137
	private int getNodePosition() {
137
		return result;
138
		if (EDGES_ON_TOP) {
138
	}
139
			return numberOfNodes;
139
140
		}
140
	/**
141
		return numberOfConnections + numberOfNodes;
141
	 * 
142
	}
142
	 * @param position
143
143
	 * @return number of layer containing element at given position
144
	/*
144
	 */
145
	 * Connection position is at the end of the list of connections
145
	private int getLayer(int position) {
146
	 */
146
		int layer = 0;
147
	private int getConnectionPosition() {
147
		int positionInLayer = itemsInLayer[0];
148
		if (EDGES_ON_TOP) {
148
		while (layer < NUMBER_OF_LAYERS - 1 && positionInLayer <= position) {
149
			return 0 + numberOfConnections + numberOfNodes;
149
			layer++;
150
		}
150
			positionInLayer += itemsInLayer[layer];
151
		return 0 + numberOfConnections;
151
		}
152
	}
152
		return layer;
153
153
	}
154
	/*
154
155
	 * Highlight node position is at the end of the list of highlighted nodes
155
	public void addFigure(IFigure figure, int layer) {
156
	 */
156
		int position = getPosition(layer);
157
	private int getHighlightNodePosition() {
157
		itemsInLayer[layer]++;
158
		if (EDGES_ON_TOP) {
158
		isLayerKnown = true;
159
			return numberOfConnections + numberOfNodes + numberOfHighlightedNodes;
159
		add(figure, position);
160
		}
160
	}
161
		return numberOfConnections + numberOfHighlightedConnections + numberOfNodes + numberOfHighlightedNodes;
161
162
	}
162
	public void add(IFigure child, Object constraint, int index) {
163
163
		super.add(child, constraint, index);
164
	/*
164
		if (!isLayerKnown) {
165
	 * Highlighted connection position is at the end of the list of highlighted connections
165
			int layer = 0, positionInLayer = itemsInLayer[0];
166
	 */
166
			while (positionInLayer < index) {
167
	private int getHighlightConnectionPosition() {
167
				layer++;
168
		if (EDGES_ON_TOP) {
168
				positionInLayer += itemsInLayer[layer];
169
			return numberOfNodes + +numberOfConnections + numberOfHighlightedNodes + numberOfHighlightedConnections;
169
			}
170
		}
170
			if (index == -1) {
171
		return numberOfConnections + numberOfNodes + numberOfHighlightedConnections;
171
				layer = NUMBER_OF_LAYERS - 1;
172
	}
172
			}
173
173
			itemsInLayer[layer]++;
174
	private int getHighlightConnectionStartPosition() {
174
		}
175
		if (EDGES_ON_TOP) {
175
		isLayerKnown = false;
176
			return numberOfConnections + numberOfNodes + numberOfHighlightedNodes;
176
	}
177
177
178
		}
178
	public void remove(IFigure child) {
179
		return numberOfConnections + numberOfNodes;
179
		int position = this.getChildren().indexOf(child);
180
	}
180
		if (position == -1) {
181
181
			throw new RuntimeException(
182
	private int getHighlightNodeStartPosition() {
182
					"Can't remove a figure that is not on this ZestRootLayer");
183
		if (EDGES_ON_TOP) {
183
		}
184
			return numberOfNodes + numberOfConnections;
184
		itemsInLayer[getLayer(position)]--;
185
		}
185
		if (decoratingFigures.contains(child)) {
186
		return numberOfConnections + numberOfHighlightedConnections + numberOfNodes;
186
			decoratingFigures.remove(child);
187
	}
187
			super.remove(child);
188
188
		} else {
189
}
189
			ArrayList decorations = getDecorations(child);
190
			super.remove(child);
191
			for (Iterator iterator = decorations.iterator(); iterator.hasNext();) {
192
				remove((IFigure) iterator.next());
193
			}
194
		}
195
	}
196
197
	public void addDecoration(IFigure decorated, IFigure decorating) {
198
		int position = this.getChildren().indexOf(decorated);
199
		if (position == -1) {
200
			throw new RuntimeException(
201
					"Can't add decoration for a figuer that is not on this ZestRootLayer");
202
		}
203
		itemsInLayer[getLayer(position)]++;
204
		isLayerKnown = true;
205
		do {
206
			position++;
207
		} while (position < getChildren().size()
208
				&& decoratingFigures.contains(getChildren().get(position)));
209
		decoratingFigures.add(decorating);
210
		add(decorating, position);
211
	}
212
}
(-)META-INF/MANIFEST.MF (-5 / +1 lines)
Lines 7-18 Link Here
7
Bundle-Localization: plugin
7
Bundle-Localization: plugin
8
Export-Package: org.eclipse.zest.layouts,
8
Export-Package: org.eclipse.zest.layouts,
9
 org.eclipse.zest.layouts.algorithms,
9
 org.eclipse.zest.layouts.algorithms,
10
 org.eclipse.zest.layouts.algorithms.internal,
11
 org.eclipse.zest.layouts.constraints,
12
 org.eclipse.zest.layouts.dataStructures,
10
 org.eclipse.zest.layouts.dataStructures,
13
 org.eclipse.zest.layouts.exampleStructures,
11
 org.eclipse.zest.layouts.interfaces
14
 org.eclipse.zest.layouts.exampleUses,
15
 org.eclipse.zest.layouts.progress
16
Require-Bundle: org.eclipse.swt;visibility:=reexport,
12
Require-Bundle: org.eclipse.swt;visibility:=reexport,
17
 org.eclipse.core.runtime,
13
 org.eclipse.core.runtime,
18
 org.eclipse.jface,
14
 org.eclipse.jface,
(-)src/org/eclipse/zest/layouts/Filter.java (-30 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
/**
14
 * A filter is used to filter objects.  Once implemented, interested
15
 * parties can ask this filter whether or not a specific object
16
 * is filtered.
17
 * 
18
 * For example, in a visualization tool, only unfiltered objects should
19
 * be displayed.  Before displaying an object, the display can ask
20
 * this filter if the object is filtered.
21
 * 
22
 * @author Casey Best
23
 */
24
public interface Filter {
25
	
26
	/**
27
	 * Returns true if the object is filtered, or false if it's not filtered.
28
	 */
29
	public boolean isObjectFiltered (LayoutItem object);
30
}
(-)src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java (-23 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
/**
14
 * 
15
 * @author Ian Bull
16
 *
17
 */
18
public class InvalidLayoutConfiguration extends Exception {
19
	
20
	static final long serialVersionUID = 0;
21
22
23
}
(-)src/org/eclipse/zest/layouts/LayoutAlgorithm.java (-111 / +50 lines)
Lines 1-111 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts;
11
package org.eclipse.zest.layouts;
12
12
13
import java.util.Comparator;
13
import org.eclipse.zest.layouts.interfaces.LayoutContext;
14
import java.util.List;
14
15
15
/**
16
import org.eclipse.zest.layouts.progress.ProgressListener;
16
 * An interface for all layout algorithms.
17
17
 * 
18
/**
18
 * 
19
 * A simple interface used by all layouts.
19
 */
20
 * 
20
public interface LayoutAlgorithm {
21
 * Each layout Algorithm must implement the applyLayoutInternal method which actually compute the layout
21
22
 * 
22
	/**
23
 * @author Casey Best
23
	 * Sets the layout context for this algorithm. The receiver will unregister
24
 * @author Ian Bull
24
	 * from its previous layout context and register to the new one
25
 */
25
	 * (registration means for example adding listeners). After a call to this
26
public interface LayoutAlgorithm {
26
	 * method, the receiving algorithm can compute and cache internal data
27
27
	 * related to given context and perform an initial layout.
28
	/**
28
	 * 
29
	 * Apply the layout to the given entities.  The entities will be moved and resized based
29
	 * @param context
30
	 * on the algorithm.
30
	 *            a new layout context or null if this algorithm should not
31
	 * 
31
	 *            perform any layout
32
	 * @param entitiesToLayout Apply the algorithm to these entities
32
	 */
33
	 * @param relationshipsToConsider Only consider these relationships when applying the algorithm.
33
	public void setLayoutContext(LayoutContext context);
34
	 * @param x The left side of the bounds in which the layout can place the entities.
34
35
	 * @param y The top side of the bounds in which the layout can place the entities.
35
	/**
36
	 * @param width The width of the bounds in which the layout can place the entities.
36
	 * Makes this algorithm perform layout computation and apply it to its
37
	 * @param height The height of the bounds in which the layout can place the entities.
37
	 * context.
38
	 * @param asynchronous Should the algorithm run Asynchronously
38
	 * 
39
	 */
39
	 * @param clean
40
	public void applyLayout(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height, boolean asynchronous, boolean continuous) throws InvalidLayoutConfiguration;
40
	 *            if true the receiver should assume that the layout context has
41
41
	 *            changed significantly and recompute the whole layout even if
42
	/**
42
	 *            it keeps track of changes with listeners. False can be used
43
	 * Returns whether or not the algorithm is currenly running
43
	 *            after dynamic layout in a context is turned back on so that
44
	 * @return True if a layout algorithm is currenly running, false otherwise
44
	 *            layout algorithm working in background can apply accumulated
45
	 */
45
	 *            changes. Static layout algorithm can ignore this call entirely
46
	public boolean isRunning();
46
	 *            if clean is false.
47
47
	 */
48
	/**
48
	public void applyLayout(boolean clean);
49
	 * Determines the order in which the objects should be displayed.
49
50
	 * Note: Some algorithms force a specific order, in which case
50
}
51
	 * this comparator will be ignored.
52
	 */
53
	public void setComparator(Comparator comparator);
54
55
	/**
56
	 * Filters the entities and relationships to apply the layout on
57
	 */
58
	public void setFilter(Filter filter);
59
60
	/**
61
	 * Set the width to height ratio you want the entities to use
62
	 * Note: Each layout is responsible for ensuring this ratio is used.
63
	 * Note: By default the layout will use a ratio of 1.0 for each entity.
64
	 */
65
	public void setEntityAspectRatio(double ratio);
66
67
	/**
68
	 * Returns the width to height ratio this layout will use to set the size of the entities.
69
	 * Note: By default the layout will use a ratio of 1.0 for each entity.
70
	 */
71
	public double getEntityAspectRatio();
72
73
	/**
74
	 * A layout algorithm could take an uncomfortable amout of time to complete.  To relieve some of
75
	 * the mystery, the layout algorithm will notify each ProgressListener of its progress. 
76
	 */
77
	public void addProgressListener(ProgressListener listener);
78
79
	/**
80
	 * Removes the given progress listener, preventing it from receiving any more updates.
81
	 */
82
	public void removeProgressListener(ProgressListener listener);
83
84
	/**
85
	 * Makes a request to this layout algorithm to stop running.
86
	 */
87
	public void stop();
88
89
	/**
90
	 * Sets the style for this layout algorithm.  This will overwrite any other style set.
91
	 * @param style
92
	 */
93
	public void setStyle(int style);
94
95
	/**
96
	 * 
97
	 * @return
98
	 */
99
	public int getStyle();
100
101
	public void addEntity(LayoutEntity entity);
102
103
	public void addRelationship(LayoutRelationship relationship);
104
105
	public void removeEntity(LayoutEntity entity);
106
107
	public void removeRelationship(LayoutRelationship relationship);
108
109
	public void removeRelationships(List relationships);
110
111
}
(-)src/org/eclipse/zest/layouts/LayoutBendPoint.java (-22 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
/**
14
 * Specifies a single bend point in a graph relationship.
15
 * @author Ian Bull
16
 * @author Chris Bennett
17
 */
18
public interface LayoutBendPoint {
19
	public double getX();
20
	public double getY();
21
	public boolean getIsControlPoint();
22
}
(-)src/org/eclipse/zest/layouts/LayoutEntity.java (-44 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
14
15
/**
16
 * This represents a single entity, providing the layout algorithms with 
17
 * a common interface to run on.
18
 * 
19
 * @author Casey Best
20
 * @author Ian Bull
21
 * @author Chris Bennett
22
 */
23
public interface LayoutEntity extends Comparable, LayoutItem {
24
	
25
	public final static String ATTR_PREFERRED_WIDTH = "tree-preferred-width";
26
    public final static String ATTR_PREFERRED_HEIGHT = "tree-preferred-height";
27
	
28
	public void setLocationInLayout (double x, double y);
29
	public void setSizeInLayout (double width, double height);
30
	
31
	public double getXInLayout();
32
	public double getYInLayout();
33
	public double getWidthInLayout();
34
	public double getHeightInLayout();
35
	
36
	public Object getLayoutInformation();
37
	public void setLayoutInformation(Object internalEntity);
38
	
39
	/**
40
	 * Classes should update the specified layout constraint if recognized
41
	 * @return
42
	 */
43
	public void populateLayoutConstraint(LayoutConstraint constraint);
44
}
(-)src/org/eclipse/zest/layouts/LayoutGraph.java (-52 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
import java.util.List;
14
15
/**
16
 * The LayoutGraph interface defines the methods used to add nodes and edges (relationships).
17
 * @author Chris
18
 */
19
public interface LayoutGraph {
20
21
	/**
22
	 * Adds a node to this graph.
23
	 * @param node 	The new node.
24
	 * @return LayoutEntity	The created node
25
	 */
26
	public void addEntity(LayoutEntity node);
27
	
28
	/**
29
	 * Adds the given relationship.
30
	 * @param relationship
31
	 */
32
	public void addRelationship(LayoutRelationship relationship);
33
	
34
	/**
35
	 * Returns a list of LayoutEntity objects that represent the objects added to this graph using addNode.
36
	 * @return List	 A List of LayoutEntity objects.  
37
	 */
38
	public List getEntities();
39
40
	/**
41
	 * Returns a list of LayoutRelationship objects that represent the objects added to this graph using addRelationship.
42
	 * @return List	 A List of LayoutRelationship objects.
43
	 */
44
	public List getRelationships();
45
	
46
	/**
47
	 * Determines if the graph is bidirectional.   
48
	 * @return boolean	If the graph is bidirectional.
49
	 */
50
	public boolean isBidirectional();
51
	
52
}
(-)src/org/eclipse/zest/layouts/LayoutItem.java (-24 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
/**
14
 * Super interface for both Layout Entities and Layout Relationships
15
 *  
16
 * @author Ian Bull
17
 *
18
 */
19
public interface LayoutItem {
20
21
	public void setGraphData(Object o);
22
	public Object getGraphData();
23
24
}
(-)src/org/eclipse/zest/layouts/LayoutIterationEvent.java (-49 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
import java.util.List;
14
15
/**
16
 * When a layout completes an iteration, it throws this event
17
 * to allow the application to update.  For example, at the
18
 * end of an iteration is can be assumed the layout has placed
19
 * each entity into a new location.  This event allows the application
20
 * to update the GUI to represent the new locations
21
 * 
22
 * @author Casey Best and Rob Lintern
23
 */
24
public class LayoutIterationEvent {
25
    private List relationshipsToLayout, entitiesToLayout;
26
    private int iterationCompleted;
27
    
28
    /**
29
     * Return the relationships used in this layout.
30
     */
31
    public List getRelationshipsToLayout() {
32
        return relationshipsToLayout;
33
    }
34
35
    /**
36
     * Return the entities used in this layout. 
37
     */
38
    public List getEntitiesToLayout() {
39
        return entitiesToLayout;
40
    }
41
42
    /**
43
     * Return the iteration of the layout algorithm that was
44
     * just completed. 
45
     */
46
    public int getIterationCompleted() {
47
        return iterationCompleted;
48
    }
49
}
(-)src/org/eclipse/zest/layouts/LayoutRelationship.java (-82 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
14
15
16
17
/**
18
 * This represents a single relationship, providing the layout algorithms with 
19
 * a common interface to run on.
20
 * 
21
 * @author Casey Best
22
 * @author Chris Callendar
23
 */
24
public interface LayoutRelationship extends LayoutItem {
25
	
26
	
27
	/**
28
	 * Gets the sourceEntity of this SimpleRelation whether the relation is
29
	 * exchangeable or not.
30
	 * @return The sourceEntity.
31
	 */
32
	public LayoutEntity getSourceInLayout();
33
34
	/**
35
	 * Gets the destinationEntity of this SimpleRelation whether the relation is
36
	 * exchangeable or not.
37
	 * @return The destinationEntity of this SimpleRelation.
38
	 */
39
	public LayoutEntity getDestinationInLayout();
40
41
42
	/**
43
	 * Sets the internal relationship object.
44
	 * @param layoutInformation
45
	 */
46
	public void setLayoutInformation(Object layoutInformation);
47
	
48
	/**
49
	 * Returns the internal relationship object.
50
	 * @return Object
51
	 */
52
	public Object getLayoutInformation();
53
	
54
	/**
55
	 * Specify a set of bend points. The layout algorithm using this will pass
56
	 * in an empty array of bendPoints, or not even call this method,
57
	 * if there are no bend points associated with this edge.
58
	 * 
59
	 * If you are updating an existing application you can just implement this 
60
	 * method to do nothing.
61
	 * 
62
	 * @param bendPoints A list of bend points. All bendpoint locations are expressed 
63
	 * as percentages of the bounds (0,0 to 1,1).The first bendpoint in the list must be the 
64
	 * source point of this relationship and the last bendpoint the destination point 
65
	 * for this relationship. This allows the correct positioning of bendpoints 
66
	 * relative to the source and destination points when drawing the graph.
67
	 */
68
	public void setBendPoints(LayoutBendPoint[] bendPoints);
69
70
	/**
71
	 * Clear bend points and related bounds
72
	 * If you are updating an existing application you can just implement this 
73
	 * method to do nothing.
74
	 */
75
	public void clearBendPoints();
76
77
	/**
78
	 * Classes should update the specirfied layout constraint if recognized
79
	 * @return
80
	 */
81
	public void populateLayoutConstraint(LayoutConstraint constraint);
82
}
(-)src/org/eclipse/zest/layouts/LayoutStyles.java (-33 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
/**
14
 * @author Ian Bull
15
 */
16
public interface LayoutStyles {
17
18
	/** Default layout style constant. */
19
	public final static int NONE           = 0x00;
20
	
21
	/** 
22
	 * Layout constant indicating that the layout algorithm 
23
	 * should NOT resize any of the nodes.
24
	 */
25
	public final static int NO_LAYOUT_NODE_RESIZING = 0x01;
26
	
27
	/**
28
	 * Some layouts may prefer to expand their bounds beyond those of the requested bounds. This
29
	 * flag asks the layout not to do so.
30
	 */
31
	public static final int ENFORCE_BOUNDS	= 0X02;
32
	
33
}
(-)src/org/eclipse/zest/layouts/NestedLayoutEntity.java (-32 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
import java.util.List;
14
15
16
/**
17
 * Extends LayoutEntity to provide methods for dealing with nested entities.
18
 * 
19
 * @author Chris Callendar
20
 */
21
public interface NestedLayoutEntity extends LayoutEntity {
22
23
	/** Returns the parent entity. */
24
	NestedLayoutEntity getParent();
25
	
26
	/** Returns the list of children. */
27
	List getChildren();
28
	
29
	/** Returns true if this entity has children. */
30
	boolean hasChildren();
31
	
32
}
(-)src/org/eclipse/zest/layouts/Stoppable.java (-27 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts;
12
13
import org.eclipse.zest.layouts.progress.ProgressListener;
14
15
/**
16
 * @author Ian Bull
17
 */
18
public interface Stoppable {
19
20
	/**
21
	 * This ends the runnable
22
	 */
23
	public void stop();
24
25
	public void addProgressListener(ProgressListener listener);
26
27
}
(-)src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java (-1035 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
12
package org.eclipse.zest.layouts.algorithms;
13
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.Calendar;
17
import java.util.Collection;
18
import java.util.Comparator;
19
import java.util.Iterator;
20
import java.util.List;
21
22
import org.eclipse.zest.layouts.Filter;
23
import org.eclipse.zest.layouts.InvalidLayoutConfiguration;
24
import org.eclipse.zest.layouts.LayoutAlgorithm;
25
import org.eclipse.zest.layouts.LayoutEntity;
26
import org.eclipse.zest.layouts.LayoutItem;
27
import org.eclipse.zest.layouts.LayoutRelationship;
28
import org.eclipse.zest.layouts.LayoutStyles;
29
import org.eclipse.zest.layouts.Stoppable;
30
import org.eclipse.zest.layouts.constraints.BasicEntityConstraint;
31
import org.eclipse.zest.layouts.dataStructures.BendPoint;
32
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
33
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
34
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
35
import org.eclipse.zest.layouts.dataStructures.InternalNode;
36
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
37
import org.eclipse.zest.layouts.progress.ProgressEvent;
38
import org.eclipse.zest.layouts.progress.ProgressListener;
39
40
/**
41
 * Handles common elements in all layout algorithms
42
 * [irbull] Refactored into a template pattern.  ApplyLayout now delegates the
43
 * task to ApplyLayoutInternal
44
 * 
45
 * [irbull] Included asynchronous layouts
46
 * 
47
 * @version 1.0
48
 * @author Casey Best
49
 * @author Ian Bull
50
 * @author Chris Callendar
51
 * @author Rob Lintern
52
 * @author Chris Bennett
53
 */
54
public abstract class AbstractLayoutAlgorithm implements LayoutAlgorithm, Stoppable {
55
56
	public void removeRelationships(Collection collection) {
57
58
	}
59
60
	public final static int MIN_ENTITY_SIZE = 5;
61
	private final static int MIN_TIME_DELAY_BETWEEN_PROGRESS_EVENTS = 1;
62
63
	private Thread creationThread = null;
64
	protected Comparator comparator;
65
	protected Filter filter;
66
	private List progressListeners;
67
	private Calendar lastProgressEventFired;
68
	private double widthToHeightRatio;
69
70
	class InternalComparator implements Comparator {
71
		Comparator externalComparator = null;
72
73
		public InternalComparator(Comparator externalComparator) {
74
			this.externalComparator = externalComparator;
75
		}
76
77
		public int compare(Object o1, Object o2) {
78
			InternalNode internalNode1 = (InternalNode) o1;
79
			InternalNode internalNode2 = (InternalNode) o2;
80
81
			return this.externalComparator.compare(internalNode1.getLayoutEntity(), internalNode2.getLayoutEntity());
82
		}
83
84
	}
85
86
	/*
87
	 * Internal Nodes.   
88
	 */
89
	private InternalNode[] internalNodes;
90
	private InternalRelationship[] internalRelationships;
91
	private double internalX;
92
	private double internalY;
93
	private double internalWidth;
94
	private double internalHeight;
95
	protected boolean internalContinuous;
96
	protected boolean internalAsynchronous;
97
98
	/*
99
	 * A queue of entities and relationships to add or remove.  Each layout 
100
	 * algorithm should check these and update their internal lists.
101
	 */
102
103
	/** A list of LayoutEntity objects to be removed from the layout. */
104
	private List entitiesToRemove;
105
	/** A list of LayoutRelationship objects to be removed. */
106
	private List relationshipsToRemove;
107
	/** A list of LayoutEntity objects to be added to the layout. */
108
	private List entitiesToAdd;
109
	/** A list of LayoutRelationship objects to be added. */
110
	private List relationshipsToAdd;
111
112
	//protected boolean cancelled = false;
113
114
	protected boolean layoutStopped = true;
115
116
	protected int layout_styles = 0;
117
118
	// Child classes can set to false to retain node shapes and sizes
119
	protected boolean resizeEntitiesAfterLayout = true;
120
121
	/**
122
	 * Initializes the abstract layout algorithm.
123
	 * @see LayoutStyles
124
	 */
125
	public AbstractLayoutAlgorithm(int styles) {
126
		this.creationThread = Thread.currentThread();
127
		this.progressListeners = new ArrayList();
128
		this.lastProgressEventFired = Calendar.getInstance();
129
		this.widthToHeightRatio = 1.0;
130
131
		this.entitiesToRemove = new ArrayList();
132
		this.relationshipsToRemove = new ArrayList();
133
		this.entitiesToAdd = new ArrayList();
134
		this.relationshipsToAdd = new ArrayList();
135
		this.layout_styles = styles;
136
	}
137
138
	/**
139
	 * Queues up the given entity (if it isn't in the list) to be added to the algorithm.
140
	 * @param entity
141
	 */
142
	public void addEntity(LayoutEntity entity) {
143
		if ((entity != null) && !entitiesToAdd.contains(entity)) {
144
			entitiesToAdd.add(entity);
145
		}
146
	}
147
148
	/**
149
	 * Queues up the given relationshp (if it isn't in the list) to be added to the algorithm.
150
	 * @param relationship
151
	 */
152
	public void addRelationship(LayoutRelationship relationship) {
153
		if ((relationship != null) && !relationshipsToAdd.contains(relationship)) {
154
			relationshipsToAdd.add(relationship);
155
		}
156
	}
157
158
	/**
159
	 * Queues up the given entity to be removed from the algorithm next time it runs.
160
	 * @param entity The entity to remove
161
	 */
162
	public void removeEntity(LayoutEntity entity) {
163
		if ((entity != null) && !entitiesToRemove.contains(entity)) {
164
			entitiesToRemove.add(entity);
165
		}
166
	}
167
168
	/**
169
	 * Queues up the given relationship to be removed from the algorithm next time it runs.
170
	 * @param relationship	The relationship to remove.
171
	 */
172
	public void removeRelationship(LayoutRelationship relationship) {
173
		if ((relationship != null) && !relationshipsToRemove.contains(relationship)) {
174
			relationshipsToRemove.add(relationship);
175
		}
176
	}
177
178
	/**
179
	 * Queues up all the relationships in the list to be removed.
180
	 * @param relationships
181
	 */
182
	public void removeRelationships(List relationships) {
183
		// note we don't check if the relationshipsToRemove contains
184
		// any of the objects in relationships.
185
		relationshipsToRemove.addAll(relationships);
186
	}
187
188
	/**
189
	 * Sets the current layout style.  This overwrites all other layout styles.
190
	 * Use getStyle to get the current style.
191
	 * @param style
192
	 */
193
	public void setStyle(int style) {
194
		this.layout_styles = style;
195
	}
196
197
	/**
198
	 * Gets the current layout style
199
	 * @return
200
	 */
201
	public int getStyle() {
202
		return this.layout_styles;
203
	}
204
205
	public abstract void setLayoutArea(double x, double y, double width, double height);
206
207
	/**
208
	 * Determines if the configuration is valid for this layout
209
	 * @param asynchronous
210
	 * @param continuous
211
	 */
212
	protected abstract boolean isValidConfiguration(boolean asynchronous, boolean continuous);
213
214
	/**
215
	 * Apply the layout to the given entities.  The entities will be moved and resized based
216
	 * on the algorithm.
217
	 * 
218
	 * @param entitiesToLayout Apply the algorithm to these entities
219
	 * @param relationshipsToConsider Only consider these relationships when applying the algorithm.
220
	 * @param x The left side of the bounds in which the layout can place the entities.
221
	 * @param y The top side of the bounds in which the layout can place the entities.
222
	 * @param width The width of the bounds in which the layout can place the entities.
223
	 * @param height The height of the bounds in which the layout can place the entities.
224
	 */
225
	abstract protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight);
226
227
	/**
228
	 * Updates the given array of entities checking if any need to be removed or added.
229
	 * @param entities the current entities
230
	 * @return the updated entities array
231
	 */
232
	protected InternalNode[] updateEntities(InternalNode[] entities) {
233
		if ((entitiesToRemove.size() > 0) || (entitiesToAdd.size() > 0)) {
234
			List internalNodesList = new ArrayList(Arrays.asList(entities));
235
236
			// remove nodes
237
			for (Iterator iter = entitiesToRemove.iterator(); iter.hasNext();) {
238
				LayoutEntity entity = (LayoutEntity) iter.next();
239
				if (entity.getLayoutInformation() != null) {
240
					internalNodesList.remove(entity.getLayoutInformation());
241
				}
242
			}
243
244
			// Also remove from _internalNodes
245
			ArrayList updatedEntities = new ArrayList(internalNodes.length - entitiesToRemove.size() + entitiesToAdd.size());
246
			for (int i = 0; i < internalNodes.length; i++) {
247
				InternalNode node = internalNodes[i];
248
				if (entitiesToRemove.contains(node.getLayoutEntity())) {
249
					entitiesToRemove.remove(node.getLayoutEntity());
250
				} else {
251
					updatedEntities.add(node);
252
				}
253
			}
254
			entitiesToRemove.clear();
255
256
			// Add any new nodes
257
			LayoutEntity[] entitiesArray = new LayoutEntity[entitiesToAdd.size()];
258
			entitiesArray = (LayoutEntity[]) entitiesToAdd.toArray(entitiesArray);
259
			InternalNode[] newNodes = createInternalNodes(entitiesArray);
260
			for (int i = 0; i < newNodes.length; i++) {
261
				internalNodesList.add(newNodes[i]);
262
				updatedEntities.add(newNodes[i]);
263
			}
264
			entitiesToAdd.clear();
265
266
			entities = new InternalNode[internalNodesList.size()];
267
			entities = (InternalNode[]) internalNodesList.toArray(entities);
268
269
			internalNodes = new InternalNode[updatedEntities.size()];
270
			internalNodes = (InternalNode[]) updatedEntities.toArray(internalNodes);
271
		}
272
273
		return entities;
274
	}
275
276
	/**
277
	 * Updates the given array of relationships checking if any need to be removed or added.
278
	 * Also updates the original array of relationships.
279
	 * @param relationships the current relationships
280
	 * @return the update relationships array
281
	 */
282
	protected InternalRelationship[] updateRelationships(InternalRelationship[] relationships) {
283
		if ((relationshipsToRemove.size() > 0) || (relationshipsToAdd.size() > 0)) {
284
			List internalRelsList = new ArrayList(Arrays.asList(relationships));
285
286
			// remove relationships
287
			if (relationshipsToRemove.size() > 0) {
288
				for (Iterator iter = relationshipsToRemove.iterator(); iter.hasNext();) {
289
					LayoutRelationship relation = (LayoutRelationship) iter.next();
290
					if (relation.getLayoutInformation() != null) {
291
						internalRelsList.remove(relation.getLayoutInformation());
292
					}
293
				}
294
			}
295
296
			// Also remove from _internalRelationships
297
			ArrayList updatedRelationships = new ArrayList(internalRelationships.length - relationshipsToRemove.size() + relationshipsToAdd.size());
298
			for (int i = 0; i < internalRelationships.length; i++) {
299
				InternalRelationship relation = internalRelationships[i];
300
				if (relationshipsToRemove.contains(relation.getLayoutRelationship())) {
301
					relationshipsToRemove.remove(relation.getLayoutRelationship());
302
				} else {
303
					updatedRelationships.add(relation);
304
				}
305
			}
306
			relationshipsToRemove.clear();
307
308
			// add relationships
309
			if (relationshipsToAdd.size() > 0) {
310
				LayoutRelationship[] relsArray = new LayoutRelationship[relationshipsToAdd.size()];
311
				relsArray = (LayoutRelationship[]) relationshipsToAdd.toArray(relsArray);
312
				InternalRelationship[] newRelationships = createInternalRelationships(relsArray);
313
				for (int i = 0; i < newRelationships.length; i++) {
314
					internalRelsList.add(newRelationships[i]);
315
					updatedRelationships.add(newRelationships[i]);
316
				}
317
			}
318
			relationshipsToAdd.clear();
319
320
			relationships = new InternalRelationship[internalRelsList.size()];
321
			relationships = (InternalRelationship[]) internalRelsList.toArray(relationships);
322
323
			internalRelationships = new InternalRelationship[updatedRelationships.size()];
324
			internalRelationships = (InternalRelationship[]) updatedRelationships.toArray(internalRelationships);
325
		}
326
327
		return relationships;
328
	}
329
330
	/**
331
	 * Moves all the entities by the given amount.  
332
	 * @param dx	the amount to shift the entities in the x-direction 
333
	 * @param dy	the amount to shift the entities in the y-direction
334
	 */
335
	/*
336
	 public void moveAllEntities(double dx, double dy) {
337
	 if ((dx != 0) || (dy != 0)) {
338
	 synchronized (_internalNodes) {
339
	 for (int i = 0; i < _internalNodes.length; i++) {
340
	 InternalNode node = _internalNodes[i];
341
	 node.setInternalLocation(node.getInternalX()+dx, node.getInternalY()+dy);
342
	 node.setLocation(node.getX()+dx, node.getY()+dy);
343
	 }
344
	 }
345
	 }
346
	 }
347
	 */
348
349
	/**
350
	 * Returns true if the layout algorithm is running
351
	 * @return boolean if the layout algorithm is running
352
	 */
353
	public synchronized boolean isRunning() {
354
		return !layoutStopped;
355
	}
356
357
	/**
358
	 * Stops the current layout from running.
359
	 * All layout algorithms should constantly check isLayoutRunning
360
	 */
361
	public synchronized void stop() {
362
		layoutStopped = true;
363
		postLayoutAlgorithm(internalNodes, internalRelationships);
364
		fireProgressEnded(getTotalNumberOfLayoutSteps());
365
	}
366
367
	//	/**
368
	//	 * Sleeps while the algorithm is paused.
369
	//	 */
370
	//	protected void sleepWhilePaused() {
371
	//		// do nothing while the algorithm is paused
372
	//		boolean wasPaused = false;
373
	//		while (isPaused()) {
374
	//			try {
375
	//				Thread.sleep(200);
376
	//			} catch (InterruptedException e) {
377
	//			}
378
	//			wasPaused = true;
379
	//		}
380
	//		// update the node positions (they might have been moved while paused)
381
	//		if (wasPaused) {
382
	//			for (int i = 0; i < internalNodes.length; i++) {
383
	//				InternalNode node = internalNodes[i];
384
	//				node.setInternalLocation(node.getPreferredX(), node.getPreferredY());
385
	//			}
386
	//		}
387
	//	}
388
389
	private void setupLayout(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
390
		internalX = x;
391
		internalY = y;
392
		internalHeight = height;
393
		internalWidth = width;
394
		// Filter all the unwanted entities and relationships
395
		entitiesToLayout = (LayoutEntity[]) filterUnwantedObjects(entitiesToLayout);
396
		relationshipsToConsider = (LayoutRelationship[]) filterUnwantedObjects(relationshipsToConsider);
397
398
		// Check that the input is valid
399
		if (!verifyInput(entitiesToLayout, relationshipsToConsider)) {
400
			layoutStopped = true;
401
			throw new RuntimeException("The relationships in relationshipsToConsider don't contain the entities in entitiesToLayout");
402
		}
403
404
		// Create the internal nodes and relationship
405
		internalNodes = createInternalNodes(entitiesToLayout);
406
		internalRelationships = createInternalRelationships(relationshipsToConsider);
407
	}
408
409
	//	public synchronized Stoppable getLayoutThread(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height, boolean continuous) {
410
	//		//setupLayout( entitiesToLayout, relationshipsToConsider, x, y, width, height );
411
	//		this.layoutStopped = false;
412
	//		this.runContinuously = continuous;
413
	//		setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height);
414
	//		preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight);
415
	//		fireProgressStarted(getTotalNumberOfLayoutSteps());
416
	//		return this;
417
	//	}
418
419
	/**
420
	 * Code called before the layout algorithm starts
421
	 */
422
	protected abstract void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height);
423
424
	/**
425
	 * Code called after the layout algorithm ends
426
	 */
427
	protected abstract void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider);
428
429
	/**
430
	 *  Gets the total number of steps in this layout
431
	 */
432
	protected abstract int getTotalNumberOfLayoutSteps();
433
434
	/**
435
	 * Gets the current layout step
436
	 * @return
437
	 */
438
	protected abstract int getCurrentLayoutStep();
439
440
	/**
441
	 * This actually applies the layout
442
	 */
443
	public synchronized void applyLayout(final LayoutEntity[] entitiesToLayout, final LayoutRelationship[] relationshipsToConsider, final double x, final double y, final double width, final double height, boolean asynchronous, boolean continuous) throws InvalidLayoutConfiguration {
444
		checkThread();
445
		this.internalAsynchronous = asynchronous;
446
		this.internalContinuous = continuous;
447
448
		if (!isValidConfiguration(asynchronous, continuous)) {
449
			throw new InvalidLayoutConfiguration();
450
		}
451
452
		clearBendPoints(relationshipsToConsider);
453
454
		this.layoutStopped = false;
455
456
		// when an algorithm starts, reset the progress event
457
		lastProgressEventFired = Calendar.getInstance();
458
		if (asynchronous) {
459
460
			Thread thread = new Thread(new Runnable() {
461
462
				public void run() {
463
					setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height);
464
					preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight);
465
					fireProgressStarted(getTotalNumberOfLayoutSteps());
466
467
					applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight);
468
					stop();
469
				}
470
471
			});
472
			thread.setPriority(Thread.MIN_PRIORITY);
473
			thread.start();
474
		} else {
475
476
			// If we are running synchronously then we have to stop this at some
477
			// point? right?
478
			setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height);
479
			preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight);
480
			fireProgressStarted(getTotalNumberOfLayoutSteps());
481
482
			applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight);
483
			stop();
484
		}
485
486
	}
487
488
	/**
489
	 * Clear out all old bend points before doing a layout
490
	 */
491
	private void clearBendPoints(LayoutRelationship[] relationships) {
492
		for (int i = 0; i < relationships.length; i++) {
493
			LayoutRelationship rel = relationships[i];
494
			rel.clearBendPoints();
495
		}
496
	}
497
498
	/**
499
	 * Update external bend points from the internal bendpoints list. Save the 
500
	 * source and destination points for later use in scaling and translating
501
	 * @param relationshipsToConsider
502
	 */
503
	protected void updateBendPoints(InternalRelationship[] relationshipsToConsider) {
504
		for (int i = 0; i < relationshipsToConsider.length; i++) {
505
			InternalRelationship relationship = relationshipsToConsider[i];
506
			List bendPoints = relationship.getBendPoints();
507
			if (bendPoints.size() > 0) {
508
				// We will assume that source/dest coordinates are for center of node
509
				BendPoint[] externalBendPoints = new BendPoint[bendPoints.size() + 2];
510
				InternalNode sourceNode = relationship.getSource();
511
				externalBendPoints[0] = new BendPoint(sourceNode.getInternalX(), sourceNode.getInternalY());
512
				InternalNode destNode = relationship.getDestination();
513
				externalBendPoints[externalBendPoints.length - 1] = new BendPoint(destNode.getInternalX(), destNode.getInternalY());
514
515
				for (int j = 0; j < bendPoints.size(); j++) {
516
					BendPoint bp = (BendPoint) bendPoints.get(j);
517
					externalBendPoints[j + 1] = new BendPoint(bp.x, bp.y, bp.getIsControlPoint());
518
				}
519
				relationship.getLayoutRelationship().setBendPoints(externalBendPoints);
520
			}
521
		}
522
	}
523
524
	//	public void run() {
525
	//
526
	//		if (started == true) {
527
	//			throw new RuntimeException("Layout has already run!");
528
	//		}
529
	//		started = true;
530
	//		//layoutStopped = false;
531
	//		isLayoutPaused = false;
532
	//		applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight);
533
	//		stop();
534
	//		layoutStopped = true;
535
	//		isLayoutPaused = false;
536
	//	}
537
538
	/**
539
	 * Creates a list of InternalNode objects from the list of LayoutEntity objects the user
540
	 * wants layed out. Sets the internal nodes' positions and sizes from the
541
	 * external entities.
542
	 */
543
	private InternalNode[] createInternalNodes(LayoutEntity[] nodes) {
544
		InternalNode[] internalNodes = new InternalNode[nodes.length];
545
		BasicEntityConstraint basicEntityConstraint = new BasicEntityConstraint();
546
		for (int i = 0; i < nodes.length; i++) {
547
			basicEntityConstraint.clear();
548
			LayoutEntity externalNode = nodes[i];
549
			InternalNode internalNode = new InternalNode(externalNode);
550
			externalNode.populateLayoutConstraint(basicEntityConstraint);
551
			internalNode.setInternalLocation(externalNode.getXInLayout(), externalNode.getYInLayout());
552
			internalNodes[i] = internalNode;
553
		} // end of for
554
		return internalNodes;
555
	}
556
557
	/**
558
	 * Creates a list of InternalRelationship objects from the given list of LayoutRelationship objects.
559
	 * @param rels
560
	 * @return List of internal relationships
561
	 */
562
	private InternalRelationship[] createInternalRelationships(LayoutRelationship[] rels) {
563
		ArrayList listOfInternalRelationships = new ArrayList(rels.length);
564
		for (int i = 0; i < rels.length; i++) {
565
			LayoutRelationship relation = rels[i];
566
			InternalNode src = (InternalNode) relation.getSourceInLayout().getLayoutInformation();
567
			InternalNode dest = (InternalNode) relation.getDestinationInLayout().getLayoutInformation();
568
			if ((src != null) && (dest != null)) {
569
				InternalRelationship internalRelationship = new InternalRelationship(relation, src, dest);
570
				listOfInternalRelationships.add(internalRelationship);
571
			} else {
572
				throw new RuntimeException("Error creating internal relationship, one of the nodes is null: src=" + src + ", dest=" + dest);
573
			}
574
		}
575
		InternalRelationship[] internalRelationships = new InternalRelationship[listOfInternalRelationships.size()];
576
		listOfInternalRelationships.toArray(internalRelationships);
577
		return internalRelationships;
578
	}
579
580
	/**
581
	 * Removes any objects that are currently filtered
582
	 */
583
	private Object[] filterUnwantedObjects(LayoutItem[] objects) {
584
		// first remove any entities or relationships that are filtered.
585
		List unfilteredObjsList = new ArrayList();
586
		if (filter != null) {
587
			for (int i = 0; i < objects.length; i++) {
588
				LayoutItem object = objects[i];
589
				if (!filter.isObjectFiltered(object)) {
590
					unfilteredObjsList.add(object);
591
				}
592
			}
593
			//@tag bug.156266-ClassCast.fix : use reflection to create the array.
594
			Object[] unfilteredObjs = (Object[]) java.lang.reflect.Array.newInstance(objects.getClass().getComponentType(), unfilteredObjsList.size());
595
			unfilteredObjsList.toArray(unfilteredObjs);
596
			return unfilteredObjs;
597
		}
598
		return objects;
599
	}
600
601
	/**
602
	 * Filters the entities and relationships to apply the layout on
603
	 */
604
	public void setFilter(Filter filter) {
605
		this.filter = filter;
606
	}
607
608
	/**
609
	 * Determines the order in which the objects should be displayed.
610
	 * Note: Some algorithms force a specific order.
611
	 */
612
	public void setComparator(Comparator comparator) {
613
		this.comparator = new InternalComparator(comparator);
614
	}
615
616
	/**
617
	 * Verifies the endpoints of the relationships are entities in the entitiesToLayout list.
618
	 * Allows other classes in this package to use this method to verify the input
619
	 */
620
	public static boolean verifyInput(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider) {
621
		boolean stillValid = true;
622
		for (int i = 0; i < relationshipsToConsider.length; i++) {
623
			LayoutRelationship relationship = relationshipsToConsider[i];
624
			LayoutEntity source = relationship.getSourceInLayout();
625
			LayoutEntity destination = relationship.getDestinationInLayout();
626
			boolean containsSrc = false;
627
			boolean containsDest = false;
628
			int j = 0;
629
			while (j < entitiesToLayout.length && !(containsSrc && containsDest)) {
630
				if (entitiesToLayout[j].equals(source)) {
631
					containsSrc = true;
632
				}
633
				if (entitiesToLayout[j].equals(destination)) {
634
					containsDest = true;
635
				}
636
				j++;
637
			}
638
			stillValid = containsSrc && containsDest;
639
		}
640
		return stillValid;
641
	}
642
643
	/**
644
	 * Gets the location in the layout bounds for this node
645
	 * @param x
646
	 * @param y
647
	 * @return
648
	 */
649
	protected DisplayIndependentPoint getLocalLocation(InternalNode[] entitiesToLayout, double x, double y, DisplayIndependentRectangle realBounds) {
650
651
		double screenWidth = realBounds.width;
652
		double screenHeight = realBounds.height;
653
		DisplayIndependentRectangle layoutBounds = getLayoutBounds(entitiesToLayout, false);
654
		double localX = (x / screenWidth) * layoutBounds.width + layoutBounds.x;
655
		double localY = (y / screenHeight) * layoutBounds.height + layoutBounds.y;
656
		return new DisplayIndependentPoint(localX, localY);
657
	}
658
659
	/**
660
	 * Find an appropriate size for the given nodes, then fit them into the given bounds.
661
	 * The relative locations of the nodes to each other must be preserved.
662
	 * Child classes should set flag reresizeEntitiesAfterLayout to false if they 
663
	 * want to preserve node sizes.
664
	 */
665
	protected void defaultFitWithinBounds(InternalNode[] entitiesToLayout, DisplayIndependentRectangle realBounds) {
666
		defaultFitWithinBounds(entitiesToLayout, new InternalRelationship[0], realBounds);
667
	}
668
669
	/**
670
	 * Find an appropriate size for the given nodes, then fit them into the given bounds.
671
	 * The relative locations of the nodes to each other must be preserved.
672
	 * Child classes should set flag reresizeEntitiesAfterLayout to false if they 
673
	 * want to preserve node sizes.
674
	 */
675
	protected void defaultFitWithinBounds(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle realBounds) {
676
677
		DisplayIndependentRectangle layoutBounds;
678
679
		if (resizeEntitiesAfterLayout) {
680
			layoutBounds = getLayoutBounds(entitiesToLayout, false);
681
682
			// Convert node x,y to be in percent rather than absolute coords
683
			convertPositionsToPercentage(entitiesToLayout, relationships, layoutBounds, false /*do not update size*/);
684
685
			// Resize and shift nodes
686
			resizeAndShiftNodes(entitiesToLayout);
687
		}
688
689
		// Recalculate layout, allowing for the node width, which we now know
690
		layoutBounds = getLayoutBounds(entitiesToLayout, true);
691
692
		// adjust node positions again, to the new coordinate system (still as a percentage)
693
		convertPositionsToPercentage(entitiesToLayout, relationships, layoutBounds, true /*update node size*/);
694
695
		DisplayIndependentRectangle screenBounds = calcScreenBounds(realBounds, layoutBounds);
696
697
		// Now convert to real screen coordinates
698
		convertPositionsToCoords(entitiesToLayout, relationships, screenBounds);
699
	}
700
701
	/**
702
	 * Calculate the screen bounds, maintaining the  
703
	 * @param realBounds
704
	 * @return
705
	 */
706
	private DisplayIndependentRectangle calcScreenBounds(DisplayIndependentRectangle realBounds, DisplayIndependentRectangle layoutBounds) {
707
		if (resizeEntitiesAfterLayout) { // OK to alter aspect ratio 
708
			double borderWidth = Math.min(realBounds.width, realBounds.height) / 10.0; // use 10% for the border - 5% on each side
709
			return new DisplayIndependentRectangle(realBounds.x + borderWidth / 2.0, realBounds.y + borderWidth / 2.0, realBounds.width - borderWidth, realBounds.height - borderWidth);
710
		} else { // retain layout aspect ratio 
711
			double heightAdjustment = realBounds.height / layoutBounds.height;
712
			double widthAdjustment = realBounds.width / layoutBounds.width;
713
			double ratio = Math.min(heightAdjustment, widthAdjustment);
714
			double adjustedHeight = layoutBounds.height * ratio;
715
			double adjustedWidth = layoutBounds.width * ratio;
716
			double adjustedX = realBounds.x + (realBounds.width - adjustedWidth) / 2.0;
717
			double adjustedY = realBounds.y + (realBounds.height - adjustedHeight) / 2.0;
718
			double borderWidth = Math.min(adjustedWidth, adjustedHeight) / 10.0; // use 10% for the border - 5% on each side
719
			return new DisplayIndependentRectangle(adjustedX + borderWidth / 2.0, adjustedY + borderWidth / 2.0, adjustedWidth - borderWidth, adjustedHeight - borderWidth);
720
		}
721
	}
722
723
	/**
724
	 * Find and set the node size - shift the nodes to the right and down to make 
725
	 * room for the width and height. 
726
	 * @param entitiesToLayout
727
	 * @param relationships
728
	 */
729
	private void resizeAndShiftNodes(InternalNode[] entitiesToLayout) {
730
		// get maximum node size as percent of screen dimmensions
731
		double nodeSize = getNodeSize(entitiesToLayout);
732
		double halfNodeSize = nodeSize / 2;
733
734
		// Resize and shift nodes
735
		for (int i = 0; i < entitiesToLayout.length; i++) {
736
			InternalNode node = entitiesToLayout[i];
737
			node.setInternalSize(nodeSize, nodeSize);
738
			node.setInternalLocation(node.getInternalX() + halfNodeSize, node.getInternalY() + halfNodeSize);
739
		}
740
	}
741
742
	/**
743
	 * Convert all node positions into a percentage of the screen. If includeNodeSize
744
	 * is true then this also updates the node's internal size. 
745
	 * @param entitiesToLayout
746
	 */
747
	private void convertPositionsToPercentage(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle layoutBounds, boolean includeNodeSize) {
748
749
		// Adjust node positions and sizes
750
		for (int i = 0; i < entitiesToLayout.length; i++) {
751
			InternalNode node = entitiesToLayout[i];
752
			DisplayIndependentPoint location = node.getInternalLocation().convertToPercent(layoutBounds);
753
			node.setInternalLocation(location.x, location.y);
754
			if (includeNodeSize) { // adjust node sizes
755
				double width = node.getInternalWidth() / layoutBounds.width;
756
				double height = node.getInternalHeight() / layoutBounds.height;
757
				node.setInternalSize(width, height);
758
			}
759
		}
760
761
		// Adjust bendpoint positions
762
		for (int i = 0; i < relationships.length; i++) {
763
			InternalRelationship rel = relationships[i];
764
			for (int j = 0; j < rel.getBendPoints().size(); j++) {
765
				BendPoint bp = (BendPoint) rel.getBendPoints().get(j);
766
				DisplayIndependentPoint toPercent = bp.convertToPercent(layoutBounds);
767
				bp.setX(toPercent.x);
768
				bp.setY(toPercent.y);
769
			}
770
		}
771
	}
772
773
	/**
774
	 * Convert the positions from a percentage of bounds area to fixed
775
	 * coordinates. NOTE: ALL OF THE POSITIONS OF NODES UNTIL NOW WERE FOR THE
776
	 * CENTER OF THE NODE - Convert it to the left top corner.
777
	 * 
778
	 * @param entitiesToLayout
779
	 * @param relationships
780
	 * @param realBounds
781
	 */
782
	private void convertPositionsToCoords(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle screenBounds) {
783
784
		// Adjust node positions and sizes
785
		for (int i = 0; i < entitiesToLayout.length; i++) {
786
			InternalNode node = entitiesToLayout[i];
787
			double width = node.getInternalWidth() * screenBounds.width;
788
			double height = node.getInternalHeight() * screenBounds.height;
789
			DisplayIndependentPoint location = node.getInternalLocation().convertFromPercent(screenBounds);
790
			node.setInternalLocation(location.x - width / 2, location.y - height / 2);
791
			if (resizeEntitiesAfterLayout) {
792
				adjustNodeSizeAndPos(node, height, width);
793
			} else {
794
				node.setInternalSize(width, height);
795
			}
796
		}
797
798
		// Adjust bendpoint positions and shift based on source node size
799
		for (int i = 0; i < relationships.length; i++) {
800
			InternalRelationship rel = relationships[i];
801
			for (int j = 0; j < rel.getBendPoints().size(); j++) {
802
				BendPoint bp = (BendPoint) rel.getBendPoints().get(j);
803
				DisplayIndependentPoint fromPercent = bp.convertFromPercent(screenBounds);
804
				bp.setX(fromPercent.x);
805
				bp.setY(fromPercent.y);
806
			}
807
		}
808
	}
809
810
	/**
811
	 * Adjust node size to take advantage of space. Reset position to top left corner of node. 
812
	 * @param node
813
	 * @param height
814
	 * @param width
815
	 */
816
	private void adjustNodeSizeAndPos(InternalNode node, double height, double width) {
817
		double widthUsingHeight = height * widthToHeightRatio;
818
		if (widthToHeightRatio <= 1.0 && widthUsingHeight <= width) {
819
			double widthToUse = height * widthToHeightRatio;
820
			double leftOut = width - widthToUse;
821
			node.setInternalSize(Math.max(height * widthToHeightRatio, MIN_ENTITY_SIZE), Math.max(height, MIN_ENTITY_SIZE));
822
			node.setInternalLocation(node.getInternalX() + leftOut / 2, node.getInternalY());
823
824
		} else {
825
			double heightToUse = height / widthToHeightRatio;
826
			double leftOut = height - heightToUse;
827
828
			node.setInternalSize(Math.max(width, MIN_ENTITY_SIZE), Math.max(width / widthToHeightRatio, MIN_ENTITY_SIZE));
829
			node.setInternalLocation(node.getInternalX(), node.getInternalY() + leftOut / 2);
830
		}
831
832
	}
833
834
	/**
835
	 * Returns the maximum possible node size as a percentage of the width or height in current coord system.
836
	 */
837
	private double getNodeSize(InternalNode[] entitiesToLayout) {
838
		double width, height;
839
		if (entitiesToLayout.length == 1) {
840
			width = 0.8;
841
			height = 0.8;
842
		} else {
843
			DisplayIndependentDimension minimumDistance = getMinimumDistance(entitiesToLayout);
844
			width = 0.8 * minimumDistance.width;
845
			height = 0.8 * minimumDistance.height;
846
		}
847
		return Math.max(width, height);
848
	}
849
850
	/**
851
	 * Find the bounds in which the nodes are located.  Using the bounds against the real bounds
852
	 * of the screen, the nodes can proportionally be placed within the real bounds.
853
	 * The bounds can be determined either including the size of the nodes or not.  If the size
854
	 * is not included, the bounds will only be guaranteed to include the center of each node.
855
	 */
856
	protected DisplayIndependentRectangle getLayoutBounds(InternalNode[] entitiesToLayout, boolean includeNodeSize) {
857
		double rightSide = Double.MIN_VALUE;
858
		double bottomSide = Double.MIN_VALUE;
859
		double leftSide = Double.MAX_VALUE;
860
		double topSide = Double.MAX_VALUE;
861
		for (int i = 0; i < entitiesToLayout.length; i++) {
862
			InternalNode entity = entitiesToLayout[i];
863
			if (entity.hasPreferredLocation()) {
864
				continue;
865
			}
866
867
			if (includeNodeSize) {
868
				leftSide = Math.min(entity.getInternalX() - entity.getInternalWidth() / 2, leftSide);
869
				topSide = Math.min(entity.getInternalY() - entity.getInternalHeight() / 2, topSide);
870
				rightSide = Math.max(entity.getInternalX() + entity.getInternalWidth() / 2, rightSide);
871
				bottomSide = Math.max(entity.getInternalY() + entity.getInternalHeight() / 2, bottomSide);
872
			} else {
873
				leftSide = Math.min(entity.getInternalX(), leftSide);
874
				topSide = Math.min(entity.getInternalY(), topSide);
875
				rightSide = Math.max(entity.getInternalX(), rightSide);
876
				bottomSide = Math.max(entity.getInternalY(), bottomSide);
877
			}
878
		}
879
		return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide);
880
	}
881
882
	/** 
883
	 * minDistance is the closest that any two points are together.
884
	 * These two points become the center points for the two closest nodes, 
885
	 * which we wish to make them as big as possible without overlapping.
886
	 * This will be the maximum of minDistanceX and minDistanceY minus a bit, lets say 20%
887
	 * 
888
	 * We make the recommended node size a square for convenience.
889
	 * 
890
	 * 
891
	 *    _______
892
	 *   |       | 
893
	 *   |       |
894
	 *   |   +   |
895
	 *   |   |\  |
896
	 *   |___|_\_|_____
897
	 *       | | \     |
898
	 *       | |  \    |
899
	 *       +-|---+   |
900
	 *         |       |
901
	 *         |_______|
902
	 * 
903
	 *  
904
	 * 
905
	 */
906
	private DisplayIndependentDimension getMinimumDistance(InternalNode[] entitiesToLayout) {
907
		DisplayIndependentDimension horAndVertdistance = new DisplayIndependentDimension(Double.MAX_VALUE, Double.MAX_VALUE);
908
		double minDistance = Double.MAX_VALUE; // the minimum distance between all the nodes
909
		//TODO: Very Slow!
910
		for (int i = 0; i < entitiesToLayout.length; i++) {
911
			InternalNode layoutEntity1 = entitiesToLayout[i];
912
			double x1 = layoutEntity1.getInternalX();
913
			double y1 = layoutEntity1.getInternalY();
914
			for (int j = i + 1; j < entitiesToLayout.length; j++) {
915
				InternalNode layoutEntity2 = entitiesToLayout[j];
916
				double x2 = layoutEntity2.getInternalX();
917
				double y2 = layoutEntity2.getInternalY();
918
				double distanceX = Math.abs(x1 - x2);
919
				double distanceY = Math.abs(y1 - y2);
920
				double distance = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));
921
922
				if (distance < minDistance) {
923
					minDistance = distance;
924
					horAndVertdistance.width = distanceX;
925
					horAndVertdistance.height = distanceY;
926
				}
927
			}
928
		}
929
		return horAndVertdistance;
930
	}
931
932
	/**
933
	 * Set the width to height ratio you want the entities to use
934
	 */
935
	public void setEntityAspectRatio(double ratio) {
936
		widthToHeightRatio = ratio;
937
	}
938
939
	/**
940
	 * Returns the width to height ratio this layout will use to set the size of the entities.
941
	 */
942
	public double getEntityAspectRatio() {
943
		return widthToHeightRatio;
944
	}
945
946
	/**
947
	 * A layout algorithm could take an uncomfortable amout of time to complete.  To relieve some of
948
	 * the mystery, the layout algorithm will notify each ProgressListener of its progress. 
949
	 */
950
	public void addProgressListener(ProgressListener listener) {
951
		if (!progressListeners.contains(listener)) {
952
			progressListeners.add(listener);
953
		}
954
	}
955
956
	/**
957
	 * Removes the given progress listener, preventing it from receiving any more updates.
958
	 */
959
	public void removeProgressListener(ProgressListener listener) {
960
		if (progressListeners.contains(listener)) {
961
			progressListeners.remove(listener);
962
		}
963
	}
964
965
	/**
966
	 * Updates the layout locations so the external nodes know about the new locations
967
	 */
968
	protected void updateLayoutLocations(InternalNode[] nodes) {
969
		for (int i = 0; i < nodes.length; i++) {
970
			InternalNode node = nodes[i];
971
			if (!node.hasPreferredLocation()) {
972
				node.setLocation(node.getInternalX(), node.getInternalY());
973
974
				if ((layout_styles & LayoutStyles.NO_LAYOUT_NODE_RESIZING) != 1) {
975
					// Only set the size if we are supposed to
976
					node.setSize(node.getInternalWidth(), node.getInternalHeight());
977
				}
978
			}
979
		}
980
	}
981
982
	protected void fireProgressStarted(int totalNumberOfSteps) {
983
		ProgressEvent event = new ProgressEvent(0, totalNumberOfSteps);
984
		for (int i = 0; i < progressListeners.size(); i++) {
985
			ProgressListener listener = (ProgressListener) progressListeners.get(i);
986
987
			listener.progressStarted(event);
988
		}
989
	}
990
991
	protected void fireProgressEnded(int totalNumberOfSteps) {
992
		ProgressEvent event = new ProgressEvent(totalNumberOfSteps, totalNumberOfSteps);
993
		for (int i = 0; i < progressListeners.size(); i++) {
994
			ProgressListener listener = (ProgressListener) progressListeners.get(i);
995
			listener.progressEnded(event);
996
		}
997
998
	}
999
1000
	/**
1001
	 * Fires an event to notify all of the registered ProgressListeners that another step
1002
	 * has been completed in the algorithm.
1003
	 * @param currentStep The current step completed.
1004
	 * @param totalNumberOfSteps The total number of steps in the algorithm.
1005
	 */
1006
	protected void fireProgressEvent(int currentStep, int totalNumberOfSteps) {
1007
1008
		// Update the layout locations to the external nodes
1009
		Calendar now = Calendar.getInstance();
1010
		now.add(Calendar.MILLISECOND, -MIN_TIME_DELAY_BETWEEN_PROGRESS_EVENTS);
1011
1012
		if (now.after(lastProgressEventFired) || currentStep == totalNumberOfSteps) {
1013
			ProgressEvent event = new ProgressEvent(currentStep, totalNumberOfSteps);
1014
1015
			for (int i = 0; i < progressListeners.size(); i++) {
1016
1017
				ProgressListener listener = (ProgressListener) progressListeners.get(i);
1018
				listener.progressUpdated(event);
1019
			}
1020
			lastProgressEventFired = Calendar.getInstance();
1021
		}
1022
1023
	}
1024
1025
	protected int getNumberOfProgressListeners() {
1026
		return progressListeners.size();
1027
	}
1028
1029
	private void checkThread() {
1030
		if (this.creationThread != Thread.currentThread()) {
1031
			throw new RuntimeException("Invalid Thread Access.");
1032
		}
1033
	}
1034
1035
}
(-)src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java (+205 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: The Chisel Group - initial API and implementation
8
 *               Mateusz Matela 
9
 *               Ian Bull
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
14
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
15
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
16
import org.eclipse.zest.layouts.interfaces.EntityLayout;
17
18
class AlgorithmHelper {
19
20
	public static int MIN_NODE_SIZE = 8;
21
22
	public static double PADDING_PERCENT = 0.8;
23
24
	/**
25
	 * Fits given entities within given bounds, preserving their relative
26
	 * locations.
27
	 * 
28
	 * @param entities
29
	 * @param destinationBounds
30
	 * @param resize
31
	 */
32
	public static void fitWithinBounds(EntityLayout[] entities, DisplayIndependentRectangle destinationBounds, boolean resize) {
33
		DisplayIndependentRectangle startingBounds = getLayoutBounds(entities, false);
34
		double sizeScale = Math.min(destinationBounds.width / startingBounds.width, destinationBounds.height / startingBounds.height);
35
		if (entities.length == 1) {
36
			fitSingleEntity(entities[0], destinationBounds, resize);
37
			return;
38
		}
39
		for (int i = 0; i < entities.length; i++) {
40
			EntityLayout entity = entities[i];
41
			DisplayIndependentDimension size = entity.getSize();
42
			if (entity.isMovable()) {
43
				DisplayIndependentPoint location = entity.getLocation();
44
				double percentX = (location.x - startingBounds.x) / (startingBounds.width);
45
				double percentY = (location.y - startingBounds.y) / (startingBounds.height);
46
47
				if (resize && entity.isResizable()) {
48
					size.width *= sizeScale;
49
					size.height *= sizeScale;
50
					entity.setSize(size.width, size.height);
51
				}
52
53
				location.x = destinationBounds.x + size.width / 2 + percentX * (destinationBounds.width - size.width);
54
				location.y = destinationBounds.y + size.height / 2 + percentY * (destinationBounds.height - size.height);
55
				entity.setLocation(location.x, location.y);
56
57
			} else
58
			if (resize && entity.isResizable()) {
59
				entity.setSize(size.width * sizeScale, size.height * sizeScale);
60
			}
61
		}
62
	}
63
64
	private static void fitSingleEntity(EntityLayout entity, DisplayIndependentRectangle destinationBounds, boolean resize) {
65
		if (entity.isMovable()) {
66
			entity.setLocation(destinationBounds.x + destinationBounds.width / 2, destinationBounds.y + destinationBounds.height / 2);
67
		}
68
		if (resize && entity.isResizable()) {
69
			double width = destinationBounds.width;
70
			double height = destinationBounds.height;
71
			double preferredAspectRatio = entity.getPreferredAspectRatio();
72
			if (preferredAspectRatio > 0) {
73
				DisplayIndependentDimension fixedSize = fixAspectRatio(width, height, preferredAspectRatio);
74
				entity.setSize(fixedSize.width, fixedSize.height);
75
			} else {
76
				entity.setSize(width, height);
77
			}
78
		}
79
	}
80
81
	/**
82
	 * Resizes the nodes so that they have a maximal area without overlapping
83
	 * each other, with additional empty space of 20% of node's width (or
84
	 * height, if bigger). It does nothing if there's less than two nodes.
85
	 * 
86
	 * @param entities
87
	 */
88
	public static void maximizeSizes(EntityLayout[] entities) {
89
		if (entities.length > 1) {
90
			DisplayIndependentDimension minDistance = getMinimumDistance(entities);
91
			double nodeSize = Math.max(minDistance.width, minDistance.height) * PADDING_PERCENT;
92
			double width = nodeSize;
93
			double height = nodeSize;
94
			for (int i = 0; i < entities.length; i++) {
95
				EntityLayout entity = entities[i];
96
				if (entity.isResizable()) {
97
					double preferredRatio = entity.getPreferredAspectRatio();
98
					if (preferredRatio > 0) {
99
						DisplayIndependentDimension fixedSize = fixAspectRatio(width, height, preferredRatio);
100
						entity.setSize(fixedSize.width, fixedSize.height);
101
					} else {
102
						entity.setSize(width, height);
103
					}
104
				}
105
			}
106
		}
107
	}
108
109
	private static DisplayIndependentDimension fixAspectRatio(double width, double height, double preferredRatio) {
110
		double actualRatio = width / height;
111
		if (actualRatio > preferredRatio) {
112
			width = height * preferredRatio;
113
			if (width < MIN_NODE_SIZE) {
114
				width = MIN_NODE_SIZE;
115
				height = width / preferredRatio;
116
			}
117
		}
118
		if (actualRatio < preferredRatio) {
119
			height = width / preferredRatio;
120
			if (height < MIN_NODE_SIZE) {
121
				height = MIN_NODE_SIZE;
122
				width = height * preferredRatio;
123
			}
124
		}
125
		return new DisplayIndependentDimension(width, height);
126
	}
127
128
	/**
129
	 * Find the bounds in which the nodes are located. Using the bounds against
130
	 * the real bounds of the screen, the nodes can proportionally be placed
131
	 * within the real bounds. The bounds can be determined either including the
132
	 * size of the nodes or not. If the size is not included, the bounds will
133
	 * only be guaranteed to include the center of each node.
134
	 */
135
	public static DisplayIndependentRectangle getLayoutBounds(EntityLayout[] entities, boolean includeNodeSize) {
136
		double rightSide = Double.NEGATIVE_INFINITY;
137
		double bottomSide = Double.NEGATIVE_INFINITY;
138
		double leftSide = Double.POSITIVE_INFINITY;
139
		double topSide = Double.POSITIVE_INFINITY;
140
		for (int i = 0; i < entities.length; i++) {
141
			EntityLayout entity = entities[i];
142
			DisplayIndependentPoint location = entity.getLocation();
143
			DisplayIndependentDimension size = entity.getSize();
144
			if (includeNodeSize) {
145
				leftSide = Math.min(location.x - size.width / 2, leftSide);
146
				topSide = Math.min(location.y - size.height / 2, topSide);
147
				rightSide = Math.max(location.x + size.width / 2, rightSide);
148
				bottomSide = Math.max(location.y + size.height / 2, bottomSide);
149
			} else {
150
				leftSide = Math.min(location.x, leftSide);
151
				topSide = Math.min(location.y, topSide);
152
				rightSide = Math.max(location.x, rightSide);
153
				bottomSide = Math.max(location.y, bottomSide);
154
			}
155
		}
156
		return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide);
157
	}
158
159
	/**
160
	 * minDistance is the closest that any two points are together. These two
161
	 * points become the center points for the two closest nodes, which we wish
162
	 * to make them as big as possible without overlapping. This will be the
163
	 * maximum of minDistanceX and minDistanceY minus a bit, lets say 20%
164
	 * 
165
	 * We make the recommended node size a square for convenience.
166
	 * 
167
	 * <pre>
168
	 *    _______
169
	 *   |       | 
170
	 *   |       |
171
	 *   |   +   |
172
	 *   |   |\  |
173
	 *   |___|_\_|_____
174
	 *       | | \     |
175
	 *       | |  \    |
176
	 *       +-|---+   |
177
	 *         |       |
178
	 *         |_______|
179
	 * </pre>
180
	 * 
181
	 * 
182
	 */
183
	public static DisplayIndependentDimension getMinimumDistance(EntityLayout[] entities) {
184
		DisplayIndependentDimension horAndVertdistance = new DisplayIndependentDimension(Double.MAX_VALUE, Double.MAX_VALUE);
185
		double minDistance = Double.MAX_VALUE;
186
187
		// TODO: Very Slow!
188
		for (int i = 0; i < entities.length; i++) {
189
			DisplayIndependentPoint location1 = entities[i].getLocation();
190
			for (int j = i + 1; j < entities.length; j++) {
191
				DisplayIndependentPoint location2 = entities[j].getLocation();
192
				double distanceX = location1.x - location2.x;
193
				double distanceY = location1.y - location2.y;
194
				double distance = distanceX * distanceX + distanceY * distanceY;
195
196
				if (distance < minDistance) {
197
					minDistance = distance;
198
					horAndVertdistance.width = Math.abs(distanceX);
199
					horAndVertdistance.height = Math.abs(distanceY);
200
				}
201
			}
202
		}
203
		return horAndVertdistance;
204
	}
205
}
(-)src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java (+48 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.algorithms;
11
12
/**
13
 * Layout algorithm that places all elements in one column or one row, depending
14
 * on set orientation.
15
 */
16
public class BoxLayoutAlgorithm extends GridLayoutAlgorithm {
17
18
	public static final int HORIZONTAL = 1;
19
20
	public static final int VERTICAL = 2;
21
22
	private int orientation = HORIZONTAL;
23
24
	public BoxLayoutAlgorithm() {
25
	}
26
27
	public BoxLayoutAlgorithm(int orientation) {
28
		setOrientation(orientation);
29
	}
30
31
	public int getOrientation() {
32
		return orientation;
33
	}
34
35
	public void setOrientation(int orientation) {
36
		if (orientation == HORIZONTAL || orientation == VERTICAL)
37
			this.orientation = orientation;
38
		else
39
			throw new RuntimeException("Invalid orientation: " + orientation);
40
	}
41
42
	protected int[] calculateNumberOfRowsAndCols(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
43
		if (orientation == HORIZONTAL)
44
			return new int[] { numChildren, 1 };
45
		else
46
			return new int[] { 1, numChildren };
47
	}
48
}
(-)src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java (-77 / +35 lines)
Lines 1-77 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
11
package org.eclipse.zest.layouts.algorithms;
12
12
13
import org.eclipse.zest.layouts.InvalidLayoutConfiguration;
13
import org.eclipse.zest.layouts.LayoutAlgorithm;
14
import org.eclipse.zest.layouts.LayoutAlgorithm;
14
import org.eclipse.zest.layouts.interfaces.LayoutContext;
15
import org.eclipse.zest.layouts.dataStructures.InternalNode;
15
16
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
16
public class CompositeLayoutAlgorithm implements LayoutAlgorithm {
17
17
18
public class CompositeLayoutAlgorithm extends AbstractLayoutAlgorithm {
18
	private LayoutAlgorithm[] algorithms = null;
19
19
20
	LayoutAlgorithm[] algorithms = null;
20
	public CompositeLayoutAlgorithm(LayoutAlgorithm[] algorithms) {
21
21
		this.algorithms = algorithms;
22
	public CompositeLayoutAlgorithm(int styles, LayoutAlgorithm[] algoirthms) {
22
	}
23
		super(styles);
23
24
		this.algorithms = algoirthms;
24
	public void applyLayout(boolean clean) {
25
	}
25
		for (int i = 0; i < algorithms.length; i++) {
26
	
26
			algorithms[i].applyLayout(clean);
27
	public CompositeLayoutAlgorithm( LayoutAlgorithm[] algoirthms) {
27
		}
28
		this(0, algoirthms);
28
	}
29
	}
29
30
30
	public void setLayoutContext(LayoutContext context) {
31
	protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) {
31
		for (int i = 0; i < algorithms.length; i++) {
32
32
			algorithms[i].setLayoutContext(context);
33
		for (int i = 0; i < algorithms.length; i++) {
33
		}
34
			try {
34
	}
35
				algorithms[i].applyLayout(entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight, this.internalAsynchronous, this.internalContinuous);
35
}
36
			} catch (InvalidLayoutConfiguration e) {
37
				e.printStackTrace();
38
			}
39
		}
40
		for (int i = 0; i < entitiesToLayout.length; i++) {
41
			entitiesToLayout[i].getLayoutEntity().setLocationInLayout(entitiesToLayout[i].getXInLayout(), entitiesToLayout[i].getYInLayout());
42
		}
43
44
		//updateLayoutLocations(entitiesToLayout);
45
	}
46
47
	protected int getCurrentLayoutStep() {
48
		// TODO Auto-generated method stub
49
		return 0;
50
	}
51
52
	protected int getTotalNumberOfLayoutSteps() {
53
		// TODO Auto-generated method stub
54
		return 0;
55
	}
56
57
	protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) {
58
		// TODO Auto-generated method stub
59
		return true;
60
	}
61
62
	protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
63
		// TODO Auto-generated method stub
64
65
	}
66
67
	protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
68
		// TODO Auto-generated method stub
69
70
	}
71
72
	public void setLayoutArea(double x, double y, double width, double height) {
73
		// TODO Auto-generated method stub
74
75
	}
76
77
}
(-)src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java (-102 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
14
import org.eclipse.zest.layouts.dataStructures.InternalNode;
15
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
16
17
/**
18
 * 
19
 * @author Ian Bull
20
 * 
21
 * Used to represent algorithms that can continuously run.  
22
 *
23
 */
24
public abstract class ContinuousLayoutAlgorithm extends AbstractLayoutAlgorithm {
25
26
	double x, y, widht, height;
27
28
	public ContinuousLayoutAlgorithm(int styles) {
29
		super(styles);
30
	}
31
32
	/**
33
	 * The logic to determine if a layout should continue running or not
34
	 */
35
	protected abstract boolean performAnotherNonContinuousIteration();
36
37
	/**
38
	 * Computes a single iteration of the layout algorithm
39
	 * @return
40
	 */
41
	protected abstract void computeOneIteration(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height);
42
43
	private boolean continueRunning() {
44
		if (layoutStopped) {
45
			return false;
46
		} else if (this.internalContinuous && !layoutStopped) {
47
			return true;
48
		} else if (performAnotherNonContinuousIteration()) {
49
			return true;
50
		} else {
51
			return false;
52
		}
53
	}
54
55
	public void setLayoutArea(double x, double y, double width, double height) {
56
		this.setBounds(x, y, width, height);
57
58
	}
59
60
	public synchronized DisplayIndependentRectangle getBounds() {
61
		return new DisplayIndependentRectangle(this.x, this.y, this.widht, this.height);
62
	}
63
64
	public synchronized void setBounds(double x, double y, double width, double height) {
65
		this.x = x;
66
		this.y = y;
67
		this.widht = width;
68
		this.height = height;
69
	}
70
71
	/**
72
	 * Calculates and applies the positions of the given entities based on a
73
	 * spring layout using the given relationships.
74
	 */
75
	protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
76
77
		this.setBounds(x, y, width, height);
78
79
		while (continueRunning()) {
80
			// check for entities and relationships to add or remove 
81
			entitiesToLayout = updateEntities(entitiesToLayout);
82
			relationshipsToConsider = updateRelationships(relationshipsToConsider);
83
			DisplayIndependentRectangle bounds = this.getBounds();
84
			double localX = bounds.x;
85
			double localY = bounds.y;
86
			double localWidth = bounds.width;
87
			double localHeight = bounds.height;
88
89
			computeOneIteration(entitiesToLayout, relationshipsToConsider, localX, localY, localWidth, localHeight);
90
91
			updateLayoutLocations(entitiesToLayout);
92
93
			if (this.internalContinuous) {
94
				fireProgressEvent(1, 1);
95
			} else {
96
				fireProgressEvent(getCurrentLayoutStep(), getTotalNumberOfLayoutSteps());
97
			}
98
99
		}
100
	}
101
102
}
(-)src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java (-124 / +132 lines)
Lines 1-124 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
11
package org.eclipse.zest.layouts.algorithms;
12
12
13
import java.lang.reflect.Field;
13
import java.lang.reflect.Field;
14
import java.util.HashMap;
14
import java.util.HashMap;
15
import java.util.Iterator;
15
import java.util.Iterator;
16
import java.util.List;
16
import java.util.List;
17
17
18
import org.eclipse.draw2d.geometry.Dimension;
18
import org.eclipse.draw2d.geometry.Dimension;
19
import org.eclipse.draw2d.graph.DirectedGraph;
19
import org.eclipse.draw2d.graph.DirectedGraph;
20
import org.eclipse.draw2d.graph.DirectedGraphLayout;
20
import org.eclipse.draw2d.graph.DirectedGraphLayout;
21
import org.eclipse.draw2d.graph.Edge;
21
import org.eclipse.draw2d.graph.Edge;
22
import org.eclipse.draw2d.graph.Node;
22
import org.eclipse.draw2d.graph.Node;
23
import org.eclipse.swt.SWT;
23
import org.eclipse.zest.layouts.LayoutAlgorithm;
24
import org.eclipse.zest.layouts.dataStructures.InternalNode;
24
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
25
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
25
import org.eclipse.zest.layouts.interfaces.EntityLayout;
26
26
import org.eclipse.zest.layouts.interfaces.LayoutContext;
27
public class DirectedGraphLayoutAlgorithm extends AbstractLayoutAlgorithm {
27
import org.eclipse.zest.layouts.interfaces.NodeLayout;
28
28
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
29
	class ExtendedDirectedGraphLayout extends DirectedGraphLayout {
29
30
30
public class DirectedGraphLayoutAlgorithm implements LayoutAlgorithm {
31
		public void visit(DirectedGraph graph) {
31
	
32
			Field field;
32
	class ExtendedDirectedGraphLayout extends DirectedGraphLayout {
33
			try {
33
34
				field = DirectedGraphLayout.class.getDeclaredField("steps");
34
		public void visit(DirectedGraph graph) {
35
				field.setAccessible(true);
35
			Field field;
36
				Object object = field.get(this);
36
			try {
37
				List steps = (List) object;
37
				field = DirectedGraphLayout.class.getDeclaredField("steps");
38
				steps.remove(10);
38
				field.setAccessible(true);
39
				steps.remove(9);
39
				Object object = field.get(this);
40
				steps.remove(8);
40
				List steps = (List) object;
41
				steps.remove(2);
41
				// steps.remove(10);
42
				field.setAccessible(false);
42
				// steps.remove(9);
43
				super.visit(graph);
43
				// steps.remove(8);
44
			} catch (SecurityException e) {
44
				// steps.remove(2);
45
				e.printStackTrace();
45
				field.setAccessible(false);
46
			} catch (NoSuchFieldException e) {
46
				super.visit(graph);
47
				e.printStackTrace();
47
			} catch (SecurityException e) {
48
			} catch (IllegalArgumentException e) {
48
				e.printStackTrace();
49
				e.printStackTrace();
49
			} catch (NoSuchFieldException e) {
50
			} catch (IllegalAccessException e) {
50
				e.printStackTrace();
51
				e.printStackTrace();
51
			} catch (IllegalArgumentException e) {
52
			}
52
				e.printStackTrace();
53
		}
53
			} catch (IllegalAccessException e) {
54
54
				e.printStackTrace();
55
	}
55
			}
56
56
		}
57
	public DirectedGraphLayoutAlgorithm(int styles) {
57
	}
58
		super(styles);
58
59
	}
59
	public static final int HORIZONTAL = 1;
60
60
61
	protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) {
61
	public static final int VERTICAL = 2;
62
		HashMap mapping = new HashMap(entitiesToLayout.length);
62
63
		DirectedGraph graph = new DirectedGraph();
63
	private int orientation = VERTICAL;
64
		for (int i = 0; i < entitiesToLayout.length; i++) {
64
65
			InternalNode internalNode = entitiesToLayout[i];
65
	private LayoutContext context;
66
			Node node = new Node(internalNode);
66
67
			node.setSize(new Dimension(10, 10));
67
	public DirectedGraphLayoutAlgorithm() {
68
			mapping.put(internalNode, node);
68
	}
69
			graph.nodes.add(node);
69
70
		}
70
	public DirectedGraphLayoutAlgorithm(int orientation) {
71
		for (int i = 0; i < relationshipsToConsider.length; i++) {
71
		if (orientation == VERTICAL)
72
			InternalRelationship relationship = relationshipsToConsider[i];
72
			this.orientation = orientation;
73
			Node source = (Node) mapping.get(relationship.getSource());
73
	}
74
			Node dest = (Node) mapping.get(relationship.getDestination());
74
75
			Edge edge = new Edge(relationship, source, dest);
75
	public int getOrientation() {
76
			graph.edges.add(edge);
76
		return orientation;
77
		}
77
	}
78
		DirectedGraphLayout directedGraphLayout = new ExtendedDirectedGraphLayout();
78
79
		directedGraphLayout.visit(graph);
79
	public void setOrientation(int orientation) {
80
80
		if (orientation == HORIZONTAL || orientation == VERTICAL)
81
		for (Iterator iterator = graph.nodes.iterator(); iterator.hasNext();) {
81
			this.orientation = orientation;
82
			Node node = (Node) iterator.next();
82
	}
83
			InternalNode internalNode = (InternalNode) node.data;
83
84
			// For horizontal layout transpose the x and y coordinates
84
	public void applyLayout(boolean clean) {
85
			if ((layout_styles & SWT.HORIZONTAL) == SWT.HORIZONTAL) {
85
		if (!clean)
86
				internalNode.setInternalLocation(node.y, node.x);
86
			return;
87
			}else {
87
		HashMap mapping = new HashMap();
88
				internalNode.setInternalLocation(node.x, node.y);
88
		DirectedGraph graph = new DirectedGraph();
89
			}
89
		EntityLayout[] entities = context.getEntities();
90
		}
90
		for (int i = 0; i < entities.length; i++) {
91
		updateLayoutLocations(entitiesToLayout);
91
			Node node = new Node(entities[i]);
92
	}
92
			node.setSize(new Dimension(10, 10));
93
93
			mapping.put(entities[i], node);
94
	protected int getCurrentLayoutStep() {
94
			graph.nodes.add(node);
95
		// TODO Auto-generated method stub
95
		}
96
		return 0;
96
		ConnectionLayout[] connections = context.getConnections();
97
	}
97
		for (int i = 0; i < connections.length; i++) {
98
98
			Node source = (Node) mapping.get(getEntity(connections[i].getSource()));
99
	protected int getTotalNumberOfLayoutSteps() {
99
			Node dest = (Node) mapping.get(getEntity(connections[i].getTarget()));
100
		// TODO Auto-generated method stub
100
			if (source != null && dest != null) {
101
		return 0;
101
				Edge edge = new Edge(connections[i], source, dest);
102
	}
102
				graph.edges.add(edge);
103
103
			}
104
	protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) {
104
		}
105
		// TODO Auto-generated method stub
105
		DirectedGraphLayout directedGraphLayout = new ExtendedDirectedGraphLayout();
106
		return true;
106
		directedGraphLayout.visit(graph);
107
	}
107
108
108
		for (Iterator iterator = graph.nodes.iterator(); iterator.hasNext();) {
109
	protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
109
			Node node = (Node) iterator.next();
110
		// TODO Auto-generated method stub
110
			EntityLayout entity = (EntityLayout) node.data;
111
111
			if (orientation == VERTICAL) {
112
	}
112
				entity.setLocation(node.x, node.y);
113
113
			} else {
114
	protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
114
				entity.setLocation(node.y, node.x);
115
		// TODO Auto-generated method stub
115
			}
116
116
		}
117
	}
117
	}
118
118
119
	public void setLayoutArea(double x, double y, double width, double height) {
119
	private EntityLayout getEntity(NodeLayout node) {
120
		// TODO Auto-generated method stub
120
		if (!node.isPruned())
121
121
			return node;
122
	}
122
		SubgraphLayout subgraph = node.getSubgraph();
123
123
		if (subgraph.isGraphEntity())
124
}
124
			return subgraph;
125
		return null;
126
	}
127
128
	public void setLayoutContext(LayoutContext context) {
129
		this.context = context;
130
	}
131
132
}
(-)src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java (-233 / +222 lines)
Lines 1-233 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
11
package org.eclipse.zest.layouts.algorithms;
12
12
13
import java.util.Arrays;
13
import org.eclipse.zest.layouts.LayoutAlgorithm;
14
14
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
15
import org.eclipse.zest.layouts.LayoutStyles;
15
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
16
import org.eclipse.zest.layouts.dataStructures.InternalNode;
16
import org.eclipse.zest.layouts.interfaces.EntityLayout;
17
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
17
import org.eclipse.zest.layouts.interfaces.LayoutContext;
18
18
19
19
20
/**
20
/**
21
 * @version 2.0
21
 * @version 2.0
22
 * @author Ian Bull
22
 * @author Ian Bull
23
 * @author Casey Best and Rob Lintern
23
 * @author Casey Best and Rob Lintern
24
 */
24
 */
25
public class GridLayoutAlgorithm extends AbstractLayoutAlgorithm {
25
public class GridLayoutAlgorithm implements LayoutAlgorithm {
26
	
26
	
27
	private static final double PADDING_PERCENTAGE = 0.95;
27
	private static final double PADDING_PERCENTAGE = 0.95;
28
28
	private static final int MIN_ENTITY_SIZE = 5;
29
	protected int rowPadding = 0;
29
30
	
30
	protected double aspectRatio = 1.0;
31
	public void setLayoutArea(double x, double y, double width, double height) {
31
	protected int rowPadding = 0;
32
		throw new RuntimeException("Operation not implemented");
32
	private boolean resize = false;
33
	}
33
	protected int rows, cols, numChildren;
34
	
34
	protected double colWidth, rowHeight, offsetX, offsetY;
35
    int rows, cols, numChildren; 
35
	protected double childrenHeight, childrenWidth;
36
    double colWidth, rowHeight, offsetX, offsetY;
36
37
    int totalProgress;
37
	private LayoutContext context;
38
    double h, w;
38
39
    
39
40
    /**
40
	public void setLayoutContext(LayoutContext context) {
41
     * Initializes the grid layout.
41
		this.context = context;
42
     * @param styles
42
	}
43
     * @see LayoutStyles
43
    
44
     */
44
	public void applyLayout(boolean clean) {
45
	public GridLayoutAlgorithm(int styles) {
45
		if (!clean)
46
		super(styles);
46
			return;
47
	}
47
		DisplayIndependentRectangle bounds = context.getBounds();
48
	
48
		calculateGrid(bounds);
49
	/**
49
		applyLayoutInternal(context.getEntities(), bounds);
50
	 * Inititalizes the grid layout with no style.
50
	}
51
	 */
51
52
	public GridLayoutAlgorithm() {
52
	/**
53
		this(LayoutStyles.NONE);
53
	 * Calculates all the dimensions of grid that layout entities will be fit
54
	}
54
	 * in. The following fields are set by this method: {@link #numChildren},
55
55
	 * {@link #rows}, {@link #cols}, {@link #colWidth}, {@link #rowHeight},
56
    
56
	 * {@link #offsetX}, {@link #offsetY}
57
    protected int getCurrentLayoutStep() {
57
	 * 
58
    	// TODO: This isn't right
58
	 * @param bounds
59
    	return 0;
59
	 */
60
    }
60
    protected void calculateGrid(DisplayIndependentRectangle bounds) {
61
    
61
		numChildren = context.getNodes().length;
62
    protected int getTotalNumberOfLayoutSteps() {
62
		int[] result = calculateNumberOfRowsAndCols(numChildren, bounds.x, bounds.y, bounds.width, bounds.height);
63
    	return totalProgress;
63
		cols = result[0];
64
    }
64
		rows = result[1];
65
65
		
66
    /**
66
		colWidth = bounds.width / cols;
67
     * 
67
		rowHeight = bounds.height / rows;
68
     */
68
		
69
	protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
69
		double [] nodeSize = calculateNodeSize (colWidth, rowHeight);
70
		
70
		childrenWidth = nodeSize[0];
71
		// TODO: Filter unwanted entities and relationships
71
		childrenHeight = nodeSize[1];
72
		//super.applyLayout (entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight);
72
		offsetX = (colWidth - childrenWidth) / 2.0; // half of the space between
73
		// now begin
73
													// columns
74
		numChildren = entitiesToLayout.length;
74
		offsetY = (rowHeight - childrenHeight) / 2.0; // half of the space
75
		if (numChildren < 1) return;
75
														// between rows
76
		
76
	}
77
		int[] colsAndRows = calculateNumberOfRowsAndCols(numChildren, x, y, width, height);
77
78
		cols = colsAndRows[0];
78
	/**
79
		rows = colsAndRows[1];
79
	 * Use this algorithm to layout the given entities and bounds. The entities
80
80
	 * will be placed in the same order as they are passed in, unless a
81
		totalProgress = rows + 2;
81
	 * comparator is supplied.
82
		fireProgressEvent (1, totalProgress);
82
	 * 
83
83
	 * @param entitiesToLayout
84
		// sort the entities
84
	 *            apply the algorithm to these entities
85
		if (comparator != null) {
85
	 * @param bounds
86
            Arrays.sort(entitiesToLayout, comparator);
86
	 *            the bounds in which the layout can place the entities.
87
		} else {
87
	 */
88
			Arrays.sort(entitiesToLayout);
88
	protected synchronized void applyLayoutInternal(EntityLayout[] entitiesToLayout, DisplayIndependentRectangle bounds) {
89
		}
89
		
90
		fireProgressEvent (2, totalProgress);
90
		int index = 0;
91
91
		for( int i = 0; i < rows; i++ ) {
92
		// Calculate row height and column width
92
			for( int j = 0; j < cols; j++ ) {
93
		colWidth = width/cols;	
93
				if( (i*cols + j) < numChildren ) {
94
		rowHeight = height/rows;	
94
					EntityLayout node = entitiesToLayout[index++];
95
95
					if (resize && node.isResizable())
96
		// Calculate amount to scale children
96
						node.setSize(Math.max(childrenWidth, MIN_ENTITY_SIZE), Math.max(childrenHeight, MIN_ENTITY_SIZE));
97
		double [] nodeSize = calculateNodeSize (colWidth, rowHeight);
97
					DisplayIndependentDimension size = node.getSize();
98
		w = nodeSize[0];
98
					double xmove = bounds.x + j * colWidth + offsetX + size.width / 2;
99
		h = nodeSize[1];
99
					double ymove = bounds.y + i * rowHeight + offsetY + size.height / 2;
100
		offsetX = (colWidth - w)/2.0; // half of the space between columns
100
					if (node.isMovable())
101
		offsetY = (rowHeight - h)/2.0; // half of the space between rows
101
						node.setLocation(xmove, ymove);
102
	}
102
				}
103
	
103
			}
104
	/**
104
		}
105
	 * Use this algorithm to layout the given entities, using the given relationships and bounds.
105
	}
106
	 * The entities will be placed in the same order as they are passed in, unless a comparator
106
107
	 * is supplied.  
107
	/**
108
	 * 
108
	 * Calculates and returns an array containing the number of columns, followed by the number of rows
109
	 * @param entitiesToLayout Apply the algorithm to these entities
109
	 */
110
	 * @param relationshipsToConsider Only consider these relationships when applying the algorithm.
110
	protected int[] calculateNumberOfRowsAndCols(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
111
	 * @param boundsX The left side of the bounds in which the layout can place the entities.
111
		if (aspectRatio == 1.0) {
112
	 * @param boundsY The top side of the bounds in which the layout can place the entities.
112
			return calculateNumberOfRowsAndCols_square (numChildren, boundX, boundY, boundWidth, boundHeight);
113
	 * @param boundsWidth The width of the bounds in which the layout can place the entities.
113
		} else {
114
	 * @param boundsHeight The height of the bounds in which the layout can place the entities.
114
			return calculateNumberOfRowsAndCols_rectangular (numChildren);
115
	 * @throws RuntimeException Thrown if entitiesToLayout doesn't contain all of the endpoints for each relationship in relationshipsToConsider
115
		}
116
	 */
116
	}
117
	protected synchronized void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) {
117
	
118
		
118
	protected int[] calculateNumberOfRowsAndCols_square (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
119
		int index = 0;
119
		int rows = Math.max (1, (int) Math.sqrt(numChildren * boundHeight/boundWidth));
120
		for( int i = 0; i < rows; i++ ) {
120
		int cols = Math.max (1, (int) Math.sqrt(numChildren * boundWidth/boundHeight));
121
			for( int j = 0; j < cols; j++ ) {
121
		
122
				if( (i*cols + j) < numChildren ) {
122
		// if space is taller than wide, adjust rows first
123
					// find new position for child
123
		if (boundWidth <= boundHeight) {
124
					double xmove = boundsX + j * colWidth + offsetX;
124
			//decrease number of rows and columns until just enough or not enough
125
					double ymove = boundsY + i * rowHeight + offsetY;
125
			while (rows*cols > numChildren) {
126
					InternalNode sn = entitiesToLayout[index++];
126
				if (rows > 1) 
127
					sn.setInternalLocation( xmove, ymove );
127
					rows--;
128
					sn.setInternalSize( Math.max(w, MIN_ENTITY_SIZE), Math.max(h, MIN_ENTITY_SIZE) );
128
				if (rows*cols > numChildren)
129
				}
129
					if (cols > 1) 
130
			}
130
						cols--;
131
			fireProgressEvent (2 + i, totalProgress);
131
			}			
132
		}	
132
			//increase number of rows and columns until just enough
133
		updateLayoutLocations(entitiesToLayout);
133
			while (rows*cols < numChildren) {
134
		fireProgressEvent (totalProgress, totalProgress);
134
				rows++;
135
	}
135
				if (rows*cols < numChildren)
136
	
136
					cols++;
137
	protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
137
			}
138
		
138
		} else {
139
	}
139
			//decrease number of rows and columns until just enough or not enough
140
140
			while (rows*cols > numChildren) {
141
	/**
141
				if (cols > 1) 
142
	 * Calculates and returns an array containing the number of columns, followed by the number of rows
142
					cols--;
143
	 */
143
				if (rows*cols > numChildren)
144
	protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
144
					if (rows > 1) 
145
		if (getEntityAspectRatio() == 1.0) {
145
						rows--;
146
			return calculateNumberOfRowsAndCols_square (numChildren, boundX, boundY, boundWidth, boundHeight);
146
			}			
147
		} else {
147
			//increase number of rows and columns until just enough
148
			return calculateNumberOfRowsAndCols_rectangular (numChildren);
148
			while (rows*cols < numChildren) {
149
		}
149
				cols++;
150
	}
150
				if (rows*cols < numChildren)
151
	
151
					rows++;
152
	protected int[] calculateNumberOfRowsAndCols_square (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
152
			}
153
		int rows = Math.max (1, (int) Math.sqrt(numChildren * boundHeight/boundWidth));
153
		}
154
		int cols = Math.max (1, (int) Math.sqrt(numChildren * boundWidth/boundHeight));
154
		int[] result = {cols, rows};
155
		
155
		return result;
156
		// if space is taller than wide, adjust rows first
156
	}
157
		if (boundWidth <= boundHeight) {
157
	
158
			//decrease number of rows and columns until just enough or not enough
158
	protected int [] calculateNumberOfRowsAndCols_rectangular (int numChildren) {
159
			while (rows*cols > numChildren) {
159
		int rows =  Math.max (1, (int)Math.ceil(Math.sqrt(numChildren)));
160
				if (rows > 1) 
160
		int cols = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren)));		
161
					rows--;
161
		int[] result = {cols, rows};
162
				if (rows*cols > numChildren)
162
		return result;
163
					if (cols > 1) 
163
	}
164
						cols--;
164
	
165
			}			
165
	
166
			//increase number of rows and columns until just enough
166
	protected double [] calculateNodeSize (double colWidth, double rowHeight) {
167
			while (rows*cols < numChildren) {
167
		double childW = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*colWidth);	
168
				rows++;
168
		double childH = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*(rowHeight - rowPadding));	
169
				if (rows*cols < numChildren)
169
		double whRatio = colWidth/rowHeight;
170
					cols++;
170
		if (whRatio < aspectRatio) {
171
			}
171
			childH = childW / aspectRatio;
172
		} else {
172
		} else {
173
			//decrease number of rows and columns until just enough or not enough
173
			childW = childH * aspectRatio;
174
			while (rows*cols > numChildren) {
174
		}
175
				if (cols > 1) 
175
		double [] result = {childW, childH};
176
					cols--;
176
		return result;
177
				if (rows*cols > numChildren)
177
	}
178
					if (rows > 1) 
178
179
						rows--;
179
	/**
180
			}			
180
	 * Sets the padding between rows in the grid
181
			//increase number of rows and columns until just enough
181
	 * 
182
			while (rows*cols < numChildren) {
182
	 * @param rowPadding
183
				cols++;
183
	 *            padding - should be greater than or equal to 0
184
				if (rows*cols < numChildren)
184
	 */
185
					rows++;
185
    public void setRowPadding(int rowPadding) {
186
			}
186
		if (rowPadding >= 0) {
187
		}
187
			this.rowPadding = rowPadding;
188
		int[] result = {cols, rows};
188
        }
189
		return result;
189
    }
190
	}
190
191
	
191
	/**
192
	protected int [] calculateNumberOfRowsAndCols_rectangular (int numChildren) {
192
	 * Sets the preferred aspect ratio for layout entities. The default aspect
193
		int rows =  Math.max (1, (int)Math.ceil(Math.sqrt(numChildren)));
193
	 * ratio is 1.
194
		int cols = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren)));		
194
	 * 
195
		int[] result = {cols, rows};
195
	 * @param aspectRatio
196
		return result;
196
	 *            aspect ratio - should be greater than 0
197
	}
197
	 */
198
	
198
	public void setAspectRatio(double aspectRatio) {
199
	
199
		if (aspectRatio > 0) {
200
	protected double [] calculateNodeSize (double colWidth, double rowHeight) {
200
			this.aspectRatio = aspectRatio;
201
		double childW = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*colWidth);	
201
		}
202
		double childH = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*(rowHeight - rowPadding));	
202
	}
203
		double whRatio = colWidth/rowHeight;
203
204
		if (whRatio < getEntityAspectRatio()) {
204
	/**
205
			childH = childW/getEntityAspectRatio();		
205
	 * 
206
		} else {
206
	 * @return true if this algorithm is set to resize elements
207
			childW = childH*getEntityAspectRatio();
207
	 */
208
		}
208
	public boolean isResizing() {
209
		double [] result = {childW, childH};
209
		return resize;
210
		return result;
210
	}
211
	}
211
212
	
212
	/**
213
	/**
213
	 * 
214
	 * Increases the padding between rows in the grid
214
	 * @param resizing
215
	 * @param rowPadding Value will not be set if less than 0.
215
	 *            true if this algorithm should resize elements (default is
216
	 */
216
	 *            false)
217
    public void setRowPadding(int rowPadding) {
217
	 */
218
        if (rowPadding < 0 ) {
218
	public void setResizing(boolean resizing) {
219
            return;
219
		resize = resizing;
220
        }
220
	}
221
        this.rowPadding = rowPadding;
221
222
    }
222
}
223
224
	protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {
225
		if ( asynchronous && continueous ) return false;
226
		else if ( asynchronous && !continueous ) return true;
227
		else if ( !asynchronous && continueous ) return false;
228
		else if ( !asynchronous && !continueous ) return true;
229
		
230
		return false;
231
	}
232
233
}
(-)src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java (-55 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 * Contributors:
9
 *     Ian Bull     - updated and modified
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import org.eclipse.zest.layouts.LayoutStyles;
14
15
/**
16
 * @version 2.0
17
 * @author Casey Best and Rob Lintern
18
 */
19
public class HorizontalLayoutAlgorithm extends GridLayoutAlgorithm {
20
21
	
22
	public HorizontalLayoutAlgorithm(int styles) {
23
		super(styles);
24
	}
25
26
	/**
27
	 * Horizontal Layout Algorithm constructor.  Sets the Style to none.
28
	 */
29
	public HorizontalLayoutAlgorithm() {
30
		this( LayoutStyles.NONE );
31
	}
32
	/**
33
	 * Calculates and returns an array containing the number of columns, followed by the number of rows
34
	 */
35
	protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
36
		int rows = 1;
37
		int cols = numChildren;
38
		int[] result = {cols, rows};
39
		return result;
40
	}
41
	
42
	protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {
43
		if ( asynchronous && continueous ) {
44
			return false;
45
		} else if ( asynchronous && !continueous ) {
46
			return true;
47
		} else if ( !asynchronous && continueous ) {
48
			return false;
49
		} else if ( !asynchronous && !continueous ) {
50
			return true;
51
		}
52
		
53
		return false;
54
	}
55
}
(-)src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java (-131 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import java.util.ArrayList;
14
import java.util.Collections;
15
import java.util.Comparator;
16
import java.util.Iterator;
17
import java.util.List;
18
19
import org.eclipse.zest.layouts.LayoutEntity;
20
import org.eclipse.zest.layouts.dataStructures.InternalNode;
21
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
22
23
/**
24
 * This layout shifts overlapping nodes to the right.
25
 * @author Ian Bull
26
 */
27
public class HorizontalShift extends AbstractLayoutAlgorithm {
28
29
	private static final double DELTA = 10;
30
	private static final double VSPACING = 2;
31
32
	public HorizontalShift(int styles) {
33
		super(styles);
34
	}
35
36
	protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider,
37
			double boundsX, double boundsY, double boundsWidth, double boundsHeight) {
38
		
39
		ArrayList row = new ArrayList();
40
		for ( int i =0; i < entitiesToLayout.length; i++) {
41
			addToRowList(entitiesToLayout[i], row);
42
		}
43
44
		int heightSoFar = 0;
45
		
46
		Collections.sort(row, new Comparator() {
47
48
			public int compare(Object arg0, Object arg1) {
49
				// TODO Auto-generated method stub
50
				List a0 = (List) arg0;
51
				List a1 = (List) arg1;
52
				LayoutEntity node0 = ((InternalNode)a0.get(0)).getLayoutEntity();
53
				LayoutEntity node1 = ((InternalNode)a1.get(0)).getLayoutEntity();
54
				return (int) (node0.getYInLayout() - (node1.getYInLayout()));
55
			}
56
			
57
		});
58
59
		Iterator iterator = row.iterator();
60
		while (iterator.hasNext() ) {
61
			List currentRow = (List) iterator.next();
62
			Collections.sort(currentRow, new Comparator() {
63
				public int compare(Object arg0, Object arg1) {
64
					return (int) (((InternalNode)arg1).getLayoutEntity().getYInLayout() - ((InternalNode)arg0).getLayoutEntity().getYInLayout());
65
				}
66
			});
67
			Iterator iterator2 = currentRow.iterator();
68
			int i = 0;
69
			int width = (int) ((boundsWidth / 2) - currentRow.size() * 75);
70
			
71
			heightSoFar += ((InternalNode)currentRow.get(0)).getLayoutEntity().getHeightInLayout() + VSPACING*8 ;
72
			while(iterator2.hasNext()) {
73
				InternalNode currentNode = (InternalNode) iterator2.next();
74
				
75
				double location = width + 10*++i;
76
				currentNode.setLocation(location , heightSoFar);
77
				width += currentNode.getLayoutEntity().getWidthInLayout();
78
			}
79
		}
80
	}
81
	
82
	
83
	private void addToRowList( InternalNode node, ArrayList list) {
84
		double  layoutY = node.getLayoutEntity().getYInLayout();
85
		
86
		for ( int i = 0; i < list.size(); i++ ) {
87
			List currentRow = (List) list.get(i);
88
			InternalNode currentRowNode = (InternalNode) currentRow.get(0);
89
			double currentRowY = currentRowNode.getLayoutEntity().getYInLayout();
90
			//double currentRowHeight = currentRowNode.getLayoutEntity().getHeightInLayout();
91
			if ( layoutY >= (currentRowY-DELTA) && layoutY <= currentRowY + DELTA ) {
92
				currentRow.add(node);
93
				//list.add(i, currentRow);
94
				return;
95
			}
96
		}
97
		List newRow = new ArrayList();
98
		newRow.add(node);
99
		list.add(newRow);
100
	}
101
102
	protected int getCurrentLayoutStep() {
103
		// TODO Auto-generated method stub
104
		return 0;
105
	}
106
107
	protected int getTotalNumberOfLayoutSteps() {
108
		// TODO Auto-generated method stub
109
		return 0;
110
	}
111
112
	protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) {
113
		// TODO Auto-generated method stub
114
		return true;
115
	}
116
117
	protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
118
		// TODO Auto-generated method stub
119
	}
120
121
	protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider,
122
			double x, double y, double width, double height) {
123
		// TODO Auto-generated method stub
124
125
	}
126
127
	public void setLayoutArea(double x, double y, double width, double height) {
128
		// TODO Auto-generated method stub
129
	}
130
}
131
(-)src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java (+103 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: The Chisel Group - initial API and implementation
8
 *               Mateusz Matela 
9
 *               Ian Bull
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import java.util.ArrayList;
14
import java.util.Collections;
15
import java.util.Comparator;
16
import java.util.Iterator;
17
import java.util.List;
18
19
import org.eclipse.zest.layouts.LayoutAlgorithm;
20
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
21
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
22
import org.eclipse.zest.layouts.interfaces.EntityLayout;
23
import org.eclipse.zest.layouts.interfaces.LayoutContext;
24
25
/**
26
 * This layout shifts overlapping nodes to the right.
27
 * 
28
 * @author Ian Bull
29
 */
30
public class HorizontalShiftAlgorithm implements LayoutAlgorithm {
31
32
	private static final double DELTA = 10;
33
34
	private static final double VSPACING = 16;
35
36
	private LayoutContext context;
37
38
	public void applyLayout(boolean clean) {
39
		if (!clean)
40
			return;
41
		ArrayList rowsList = new ArrayList();
42
		EntityLayout[] entities = context.getEntities();
43
44
		for (int i = 0; i < entities.length; i++) {
45
			addToRowList(entities[i], rowsList);
46
		}
47
48
		Collections.sort(rowsList, new Comparator() {
49
			public int compare(Object o1, Object o2) {
50
				List a0 = (List) o1;
51
				List a1 = (List) o2;
52
				EntityLayout entity0 = (EntityLayout) a0.get(0);
53
				EntityLayout entity1 = (EntityLayout) a1.get(0);
54
				return (int) (entity0.getLocation().y - entity1.getLocation().y);
55
			}
56
		});
57
58
		Comparator entityComparator = new Comparator() {
59
			public int compare(Object o1, Object o2) {
60
				return (int) (((EntityLayout) o1).getLocation().y - ((EntityLayout) o2).getLocation().y);
61
			}
62
		};
63
		DisplayIndependentRectangle bounds = context.getBounds();
64
		int heightSoFar = 0;
65
66
		for (Iterator iterator = rowsList.iterator(); iterator.hasNext();) {
67
			List currentRow = (List) iterator.next();
68
			Collections.sort(currentRow, entityComparator);
69
70
			int i = 0;
71
			int width = (int) (bounds.width / 2 - currentRow.size() * 75);
72
73
			heightSoFar += ((EntityLayout) currentRow.get(0)).getSize().height + VSPACING;
74
			for (Iterator iterator2 = currentRow.iterator(); iterator2.hasNext();) {
75
				EntityLayout entity = (EntityLayout) iterator2.next();
76
				DisplayIndependentDimension size = entity.getSize();
77
				entity.setLocation(width + 10 * ++i + size.width / 2, heightSoFar + size.height / 2);
78
				width += size.width;
79
			}
80
		}
81
	}
82
83
	public void setLayoutContext(LayoutContext context) {
84
		this.context = context;
85
	}
86
87
	private void addToRowList(EntityLayout entity, ArrayList rowsList) {
88
		double layoutY = entity.getLocation().y;
89
90
		for (Iterator iterator = rowsList.iterator(); iterator.hasNext();) {
91
			List currentRow = (List) iterator.next();
92
			EntityLayout currentRowEntity = (EntityLayout) currentRow.get(0);
93
			double currentRowY = currentRowEntity.getLocation().y;
94
			if (layoutY >= currentRowY - DELTA && layoutY <= currentRowY + DELTA) {
95
				currentRow.add(entity);
96
				return;
97
			}
98
		}
99
		List newRow = new ArrayList();
100
		newRow.add(entity);
101
		rowsList.add(newRow);
102
	}
103
}
(-)src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java (-68 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import org.eclipse.zest.layouts.LayoutStyles;
14
import org.eclipse.zest.layouts.dataStructures.InternalNode;
15
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
16
17
18
19
20
/**
21
 * A simple algorithm to arrange graph nodes in a layered horizontal tree-like layout. 
22
 * @see TreeLayoutAlgorithm
23
 * 
24
 * @version  1.0
25
 * @author   Rob Lintern
26
 */
27
public class HorizontalTreeLayoutAlgorithm extends TreeLayoutAlgorithm {
28
29
	
30
	/**
31
	 * Creates a horizontal tree layout with no style
32
	 */
33
	public HorizontalTreeLayoutAlgorithm() {
34
		this( LayoutStyles.NONE );
35
	}
36
	
37
    /**
38
     * 
39
     */
40
    public HorizontalTreeLayoutAlgorithm( int styles ) {
41
        super( styles );
42
    }
43
    
44
    protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
45
        // NOTE: width and height are swtiched here when calling super method
46
        super.preLayoutAlgorithm(entitiesToLayout, relationshipsToConsider, x, y, height, width); 
47
    }
48
    
49
    protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
50
        // swap x->y and width->height
51
        for (int i = 0; i < entitiesToLayout.length; i++) {
52
            InternalNode entity = entitiesToLayout[i];
53
			entity.setInternalLocation(entity.getInternalY(), entity.getInternalX() );
54
			entity.setInternalSize( entity.getInternalWidth(), entity.getInternalHeight() );
55
        }
56
    	super.postLayoutAlgorithm(entitiesToLayout, relationshipsToConsider);
57
    }
58
    
59
    protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {
60
		if ( asynchronous && continueous ) return false;
61
		else if ( asynchronous && !continueous ) return true;
62
		else if ( !asynchronous && continueous ) return false;
63
		else if ( !asynchronous && !continueous ) return true;
64
		
65
		return false;
66
    }
67
68
}
(-)src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java (-149 / +101 lines)
Lines 1-149 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
11
package org.eclipse.zest.layouts.algorithms;
12
12
13
import java.util.Iterator;
13
import org.eclipse.zest.layouts.LayoutAlgorithm;
14
import java.util.List;
14
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
15
15
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
16
import org.eclipse.zest.layouts.LayoutStyles;
16
import org.eclipse.zest.layouts.interfaces.EntityLayout;
17
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
17
import org.eclipse.zest.layouts.interfaces.LayoutContext;
18
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
18
19
import org.eclipse.zest.layouts.dataStructures.InternalNode;
19
/**
20
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
20
 * This layout will take the given entities, apply a tree layout to them, and
21
21
 * then display the tree in a circular fashion with the roots in the center.
22
22
 * 
23
/**
23
 * @author Casey Best
24
 * This layout will take the given entities, apply a tree layout to them, and then display the 
24
 * @auhtor Rob Lintern
25
 * tree in a circular fashion with the roots in the center.
25
 */
26
 * 
26
public class RadialLayoutAlgorithm implements LayoutAlgorithm {
27
 * @author Casey Best
27
28
 * @auhtor Rob Lintern
28
	private static final double MAX_DEGREES = Math.PI * 2;
29
 */
29
	private double startDegree = 0;
30
public class RadialLayoutAlgorithm extends TreeLayoutAlgorithm {
30
	private double endDegree = MAX_DEGREES;
31
	private static final double MAX_DEGREES = Math.PI * 2;
31
32
	private double startDegree;
32
	private LayoutContext context;
33
	private double endDegree;
33
	private boolean resize = false;
34
	private TreeLayoutAlgorithm treeLayout;
34
35
	private List roots;
35
	private TreeLayoutAlgorithm treeLayout = new TreeLayoutAlgorithm();
36
	
36
	
37
	
37
	public void applyLayout(boolean clean) {
38
	/**
38
		if (!clean)
39
	 * Creates a radial layout with no style.
39
			return;
40
	 */
40
		treeLayout.internalApplyLayout();
41
	public RadialLayoutAlgorithm() {
41
		EntityLayout[] entities = context.getEntities();
42
		this ( LayoutStyles.NONE );
42
		DisplayIndependentRectangle bounds = context.getBounds();
43
	}
43
		computeRadialPositions(entities, bounds);
44
	
44
		if (resize)
45
	//TODO: This is a really strange pattern.  It extends tree layout and it contains a tree layout ? 
45
			AlgorithmHelper.maximizeSizes(entities);
46
	public RadialLayoutAlgorithm ( int styles ) {
46
		int insets = 4;
47
		super( styles );
47
		bounds.x += insets;
48
		treeLayout = new TreeLayoutAlgorithm( styles );
48
		bounds.y += insets;
49
		startDegree = 0;
49
		bounds.width -= 2 * insets;
50
		endDegree = MAX_DEGREES;
50
		bounds.height -= 2 * insets;
51
	}
51
		AlgorithmHelper.fitWithinBounds(entities, bounds, resize);
52
	
52
	}
53
	public void setLayoutArea(double x, double y, double width, double height) {
53
54
		throw new RuntimeException("Operation not implemented");
54
	private void computeRadialPositions(EntityLayout[] entities, DisplayIndependentRectangle bounds) {
55
	}
55
		DisplayIndependentRectangle layoutBounds = AlgorithmHelper.getLayoutBounds(entities, false);
56
	
56
		layoutBounds.x = bounds.x;
57
	
57
		layoutBounds.width = bounds.width;
58
	protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {		
58
		for (int i = 0; i < entities.length; i++) {
59
		if ( asynchronous && continueous ) return false;
59
			DisplayIndependentPoint location = entities[i].getLocation();
60
		else if ( asynchronous && !continueous ) return true;
60
			double percenttheta = (location.x - layoutBounds.x) / layoutBounds.width;
61
		else if ( !asynchronous && continueous ) return false;
61
			double distance = (location.y - layoutBounds.y) / layoutBounds.height;
62
		else if ( !asynchronous && !continueous ) return true;
62
			double theta = startDegree + Math.abs(endDegree - startDegree) * percenttheta;
63
		
63
			location.x = distance * Math.cos(theta);
64
		return false;	
64
			location.y = distance * Math.sin(theta);
65
	}
65
			entities[i].setLocation(location.x, location.y);
66
	
66
		}
67
67
	}
68
	DisplayIndependentRectangle layoutBounds = null;
68
69
	protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
69
	public void setLayoutContext(LayoutContext context) {
70
		// TODO Auto-generated method stub
70
		this.context = context;
71
		layoutBounds = new DisplayIndependentRectangle(x, y, width, height);
71
		treeLayout.setLayoutContext(context);
72
		super.preLayoutAlgorithm(entitiesToLayout, relationshipsToConsider, x, y,
72
	}
73
				width, height);
73
74
	}
74
	/**
75
	
75
	 * Set the range the radial layout will use when applyLayout is called. Both
76
	protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
76
	 * values must be in radians.
77
		roots = treeLayout.getRoots();
77
	 */
78
		computeRadialPositions (entitiesToLayout, layoutBounds);
78
	public void setRangeToLayout(double startDegree, double endDegree) {
79
		
79
		this.startDegree = startDegree;
80
		defaultFitWithinBounds(entitiesToLayout, layoutBounds);
80
		this.endDegree = endDegree;
81
		
81
	}
82
		super.postLayoutAlgorithm(entitiesToLayout, relationshipsToConsider);
82
83
83
	/**
84
	}
84
	 * 
85
	
85
	 * @return true if this algorithm is set to resize elements
86
	/**
86
	 */
87
	 * Set the range the radial layout will use when applyLayout is called.
87
	public boolean isResizing() {
88
	 * Both values must be in radians.
88
		return resize;
89
	 */
89
	}
90
	public void setRangeToLayout (double startDegree, double endDegree) {
90
91
	    this.startDegree = startDegree;
91
	/**
92
	    this.endDegree = endDegree;
92
	 * 
93
	}
93
	 * @param resizing
94
	
94
	 *            true if this algorithm should resize elements (default is
95
	/**
95
	 *            false)
96
	 * Take the tree and make it round.  This is done by determining the location of each entity in terms
96
	 */
97
	 * of its percentage in the tree layout.  Then apply that percentage to the radius and distance from
97
	public void setResizing(boolean resizing) {
98
	 * the center.
98
		resize = resizing;
99
	 */
99
		treeLayout.setResizing(resize);
100
	protected void computeRadialPositions (InternalNode[] entities, DisplayIndependentRectangle bounds2) { //TODO TODO TODO
100
	}
101
		DisplayIndependentRectangle bounds = new DisplayIndependentRectangle(getLayoutBounds(entities, true));
101
}
102
		bounds.height = bounds2.height;
103
		bounds.y = bounds2.y;
104
        for (int i = 0; i < entities.length; i++) {
105
            InternalNode entity = entities[i];
106
			double percentTheta = (entity.getInternalX() - bounds.x) / bounds.width;
107
			double distance = (entity.getInternalY() - bounds.y) / bounds.height;
108
			double theta = startDegree + Math.abs(endDegree - startDegree) * percentTheta;
109
			double newX = distance * Math.cos (theta);
110
			double newY = distance * Math.sin (theta);
111
			
112
			entity.setInternalLocation( newX, newY );
113
		}
114
	}
115
	
116
	/**
117
	 * Find the bounds in which the nodes are located.  Using the bounds against the real bounds
118
	 * of the screen, the nodes can proportionally be placed within the real bounds.
119
	 * The bounds can be determined either including the size of the nodes or not.  If the size
120
	 * is not included, the bounds will only be guaranteed to include the center of each node.
121
	 */
122
	protected DisplayIndependentRectangle getLayoutBounds (InternalNode[] entitiesToLayout, boolean includeNodeSize) {
123
		DisplayIndependentRectangle layoutBounds = super.getLayoutBounds(entitiesToLayout, includeNodeSize);
124
		DisplayIndependentPoint centerPoint = (roots != null) ? determineCenterPoint(roots) : 
125
		    new DisplayIndependentPoint (layoutBounds.x + layoutBounds.width / 2, layoutBounds.y + layoutBounds.height / 2);
126
		//	The center entity is determined in applyLayout
127
		double maxDistanceX = Math.max( 
128
				Math.abs (layoutBounds.x + layoutBounds.width - centerPoint.x),
129
				Math.abs (centerPoint.x - layoutBounds.x));
130
		double maxDistanceY = Math.max( 
131
				Math.abs (layoutBounds.y + layoutBounds.height - centerPoint.y),
132
				Math.abs (centerPoint.y - layoutBounds.y));
133
		layoutBounds = new DisplayIndependentRectangle (centerPoint.x - maxDistanceX, centerPoint.y - maxDistanceY, maxDistanceX * 2, maxDistanceY * 2);
134
		return layoutBounds;
135
	}
136
	
137
	/**
138
	 * Find the center point between the roots
139
	 */
140
	private DisplayIndependentPoint determineCenterPoint (List roots) {
141
		double totalX = 0, totalY = 0;
142
		for ( Iterator iterator = roots.iterator(); iterator.hasNext(); ) {
143
			InternalNode entity = (InternalNode)iterator.next();
144
			totalX += entity.getInternalX();
145
			totalY += entity.getInternalY();
146
		}
147
		return new DisplayIndependentPoint (totalX / roots.size(), totalY / roots.size());
148
	}
149
}
(-)src/org/eclipse/zest/layouts/algorithms/SpaceTreeLayoutAlgorithm.java (+1204 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: The Chisel Group - initial API and implementation
8
 *               Mateusz Matela 
9
 *               Ian Bull
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import java.util.ArrayList;
14
import java.util.Arrays;
15
import java.util.Collections;
16
import java.util.Comparator;
17
import java.util.Iterator;
18
import java.util.LinkedList;
19
import java.util.List;
20
import java.util.ListIterator;
21
22
import org.eclipse.zest.layouts.LayoutAlgorithm;
23
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
24
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
25
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
26
import org.eclipse.zest.layouts.interfaces.ContextListener;
27
import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager;
28
import org.eclipse.zest.layouts.interfaces.LayoutContext;
29
import org.eclipse.zest.layouts.interfaces.LayoutListener;
30
import org.eclipse.zest.layouts.interfaces.NodeLayout;
31
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
32
33
/**
34
 * Layout algorithm implementing SpaceTree. It assumes that nodes in the layout
35
 * context make a tree structure.
36
 * 
37
 * It expands and collapses nodes to optimize use of available space. In order
38
 * to keep the tree structure clearly visible, it also keeps track of the nodes'
39
 * positions to makes sure they stay in their current layer and don't overlap
40
 * with each other.
41
 * 
42
 * It's recommended to set an <code>ExpandCollapseManger</code> returned by
43
 * {@link #getExpandCollapseManager()} in the <code>LayoutContext</code> that
44
 * will be used with this layout algorithm. Other
45
 * <code>ExpandCollapseManager</code>s could also work, but the algorithm's
46
 * functionality would be very limited.
47
 */
48
public class SpaceTreeLayoutAlgorithm implements LayoutAlgorithm {
49
50
	/**
51
	 * Tree direction constant for which root is placed at the top and branches
52
	 * spread downwards
53
	 */
54
	public final static int TOP_DOWN = 1;
55
56
	/**
57
	 * Tree direction constant for which root is placed at the bottom and
58
	 * branches spread upwards
59
	 */
60
	public final static int BOTTOM_UP = 2;
61
62
	/**
63
	 * Tree direction constant for which root is placed at the left and branches
64
	 * spread to the right
65
	 */
66
	public final static int LEFT_RIGHT = 3;
67
68
	/**
69
	 * Tree direction constant for which root is placed at the right and
70
	 * branches spread to the left
71
	 */
72
	public final static int RIGHT_LEFT = 4;
73
74
	private class SpaceTreeNode extends TreeLayoutObserver.TreeNode {
75
		public SubgraphLayout subgraph = null;
76
77
		public boolean expanded = false;
78
		public double positionInLayer;
79
80
		public SpaceTreeNode(NodeLayout node, TreeLayoutObserver owner) {
81
			super(node, owner);
82
		}
83
84
		protected void addChild(TreeLayoutObserver.TreeNode child) {
85
			super.addChild(child);
86
87
			SpaceTreeNode child2 = (SpaceTreeNode) child;
88
			child2.expanded = false;
89
			child2.setSubgraph(null);
90
91
			if (child.depth >= 0)
92
				((SpaceTreeLayer) spaceTreeLayers.get(child.depth)).removeNode(child2);
93
94
			if (expanded) {
95
				child.depth = this.depth + 1;
96
97
				SpaceTreeLayer childLayer;
98
				if (child.depth < spaceTreeLayers.size())
99
					childLayer = ((SpaceTreeLayer) spaceTreeLayers.get(child.depth));
100
				else
101
					spaceTreeLayers.add(childLayer = new SpaceTreeLayer(child.depth));
102
103
				if (childLayer.nodes.isEmpty())
104
					child.order = 0;
105
				else
106
					child.order = ((SpaceTreeNode) childLayer.nodes.get(childLayer.nodes.size() - 1)).order + 1;
107
				childLayer.addNodes(Arrays.asList(new Object[] { child }));
108
			}
109
		}
110
111
		public void precomputeTree() {
112
			super.precomputeTree();
113
			if (this == owner.getSuperRoot()) {
114
				expanded = true;
115
				while (spaceTreeLayers.size() <= this.height)
116
					spaceTreeLayers.add(new SpaceTreeLayer(spaceTreeLayers.size()));
117
118
				if (treeObserver != null)
119
					refreshLayout(true);
120
			}
121
		}
122
123
		public SubgraphLayout collapseAllChildrenIntoSubgraph(SubgraphLayout subgraph, boolean includeYourself) {
124
			expanded = false;
125
			ArrayList allChildren = new ArrayList();
126
			LinkedList nodesToVisit = new LinkedList();
127
			nodesToVisit.addLast(this);
128
			while (!nodesToVisit.isEmpty()) {
129
				SpaceTreeNode currentNode = (SpaceTreeNode) nodesToVisit.removeFirst();
130
				for (Iterator iterator = currentNode.children.iterator(); iterator.hasNext();) {
131
					SpaceTreeNode child = (SpaceTreeNode) iterator.next();
132
					allChildren.add(child.node);
133
					child.setSubgraph(null);
134
					child.expanded = false;
135
					nodesToVisit.addLast(child);
136
				}
137
			}
138
			if (includeYourself)
139
				allChildren.add(this.node);
140
			if (allChildren.isEmpty()) {
141
				setSubgraph(null);
142
				return null;
143
			}
144
			NodeLayout[] childrenArray = (NodeLayout[]) allChildren.toArray(new NodeLayout[allChildren.size()]);
145
			if (subgraph == null) {
146
				subgraph = context.createSubgraph(childrenArray);
147
				subgraph.setDirection(getSubgraphDirection());
148
			} else {
149
				subgraph.addNodes(childrenArray);
150
			}
151
			if (!includeYourself)
152
				setSubgraph(subgraph);
153
			return subgraph;
154
		}
155
156
		public void setSubgraph(SubgraphLayout subgraph) { // !
157
			if (this.subgraph != subgraph) {
158
				this.subgraph = subgraph;
159
				refreshSubgraphLocation();
160
			}
161
		}
162
163
		/**
164
		 * Moves the node back to its layer, as close as possible to given
165
		 * preferred location
166
		 * 
167
		 * @param preferredLocation
168
		 */
169
		public void adjustPosition(DisplayIndependentPoint preferredLocation) { // !
170
			protectedNode = (SpaceTreeNode) owner.getSuperRoot();
171
172
			double newPositionInLayer = (direction == BOTTOM_UP || direction == TOP_DOWN) ? preferredLocation.x : preferredLocation.y;
173
			if (((SpaceTreeNode) parent).expanded) {
174
				((SpaceTreeLayer) spaceTreeLayers.get(depth)).moveNode(this, newPositionInLayer);
175
				centerParentsTopDown();
176
			}
177
		}
178
179
		public void refreshSubgraphLocation() {
180
			if (subgraph != null && subgraph.isGraphEntity()) {
181
				DisplayIndependentPoint nodeLocation = node.getLocation();
182
				DisplayIndependentDimension nodeSize = node.getSize();
183
				DisplayIndependentDimension subgraphSize = subgraph.getSize();
184
				double x = 0, y = 0;
185
				switch (direction) {
186
				case TOP_DOWN:
187
					x = nodeLocation.x;
188
					y = nodeLocation.y + (nodeSize.height + subgraphSize.height) / 2;
189
					break;
190
				case BOTTOM_UP:
191
					x = nodeLocation.x;
192
					y = nodeLocation.y - (nodeSize.height + subgraphSize.height) / 2;
193
					break;
194
				case LEFT_RIGHT:
195
					x = nodeLocation.x + (nodeSize.width + subgraphSize.width) / 2;
196
					y = nodeLocation.y;
197
					break;
198
				case RIGHT_LEFT:
199
					x = nodeLocation.x - (nodeSize.width + subgraphSize.height) / 2;
200
					y = nodeLocation.y;
201
					break;
202
				}
203
				subgraph.setLocation(x, y);
204
			}
205
			((SpaceTreeLayer) spaceTreeLayers.get(depth)).refreshThickness();
206
		}
207
208
		public double spaceRequiredForNode() {
209
			if (node == null)
210
				return 0;
211
			switch (direction) {
212
			case TOP_DOWN:
213
			case BOTTOM_UP:
214
				return node.getSize().width;
215
			case LEFT_RIGHT:
216
			case RIGHT_LEFT:
217
				return node.getSize().height;
218
			}
219
			throw new RuntimeException("invalid direction");
220
		}
221
222
		public double spaceRequiredForChildren() {
223
			if (children.isEmpty())
224
				return 0;
225
			double result = 0;
226
			for (Iterator iterator = children.iterator(); iterator.hasNext();) {
227
				SpaceTreeNode child = (SpaceTreeNode) iterator.next();
228
				result += child.spaceRequiredForNode();
229
			}
230
			result += leafGap * (children.size() - 1);
231
			return result;
232
		}
233
234
		/**
235
		 * Checks if nodes in given list have proper positions according to
236
		 * their children (a parent's position cannot be smaller than its first
237
		 * child's position nor bigger than its last child's position). If not,
238
		 * it tries to fix them.
239
		 * 
240
		 * @param nodesToCheck
241
		 * @return true if all locations are correct or could be corrected while
242
		 *         checking.
243
		 */
244
		public boolean childrenPositionsOK(ArrayList nodesToCheck) {
245
			for (Iterator iterator = nodesToCheck.iterator(); iterator.hasNext();) {
246
				SpaceTreeNode node = (SpaceTreeNode) iterator.next();
247
				if (node.depth < 0 || node.children.isEmpty())
248
					continue;
249
				SpaceTreeNode child = ((SpaceTreeNode) node.children.get(0));
250
				if (child.positionInLayer > node.positionInLayer) {
251
					((SpaceTreeLayer) spaceTreeLayers.get(node.depth)).moveNode(node, child.positionInLayer);
252
					if (child.positionInLayer > node.positionInLayer) {
253
						((SpaceTreeLayer) spaceTreeLayers.get(child.depth)).moveNode(child, node.positionInLayer);
254
						if (child.positionInLayer > node.positionInLayer) {
255
							return false;
256
						}
257
					}
258
				}
259
				child = ((SpaceTreeNode) node.children.get(node.children.size() - 1));
260
				if (child.positionInLayer < node.positionInLayer) {
261
					((SpaceTreeLayer) spaceTreeLayers.get(node.depth)).moveNode(node, child.positionInLayer);
262
					if (child.positionInLayer < node.positionInLayer) {
263
						((SpaceTreeLayer) spaceTreeLayers.get(child.depth)).moveNode(child, node.positionInLayer);
264
						if (child.positionInLayer < node.positionInLayer) {
265
							return false;
266
						}
267
					}
268
				}
269
			}
270
			return true;
271
		}
272
273
		public void centerParentsBottomUp() {
274
			if (!children.isEmpty() && expanded) {
275
				for (Iterator iterator = children.iterator(); iterator.hasNext();) {
276
					((SpaceTreeNode) iterator.next()).centerParentsBottomUp();
277
				}
278
279
				if (depth >= 0) {
280
					SpaceTreeNode firstChild = (SpaceTreeNode) children.get(0);
281
					SpaceTreeNode lastChild = (SpaceTreeNode) children.get(children.size() - 1);
282
					SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(depth);
283
					layer.moveNode(this, (firstChild.positionInLayer + lastChild.positionInLayer) / 2);
284
				}
285
			}
286
		}
287
288
		public void centerParentsTopDown() {
289
			if (this == owner.getSuperRoot()) {
290
				this.positionInLayer = getAvailableSpace() / 2;
291
			}
292
			if (!children.isEmpty() && expanded) {
293
				SpaceTreeNode firstChild = (SpaceTreeNode) children.get(0);
294
				SpaceTreeNode lastChild = (SpaceTreeNode) children.get(children.size() - 1);
295
				double offset = this.positionInLayer - (firstChild.positionInLayer + lastChild.positionInLayer) / 2;
296
				if (firstChild.positionInLayer - firstChild.spaceRequiredForNode() / 2 + offset < 0)
297
					offset = -firstChild.positionInLayer + firstChild.spaceRequiredForNode() / 2;
298
				double availableSpace = getAvailableSpace();
299
				if (lastChild.positionInLayer + lastChild.spaceRequiredForNode() / 2 + offset > availableSpace) {
300
					offset = availableSpace - lastChild.positionInLayer - lastChild.spaceRequiredForNode() / 2;
301
				}
302
				SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(depth + 1);
303
				layer.fitNodesWithinBounds(children, firstChild.positionInLayer + offset, lastChild.positionInLayer + offset);
304
305
				for (Iterator iterator = children.iterator(); iterator.hasNext();) {
306
					((SpaceTreeNode) iterator.next()).centerParentsTopDown();
307
				}
308
			}
309
		}
310
311
		public void flushExpansionChanges() {
312
			if (node != null)
313
				node.prune(null);
314
			if (this.expanded) {
315
				setSubgraph(null);
316
				for (Iterator iterator = children.iterator(); iterator.hasNext();) {
317
					((SpaceTreeNode) iterator.next()).flushExpansionChanges();
318
				}
319
			}
320
			if (!this.expanded && subgraph == null) {
321
				collapseAllChildrenIntoSubgraph(null, false);
322
			}
323
		}
324
325
		public boolean flushCollapseChanges() {
326
			if (!expanded) {
327
				int numberOfChildrenInSubgraph = subgraph == null ? 0 : subgraph.countNodes();
328
				collapseAllChildrenIntoSubgraph(subgraph, false);
329
				int newNumberOfChildrenInSubgraph = (subgraph == null ? 0 : subgraph.countNodes());
330
				if (numberOfChildrenInSubgraph != newNumberOfChildrenInSubgraph && newNumberOfChildrenInSubgraph > 0)
331
					refreshSubgraphLocation();
332
				return numberOfChildrenInSubgraph != newNumberOfChildrenInSubgraph;
333
			}
334
			if (expanded && subgraph == null) {
335
				boolean madeChagnes = false;
336
				for (Iterator iterator = children.iterator(); iterator.hasNext();) {
337
					madeChagnes = ((SpaceTreeNode) iterator.next()).flushCollapseChanges() || madeChagnes;
338
				}
339
				return madeChagnes;
340
			}
341
			return false;
342
		}
343
344
		/**
345
		 * Sets locations of nodes in the graph depending on their current layer
346
		 * and position in layer.
347
		 * 
348
		 * @param thicknessSoFar
349
		 *            sum of thicknesses and gaps for all layers 'above' this
350
		 *            node (should be 0 if called on superRoot)
351
		 * @return true if location of at least one node has changed
352
		 */
353
		public boolean flushLocationChanges(double thicknessSoFar) {
354
			boolean madeChanges = false;
355
			if (node != null) {
356
				DisplayIndependentDimension nodeSize = node.getSize();
357
				double x = 0, y = 0;
358
				switch (direction) {
359
				case TOP_DOWN:
360
					x = bounds.x + positionInLayer;
361
					y = thicknessSoFar + nodeSize.height / 2;
362
					break;
363
				case BOTTOM_UP:
364
					x = bounds.x + positionInLayer;
365
					y = bounds.y + bounds.height - thicknessSoFar - nodeSize.height / 2;
366
					break;
367
				case LEFT_RIGHT:
368
					x = thicknessSoFar + nodeSize.height / 2;
369
					y = bounds.y + positionInLayer;
370
					break;
371
				case RIGHT_LEFT:
372
					x = bounds.x + bounds.width - thicknessSoFar - nodeSize.height / 2;
373
					y = bounds.y + positionInLayer;
374
					break;
375
				}
376
				DisplayIndependentPoint currentLocation = node.getLocation();
377
				if (currentLocation.x != x || currentLocation.y != y) {
378
					node.setLocation(x, y);
379
					refreshSubgraphLocation();
380
					madeChanges = true;
381
				}
382
			}
383
			if (expanded && subgraph == null) {
384
				thicknessSoFar += (depth >= 0 ? ((SpaceTreeLayer) spaceTreeLayers.get(depth)).thickness : 0) + layerGap;
385
				for (Iterator iterator = children.iterator(); iterator.hasNext();) {
386
					SpaceTreeNode child = (SpaceTreeNode) iterator.next();
387
					madeChanges = child.flushLocationChanges(thicknessSoFar) || madeChanges;
388
				}
389
			}
390
			return madeChanges;
391
		}
392
393
		public String toString() {
394
			StringBuffer sb = new StringBuffer();
395
			for (int i = 0; i < depth; i++)
396
				sb.append(" ");
397
			if (node != null)
398
				sb.append(node.toString());
399
			sb.append("|" + this.order);
400
			sb.append('\n');
401
			for (Iterator iterator = children.iterator(); iterator.hasNext();) {
402
				SpaceTreeNode child = (SpaceTreeNode) iterator.next();
403
				sb.append(child.toString());
404
			}
405
			return sb.toString();
406
		}
407
	}
408
409
	private TreeLayoutObserver.TreeNodeFactory spaceTreeNodeFactory = new TreeLayoutObserver.TreeNodeFactory() {
410
		public TreeLayoutObserver.TreeNode createTreeNode(NodeLayout nodeLayout, TreeLayoutObserver observer) {
411
			return new SpaceTreeNode(nodeLayout, observer);
412
		};
413
	};
414
415
	private class SpaceTreeLayer {
416
		public ArrayList nodes = new ArrayList();
417
		private final int depth;
418
		public double thickness = 0;
419
420
		public SpaceTreeLayer(int depth) {
421
			this.depth = depth;
422
		}
423
424
		public void addNodes(List nodesToAdd) {
425
			ListIterator layerIterator = nodes.listIterator();
426
			SpaceTreeNode previousNode = null;
427
			for (Iterator iterator = nodesToAdd.iterator(); iterator.hasNext();) {
428
				SpaceTreeNode nodeToAdd = (SpaceTreeNode) iterator.next();
429
430
				SpaceTreeNode nodeInLayer = null;
431
				while (layerIterator.hasNext()) {
432
					nodeInLayer = (SpaceTreeNode) layerIterator.next();
433
					if (nodeInLayer.order >= nodeToAdd.order)
434
						break;
435
					double expectedPostion = (previousNode == null) ? 0 : previousNode.positionInLayer + expectedDistance(previousNode, nodeInLayer);
436
					nodeInLayer.positionInLayer = Math.max(nodeInLayer.positionInLayer, expectedPostion);
437
					previousNode = nodeInLayer;
438
				}
439
440
				if (nodeInLayer == null) {
441
					layerIterator.add(nodeToAdd);
442
				} else if (nodeInLayer.order == nodeToAdd.order) {
443
					layerIterator.set(nodeToAdd);
444
				} else {
445
					if (nodeInLayer.order > nodeToAdd.order)
446
						layerIterator.previous();
447
					layerIterator.add(nodeToAdd);
448
				}
449
				layerIterator.previous();
450
			}
451
			// move the rest of nodes so that they don't overlap
452
			while (layerIterator.hasNext()) {
453
				SpaceTreeNode nodeInLayer = (SpaceTreeNode) layerIterator.next();
454
				double expectedPostion = (previousNode == null) ? 0 : previousNode.positionInLayer + expectedDistance(previousNode, nodeInLayer);
455
				nodeInLayer.positionInLayer = Math.max(nodeInLayer.positionInLayer, expectedPostion);
456
				previousNode = nodeInLayer;
457
			}
458
459
			refreshThickness();
460
		}
461
462
		public void removeNode(SpaceTreeNode node) {
463
			if (nodes.remove(node)) {
464
				((SpaceTreeLayer) spaceTreeLayers.get(depth + 1)).removeNodes(node.children);
465
				refreshThickness();
466
			}
467
		}
468
469
		public void removeNodes(List nodesToRemove) {
470
			if (this.nodes.removeAll(nodesToRemove)) {
471
				SpaceTreeLayer nextLayer = ((SpaceTreeLayer) spaceTreeLayers.get(depth + 1));
472
				for (Iterator iterator = nodesToRemove.iterator(); iterator.hasNext();) {
473
					SpaceTreeNode nodeToRemove = (SpaceTreeNode) iterator.next();
474
					nextLayer.removeNodes(nodeToRemove.children);
475
				}
476
				refreshThickness();
477
			}
478
		}
479
480
		public void checkThickness(SpaceTreeNode node) {
481
			double nodeThickness = 0;
482
			DisplayIndependentDimension size = node.node.getSize();
483
			nodeThickness = (direction == TOP_DOWN || direction == BOTTOM_UP) ? size.height : size.width;
484
			if (node.subgraph != null && node.subgraph.isGraphEntity()) {
485
				size = node.subgraph.getSize();
486
				nodeThickness += (direction == TOP_DOWN || direction == BOTTOM_UP) ? size.height : size.width;
487
			}
488
			this.thickness = Math.max(this.thickness, nodeThickness);
489
		}
490
491
		public void refreshThickness() {
492
			this.thickness = 0;
493
			for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
494
				checkThickness((SpaceTreeNode) iterator.next());
495
			}
496
		}
497
498
		public void fitNodesWithinBounds(List nodeList, double startPosition, double endPosition) {
499
			NodeSnapshot[][] snapShot = takeSnapShot();
500
			SpaceTreeNode[] nodes = (SpaceTreeNode[]) nodeList.toArray(new SpaceTreeNode[nodeList.size()]);
501
			double initialStartPosition = nodes[0].positionInLayer;
502
			double initialNodesBredth = nodes[nodes.length - 1].positionInLayer - initialStartPosition;
503
			double[] desiredPositions = new double[nodes.length];
504
			// calculate desired positions for every node, regarding their
505
			// initial initial proportions
506
			for (int i = 0; i < nodes.length; i++) {
507
				double initialPositionAsPercent = (initialNodesBredth > 0) ? (nodes[i].positionInLayer - initialStartPosition) / initialNodesBredth
508
						: 0;
509
				desiredPositions[i] = initialPositionAsPercent * (endPosition - startPosition);
510
			}
511
			// make sure there's proper distance between each pair of
512
			// consecutive nodes
513
			for (int i = 1; i < nodes.length; i++) {
514
				SpaceTreeNode node = nodes[i];
515
				SpaceTreeNode previousNode = nodes[i - 1];
516
				double expectedDistance = expectedDistance(previousNode, node);
517
				if (desiredPositions[i] - desiredPositions[i - 1] < expectedDistance) {
518
					desiredPositions[i] = desiredPositions[i - 1] + expectedDistance;
519
				}
520
			}
521
			// if the above operation caused some nodes to fall out of requested
522
			// bounds, push them back
523
			if (desiredPositions[nodes.length - 1] > (endPosition - startPosition)) {
524
				desiredPositions[nodes.length - 1] = (endPosition - startPosition);
525
				for (int i = nodes.length - 1; i > 0; i--) {
526
					SpaceTreeNode node = nodes[i];
527
					SpaceTreeNode previousNode = nodes[i - 1];
528
					double expectedDistance = expectedDistance(previousNode, node);
529
					if (desiredPositions[i] - desiredPositions[i - 1] < expectedDistance) {
530
						desiredPositions[i - 1] = desiredPositions[i] - expectedDistance;
531
					} else
532
						break;
533
				}
534
			}
535
536
			for (int i = 0; i < nodeList.size(); i++) {
537
				SpaceTreeNode node = (SpaceTreeNode) nodeList.get(i);
538
				double desiredPosition = startPosition + desiredPositions[i];
539
				moveNode(node, desiredPosition);
540
				if (Math.abs(node.positionInLayer - desiredPosition) > 0.5) {
541
					startPosition += (node.positionInLayer - desiredPosition);
542
					i = -1;
543
					revertToShanpshot(snapShot);
544
				}
545
			}
546
		}
547
548
		public void moveNode(SpaceTreeNode node, double newPosition) {
549
			Collections.sort(nodes, new Comparator() {
550
				public int compare(Object arg0, Object arg1) {
551
					return ((SpaceTreeNode) arg0).order - ((SpaceTreeNode) arg1).order;
552
				}
553
			});
554
			double positionInLayerAtStart = node.positionInLayer;
555
			if (newPosition >= positionInLayerAtStart)
556
				moveNodeForward(node, newPosition);
557
			if (newPosition <= positionInLayerAtStart)
558
				moveNodeBackward(node, newPosition);
559
		}
560
561
		/**
562
		 * Tries to increase node's position in layer. It can move a node only
563
		 * if it doesn't cause nodes to fall out of available space (see
564
		 * {@link SpaceTreeLayoutAlgorithm#getAvailableSpace()}. If there's not
565
		 * enough space available, some nodes may be collapsed to increase it as
566
		 * long as it doesn't cause
567
		 * {@link SpaceTreeLayoutAlgorithm#protectedNode} or any of its
568
		 * descendants to be collapsed.
569
		 * 
570
		 * @param nodeToMove
571
		 * @param newPosition
572
		 */
573
		private void moveNodeForward(SpaceTreeNode nodeToMove, double newPosition) {
574
			int nodeIndex = nodes.indexOf(nodeToMove);
575
			if (nodeIndex == -1)
576
				throw new IllegalArgumentException("node not on this layer");
577
			// move forward -> check space to the 'right'
578
			NodeSnapshot[][] snapShot = takeSnapShot();
579
			boolean firstRun = true;
580
			mainLoop: while (firstRun || nodeToMove.positionInLayer < newPosition) {
581
				firstRun = false;
582
				double requiredSpace = 0;
583
				SpaceTreeNode previousNode = nodeToMove;
584
				for (int i = nodeIndex + 1; i < nodes.size(); i++) {
585
					SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i);
586
					requiredSpace += expectedDistance(previousNode, nextNode);
587
					previousNode = nextNode;
588
				}
589
				requiredSpace += previousNode.spaceRequiredForNode() / 2;
590
				if (requiredSpace > getAvailableSpace() - newPosition) {
591
					// find nodes to remove
592
					boolean removed = false;
593
					for (int i = nodeIndex; i < nodes.size(); i++) {
594
						SpaceTreeNode nextNode = ((SpaceTreeNode) nodes.get(i));
595
						if (protectedNode == null || (!protectedNode.isAncestorOf(nextNode) && !nextNode.parent.isAncestorOf(protectedNode))) {
596
							collapseNode((SpaceTreeNode) nextNode.parent);
597
							if (nextNode.parent == nodeToMove.parent)
598
								break mainLoop;
599
							removed = true;
600
							break;
601
						}
602
					}
603
					if (!removed) {
604
						// not enough space, but we can't collapse anything...
605
						newPosition = getAvailableSpace() - requiredSpace;
606
						revertToShanpshot(snapShot);
607
						continue mainLoop;
608
					}
609
				}
610
				// move the node and all its neighbors to the 'right'
611
				SpaceTreeNode currentNodeToMove = nodeToMove;
612
				double newPositionForCurrent = newPosition;
613
				for (int i = nodeIndex; i < nodes.size(); i++) {
614
					currentNodeToMove.positionInLayer = newPositionForCurrent;
615
					// move parent if moved node is its first child
616
					if (currentNodeToMove.firstChild) {
617
						SpaceTreeNode parent = (SpaceTreeNode) currentNodeToMove.parent;
618
						if (depth > 0 && parent.positionInLayer <= newPositionForCurrent) {
619
							SpaceTreeLayer parentLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth - 1);
620
							parentLayer.moveNodeForward(parent, newPositionForCurrent);
621
							if (parent.positionInLayer < newPositionForCurrent) {
622
								double delta = newPositionForCurrent - parent.positionInLayer;
623
								newPosition -= delta;
624
								revertToShanpshot(snapShot);
625
								continue mainLoop;
626
							}
627
						}
628
					}
629
					// move children if necessary
630
					if (currentNodeToMove.expanded && !currentNodeToMove.children.isEmpty()) {
631
						SpaceTreeNode lastChild = (SpaceTreeNode) currentNodeToMove.children.get(currentNodeToMove.children.size() - 1);
632
						if (lastChild.positionInLayer < newPositionForCurrent) {
633
							// try to move all the children, that is move the
634
							// first child and the rest will be pushed
635
							SpaceTreeNode firstChild = (SpaceTreeNode) currentNodeToMove.children.get(0);
636
							SpaceTreeLayer childLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth + 1);
637
							double expectedDistanceBetweenChildren = currentNodeToMove.spaceRequiredForChildren() - firstChild.spaceRequiredForNode()
638
									/ 2 - lastChild.spaceRequiredForNode() / 2;
639
							childLayer.moveNodeForward(firstChild, newPositionForCurrent - expectedDistanceBetweenChildren);
640
							if (currentNodeToMove.expanded && lastChild.positionInLayer < newPositionForCurrent) {
641
								// the previous attempt failed -> try to move
642
								// only the last child
643
								childLayer.moveNodeForward(lastChild, newPositionForCurrent);
644
								if (lastChild.positionInLayer < newPositionForCurrent) {
645
									// child couldn't be moved as far as needed
646
									// -> move current node back to the position
647
									// over the child
648
									double delta = newPositionForCurrent - lastChild.positionInLayer;
649
									newPosition -= delta;
650
									revertToShanpshot(snapShot);
651
									continue mainLoop;
652
								}
653
							}
654
						}
655
					}
656
657
					if (i < nodes.size() - 1) {
658
						SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i + 1);
659
						newPositionForCurrent += expectedDistance(currentNodeToMove, nextNode);
660
						currentNodeToMove = nextNode;
661
						if (currentNodeToMove.positionInLayer > newPositionForCurrent)
662
							break;
663
					}
664
				}
665
			}
666
		}
667
668
		/**
669
		 * Method complementary to
670
		 * {@link #moveNodeForward(SpaceTreeNode, double)}. Decreases node's
671
		 * position in layer.
672
		 * 
673
		 * @param nodeToMove
674
		 * @param newPosition
675
		 */
676
		private void moveNodeBackward(SpaceTreeNode nodeToMove, double newPosition) {
677
			int nodeIndex = nodes.indexOf(nodeToMove);
678
			if (nodeIndex == -1)
679
				throw new IllegalArgumentException("node not on this layer");
680
			// move backward -> check space to the 'left'
681
			// move and collapse until there's enough space
682
			NodeSnapshot[][] snapShot = takeSnapShot();
683
			boolean firstRun = true;
684
			mainLoop: while (firstRun || nodeToMove.positionInLayer > newPosition) {
685
				firstRun = false;
686
				double requiredSpace = 0;
687
				SpaceTreeNode previousNode = nodeToMove;
688
				for (int i = nodeIndex - 1; i >= 0; i--) {
689
					SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i);
690
					requiredSpace += expectedDistance(previousNode, nextNode);
691
					previousNode = nextNode;
692
				}
693
				requiredSpace += previousNode.spaceRequiredForNode() / 2;
694
				if (requiredSpace > newPosition) {
695
					// find nodes to remove
696
					boolean removed = false;
697
					for (int i = nodeIndex; i >= 0; i--) {
698
						SpaceTreeNode nextNode = ((SpaceTreeNode) nodes.get(i));
699
						if (protectedNode == null || (!protectedNode.isAncestorOf(nextNode) && !nextNode.parent.isAncestorOf(protectedNode))) {
700
							collapseNode((SpaceTreeNode) nextNode.parent);
701
							if (nextNode.parent == nodeToMove.parent)
702
								break mainLoop;
703
							nodeIndex -= nextNode.parent.children.size();
704
							removed = true;
705
							break;
706
						}
707
					}
708
					if (!removed) {
709
						// not enough space, but we can't collapse anything...
710
						newPosition = requiredSpace;
711
						revertToShanpshot(snapShot);
712
						continue mainLoop;
713
					}
714
				}
715
				// move the node and all its neighbors to the 'left'
716
				SpaceTreeNode currentNodeToMove = nodeToMove;
717
				double newPositionForCurrent = newPosition;
718
				for (int i = nodeIndex; i >= 0; i--) {
719
					currentNodeToMove.positionInLayer = newPositionForCurrent;
720
					// move parent if moved node is its last child
721
					if (currentNodeToMove.lastChild) {
722
						SpaceTreeNode parent = (SpaceTreeNode) currentNodeToMove.parent;
723
						if (depth > 0 && parent.positionInLayer >= newPositionForCurrent) {
724
							SpaceTreeLayer parentLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth - 1);
725
							parentLayer.moveNodeBackward(parent, newPositionForCurrent);
726
							if (parent.positionInLayer > newPositionForCurrent) {
727
								double delta = parent.positionInLayer - newPositionForCurrent;
728
								newPosition += delta;
729
								revertToShanpshot(snapShot);
730
								continue mainLoop;
731
							}
732
						}
733
					}
734
					// move children if necessary
735
					if (currentNodeToMove.expanded && !currentNodeToMove.children.isEmpty()) {
736
						SpaceTreeNode firstChild = (SpaceTreeNode) currentNodeToMove.children.get(0);
737
						if (firstChild.positionInLayer > newPositionForCurrent) {
738
							// try to move all the children, that is move the
739
							// last child and the rest will be pushed
740
							SpaceTreeNode lastChild = (SpaceTreeNode) currentNodeToMove.children.get(currentNodeToMove.children.size() - 1);
741
							SpaceTreeLayer childLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth + 1);
742
							double expectedDistanceBetweenChildren = currentNodeToMove.spaceRequiredForChildren() - firstChild.spaceRequiredForNode()
743
									/ 2 - lastChild.spaceRequiredForNode() / 2;
744
							childLayer.moveNodeBackward(lastChild, newPositionForCurrent + expectedDistanceBetweenChildren);
745
							if (currentNodeToMove.expanded && firstChild.positionInLayer > newPositionForCurrent) {
746
								// the previous attempt failed -> try to move
747
								// only the first child
748
								childLayer.moveNodeBackward(firstChild, newPositionForCurrent);
749
								if (firstChild.positionInLayer > newPositionForCurrent) {
750
									// child couldn't be moved as far as needed
751
									// -> move current node back to the position
752
									// over the child
753
									double delta = firstChild.positionInLayer - newPositionForCurrent;
754
									newPosition += delta;
755
									revertToShanpshot(snapShot);
756
									continue mainLoop;
757
								}
758
							}
759
						}
760
					}
761
					if (i > 0) {
762
						SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i - 1);
763
						newPositionForCurrent -= expectedDistance(currentNodeToMove, nextNode);
764
						currentNodeToMove = nextNode;
765
						if (currentNodeToMove.positionInLayer < newPositionForCurrent)
766
							break;
767
					}
768
				}
769
			}
770
		}
771
772
		public String toString() {
773
			StringBuffer buffer = new StringBuffer();
774
			buffer.append("Layer ").append(depth).append(": ");
775
			for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
776
				SpaceTreeNode node = (SpaceTreeNode) iterator.next();
777
				buffer.append(node.node).append(", ");
778
			}
779
			return buffer.toString();
780
		}
781
782
		private void collapseNode(SpaceTreeNode node) {
783
			node.expanded = false;
784
			SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(node.depth + 1);
785
			layer.removeNodes(node.children);
786
			for (Iterator iterator = node.children.iterator(); iterator.hasNext();) {
787
				SpaceTreeNode child = (SpaceTreeNode) iterator.next();
788
				if (child.expanded)
789
					collapseNode(child);
790
			}
791
		}
792
	}
793
794
	private class SpaceTreeExpandCollapseManager implements ExpandCollapseManager {
795
		public void initExpansion(LayoutContext context) {
796
			// do nothing - initialization performed in #setLayoutContext()
797
		}
798
799
		public void setExpanded(LayoutContext context, NodeLayout node, boolean expanded) {
800
			SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node);
801
			if (expanded) {
802
				maximizeExpansion(spaceTreeNode);
803
				refreshLayout(true);
804
			} else if (spaceTreeNode.expanded) {
805
				spaceTreeNode.expanded = false;
806
				((SpaceTreeLayer) spaceTreeLayers.get(spaceTreeNode.depth + 1)).removeNodes(spaceTreeNode.children);
807
				refreshLayout(true);
808
			}
809
		}
810
811
		public boolean canExpand(LayoutContext context, NodeLayout node) {
812
			SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node);
813
			if (spaceTreeNode != null) {
814
				// we don't check if node is expanded because it can be expanded
815
				// again
816
				return !spaceTreeNode.children.isEmpty();
817
			}
818
			return false;
819
		}
820
821
		public boolean canCollapse(LayoutContext context, NodeLayout node) {
822
			SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node);
823
			if (spaceTreeNode != null) {
824
				return spaceTreeNode.expanded && !spaceTreeNode.children.isEmpty();
825
			}
826
			return false;
827
		}
828
829
		public void maximizeExpansion(SpaceTreeNode nodeToExpand) {
830
			protectedNode = nodeToExpand;
831
			double availableSpace = getAvailableSpace();
832
			double requiredSpace = 0;
833
834
			((SpaceTreeLayer) spaceTreeLayers.get(nodeToExpand.depth + 1)).removeNodes(nodeToExpand.children);
835
836
			ArrayList nodesInThisLayer = null;
837
			ArrayList nodesInNextLayer = new ArrayList();
838
			nodesInNextLayer.add(nodeToExpand);
839
			double spaceRequiredInNextLayer = nodeToExpand.spaceRequiredForNode();
840
			for (int layer = 0; !nodesInNextLayer.isEmpty(); layer++) {
841
				NodeSnapshot[][] snapShot = takeSnapShot();
842
				requiredSpace = Math.max(requiredSpace, spaceRequiredInNextLayer);
843
				spaceRequiredInNextLayer = 0;
844
845
				nodesInThisLayer = nodesInNextLayer;
846
				nodesInNextLayer = new ArrayList();
847
848
				int numOfNodesWithChildren = 0;
849
				for (Iterator iterator = nodesInThisLayer.iterator(); iterator.hasNext();) {
850
					SpaceTreeNode node = (SpaceTreeNode) iterator.next();
851
					if (!node.children.isEmpty()) {
852
						node.expanded = true;
853
						spaceRequiredInNextLayer += node.spaceRequiredForChildren();
854
						nodesInNextLayer.addAll(node.children);
855
						numOfNodesWithChildren++;
856
					}
857
				}
858
859
				for (Iterator iterator = nodesInNextLayer.iterator(); iterator.hasNext();) {
860
					SpaceTreeNode node = (SpaceTreeNode) iterator.next();
861
					node.expanded = false;
862
				}
863
864
				if (numOfNodesWithChildren == 0)
865
					break;
866
867
				spaceRequiredInNextLayer += branchGap * (numOfNodesWithChildren - 1);
868
869
				boolean addedNewLayer = false;
870
				if ((spaceRequiredInNextLayer <= requiredSpace || spaceRequiredInNextLayer <= availableSpace || (layer < 1 && nodeToExpand.depth
871
						+ layer < 1))
872
						&& !nodesInNextLayer.isEmpty()) {
873
					// add next layer and center its nodes
874
875
					SpaceTreeLayer childLayer = (SpaceTreeLayer) spaceTreeLayers.get(nodeToExpand.depth + layer + 1);
876
					childLayer.addNodes(nodesInNextLayer);
877
					SpaceTreeNode firstChild = ((SpaceTreeNode) nodesInNextLayer.get(0));
878
					SpaceTreeNode lastChild = ((SpaceTreeNode) nodesInNextLayer.get(nodesInNextLayer.size() - 1));
879
					double boundsWidth = spaceRequiredInNextLayer - firstChild.spaceRequiredForNode() / 2 - lastChild.spaceRequiredForNode() / 2;
880
					double startPosition = Math.max((availableSpace - boundsWidth) / 2, firstChild.spaceRequiredForNode() / 2);
881
					setAvailableSpace(spaceRequiredInNextLayer);
882
					childLayer.fitNodesWithinBounds(nodesInNextLayer, startPosition, startPosition + boundsWidth);
883
					setAvailableSpace(0);
884
					if (nodeToExpand.childrenPositionsOK(nodesInThisLayer) || layer == 0 || nodeToExpand.depth + layer < 1)
885
						addedNewLayer = true;
886
				}
887
				if (!addedNewLayer) {
888
					revertToShanpshot(snapShot);
889
					break;
890
				}
891
			}
892
			nodeToExpand.centerParentsBottomUp();
893
			nodeToExpand.centerParentsTopDown();
894
		}
895
	};
896
897
	private SpaceTreeExpandCollapseManager expandCollapseManager = new SpaceTreeExpandCollapseManager();
898
899
	private ContextListener contextListener = new ContextListener.Stub() {
900
		public boolean boundsChanged(LayoutContext context) {
901
			boolean previousBoundsWrong = (bounds == null || bounds.width * bounds.height <= 0);
902
			bounds = context.getBounds();
903
			if (bounds.width * bounds.height > 0 && previousBoundsWrong) {
904
				expandCollapseManager.maximizeExpansion((SpaceTreeNode) treeObserver.getSuperRoot());
905
				refreshLayout(false);
906
			}
907
			return false;
908
		}
909
	};
910
911
	private LayoutListener layoutListener = new LayoutListener() {
912
913
		public boolean subgraphResized(LayoutContext context, SubgraphLayout subgraph) {
914
			return defaultSubgraphHandle(context, subgraph);
915
		}
916
917
		public boolean subgraphMoved(LayoutContext context, SubgraphLayout subgraph) {
918
			return defaultSubgraphHandle(context, subgraph);
919
		}
920
921
		public boolean nodeResized(LayoutContext context, NodeLayout node) {
922
			setAvailableSpace(getAvailableSpace() + ((SpaceTreeNode) treeObserver.getTreeNode(node)).spaceRequiredForNode());
923
			boolean result = defaultNodeHandle(context, node);
924
			setAvailableSpace(0);
925
			return result;
926
		}
927
928
		public boolean nodeMoved(LayoutContext context, NodeLayout node) {
929
			return defaultNodeHandle(context, node);
930
		}
931
932
		/**
933
		 * Finds a root of given subgraph, updates the root position and moves
934
		 * the subgraph to proper position
935
		 * 
936
		 * @param context
937
		 * @param subgraph
938
		 * @return
939
		 */
940
		private boolean defaultSubgraphHandle(LayoutContext context, SubgraphLayout subgraph) {
941
			SpaceTreeNode node = (SpaceTreeNode) treeObserver.getTreeNode(subgraph.getNodes()[0]);
942
			while (node != null && node.node != null && node.node.getSubgraph() == subgraph) {
943
				node = (SpaceTreeNode) node.parent;
944
			}
945
			if (node != null && node.subgraph == subgraph) {
946
				node.adjustPosition(subgraph.getLocation());
947
				if (context.isBackgroundLayoutEnabled()) {
948
					((SpaceTreeNode) treeObserver.getSuperRoot()).flushLocationChanges(0);
949
					node.refreshSubgraphLocation();
950
					context.flushChanges(false);
951
				}
952
			}
953
			return false;
954
		}
955
956
		private boolean defaultNodeHandle(LayoutContext context, NodeLayout node) {
957
			if (bounds.width * bounds.height <= 0)
958
				return false;
959
			SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node);
960
			spaceTreeNode.adjustPosition(node.getLocation());
961
			if (context.isBackgroundLayoutEnabled()) {
962
				((SpaceTreeNode) treeObserver.getSuperRoot()).flushLocationChanges(0);
963
				spaceTreeNode.refreshSubgraphLocation();
964
				context.flushChanges(false);
965
			}
966
			return false;
967
		}
968
	};
969
970
	private int direction = TOP_DOWN;
971
972
	private double leafGap = 15;
973
	private double branchGap = leafGap + 5;
974
	private double layerGap = 20;
975
976
	private boolean directionChanged = false;
977
978
	private LayoutContext context;
979
980
	private DisplayIndependentRectangle bounds;
981
982
	private TreeLayoutObserver treeObserver;
983
984
	private double availableSpace;
985
986
	private ArrayList spaceTreeLayers = new ArrayList();
987
988
	/**
989
	 * If not null, this node and all of its children shall not be collapsed
990
	 * during node movements.
991
	 */
992
	private SpaceTreeNode protectedNode = null;
993
994
	/**
995
	 * Constructs an instance of <code>SpaceTreeLayoutAlgorithm</code> that
996
	 * places the root of a tree at the top of the graph.
997
	 */
998
	public SpaceTreeLayoutAlgorithm() {
999
	}
1000
1001
	/**
1002
	 * Constructs an instance of <code>SpaceTreeLayoutAlgorithm</code> that
1003
	 * places the root of a tree according to given direction
1004
	 * 
1005
	 * @param direction
1006
	 *            direction of the tree, sould be one of the following:
1007
	 *            {@link #TOP_DOWN}, {@link #BOTTOM_UP}, {@link #LEFT_RIGHT},
1008
	 *            {@link #RIGHT_LEFT}.
1009
	 */
1010
	public SpaceTreeLayoutAlgorithm(int direction) {
1011
		setDirection(direction);
1012
	}
1013
1014
	/**
1015
	 * 
1016
	 * @return current direction (placement) of the tree
1017
	 */
1018
	public int getDirection() {
1019
		return direction;
1020
	}
1021
1022
	/**
1023
	 * Sets direction (placement) of the tree
1024
	 * 
1025
	 * @param direction
1026
	 *            direction of the tree, sould be one of the following:
1027
	 *            {@link #TOP_DOWN}, {@link #BOTTOM_UP}, {@link #LEFT_RIGHT},
1028
	 *            {@link #RIGHT_LEFT}.
1029
	 */
1030
	public void setDirection(int direction) {
1031
		if (direction == this.direction)
1032
			return;
1033
		if (direction == TOP_DOWN || direction == BOTTOM_UP || direction == LEFT_RIGHT || direction == RIGHT_LEFT) {
1034
			this.direction = direction;
1035
			directionChanged = true;
1036
			if (context.isBackgroundLayoutEnabled())
1037
				checkPendingChangeDirection();
1038
		} else
1039
			throw new IllegalArgumentException("Invalid direction: " + direction);
1040
	}
1041
1042
	public void applyLayout(boolean clean) {
1043
		bounds = context.getBounds();
1044
1045
		if (bounds.width * bounds.height == 0)
1046
			return;
1047
1048
		if (clean) {
1049
			treeObserver.recomputeTree();
1050
			expandCollapseManager.maximizeExpansion((SpaceTreeNode) treeObserver.getSuperRoot());
1051
		}
1052
		SpaceTreeNode superRoot = ((SpaceTreeNode) treeObserver.getSuperRoot());
1053
		superRoot.flushExpansionChanges();
1054
		superRoot.flushLocationChanges(0);
1055
		checkPendingChangeDirection();
1056
	}
1057
1058
	public void setLayoutContext(LayoutContext context) {
1059
		if (this.context != null) {
1060
			this.context.removeContextListener(contextListener);
1061
			this.context.removeLayoutListener(layoutListener);
1062
			treeObserver.stop();
1063
		}
1064
		this.context = context;
1065
		context.addContextListener(contextListener);
1066
		context.addLayoutListener(layoutListener);
1067
		treeObserver = new TreeLayoutObserver(context, spaceTreeNodeFactory);
1068
1069
		bounds = context.getBounds();
1070
	}
1071
1072
	/**
1073
	 * 
1074
	 * @return <code>ExpandCollapseManager</code> that can (and should) be used
1075
	 *         on layout context managed by this layout algorithm.
1076
	 */
1077
	public ExpandCollapseManager getExpandCollapseManager() {
1078
		return expandCollapseManager;
1079
	}
1080
1081
	private void checkPendingChangeDirection() {
1082
		if (directionChanged) {
1083
			SubgraphLayout[] subgraphs = context.getSubgraphs();
1084
			int subgraphDirection = getSubgraphDirection();
1085
			for (int i = 0; i < subgraphs.length; i++) {
1086
				subgraphs[i].setDirection(subgraphDirection);
1087
			}
1088
			directionChanged = false;
1089
		}
1090
	}
1091
1092
	private int getSubgraphDirection() {
1093
		switch (direction) {
1094
		case TOP_DOWN:
1095
			return SubgraphLayout.TOP_DOWN;
1096
		case BOTTOM_UP:
1097
			return SubgraphLayout.BOTTOM_UP;
1098
		case LEFT_RIGHT:
1099
			return SubgraphLayout.LEFT_RIGHT;
1100
		case RIGHT_LEFT:
1101
			return SubgraphLayout.RIGHT_LEFT;
1102
		}
1103
		throw new RuntimeException();
1104
	}
1105
1106
	protected void refreshLayout(boolean animation) {
1107
		if (!context.isBackgroundLayoutEnabled())
1108
			return;
1109
		SpaceTreeNode superRoot = (SpaceTreeNode) treeObserver.getSuperRoot();
1110
		if (animation && superRoot.flushCollapseChanges())
1111
			context.flushChanges(true);
1112
		if (superRoot.flushLocationChanges(0) && animation)
1113
			context.flushChanges(true);
1114
		superRoot.flushExpansionChanges();
1115
		superRoot.flushLocationChanges(0);
1116
		context.flushChanges(animation);
1117
	}
1118
1119
	/**
1120
	 * Available space is the biggest of the following values:
1121
	 * <ul>
1122
	 * <li>Space provided by current context bounds</li>
1123
	 * <li>Space already taken by the widest layer</li>
1124
	 * <li>Value set with {@link #setAvailableSpace(double)}</li>
1125
	 * </ul>
1126
	 * 
1127
	 * @return
1128
	 */
1129
	private double getAvailableSpace() {
1130
		double result = (direction == TOP_DOWN || direction == BOTTOM_UP) ? bounds.width : bounds.height;
1131
		result = Math.max(result, this.availableSpace);
1132
		for (Iterator iterator = spaceTreeLayers.iterator(); iterator.hasNext();) {
1133
			SpaceTreeLayer layer = (SpaceTreeLayer) iterator.next();
1134
			if (!layer.nodes.isEmpty()) {
1135
				SpaceTreeNode first = (SpaceTreeNode) layer.nodes.get(0);
1136
				SpaceTreeNode last = (SpaceTreeNode) layer.nodes.get(layer.nodes.size() - 1);
1137
				result = Math.max(result, last.positionInLayer - first.positionInLayer + (first.spaceRequiredForNode() + last.spaceRequiredForNode())
1138
						/ 2);
1139
			} else
1140
				break;
1141
		}
1142
		return result;
1143
	}
1144
1145
	/**
1146
	 * This method allows to reserve more space than actual layout bounds
1147
	 * provide or nodes currently occupy.
1148
	 * 
1149
	 * @param availableSpace
1150
	 */
1151
	private void setAvailableSpace(double availableSpace) {
1152
		this.availableSpace = availableSpace;
1153
	}
1154
1155
	private double expectedDistance(SpaceTreeNode node, SpaceTreeNode neighbor) {
1156
		double expectedDistance = (node.spaceRequiredForNode() + neighbor.spaceRequiredForNode()) / 2;
1157
		expectedDistance += (node.parent == neighbor.parent) ? leafGap : branchGap;
1158
		return expectedDistance;
1159
	}
1160
1161
	private class NodeSnapshot {
1162
		SpaceTreeNode node;
1163
		double position;
1164
		boolean expanded;
1165
	}
1166
1167
	/**
1168
	 * Stores current expansion state of tree nodes and their position in layers
1169
	 * 
1170
	 * @return array containing state of all unpruned nodes
1171
	 */
1172
	private NodeSnapshot[][] takeSnapShot() {
1173
		NodeSnapshot[][] result = new NodeSnapshot[spaceTreeLayers.size()][];
1174
		for (int i = 0; i < result.length; i++) {
1175
			SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(i);
1176
			result[i] = new NodeSnapshot[layer.nodes.size()];
1177
			for (int j = 0; j < result[i].length; j++) {
1178
				result[i][j] = new NodeSnapshot();
1179
				result[i][j].node = ((SpaceTreeNode) layer.nodes.get(j));
1180
				result[i][j].position = result[i][j].node.positionInLayer;
1181
				result[i][j].expanded = result[i][j].node.expanded;
1182
			}
1183
		}
1184
		return result;
1185
	}
1186
1187
	/**
1188
	 * Restores tree nodes' expansion state and position in layers
1189
	 * 
1190
	 * @param snapShot
1191
	 *            state obtained with {@link #takeSnapShot()}
1192
	 */
1193
	private void revertToShanpshot(NodeSnapshot[][] snapShot) {
1194
		for (int i = 0; i < snapShot.length; i++) {
1195
			SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(i);
1196
			layer.nodes.clear();
1197
			for (int j = 0; j < snapShot[i].length; j++) {
1198
				snapShot[i][j].node.positionInLayer = snapShot[i][j].position;
1199
				snapShot[i][j].node.expanded = snapShot[i][j].expanded;
1200
				layer.nodes.add(snapShot[i][j].node);
1201
			}
1202
		}
1203
	}
1204
}
(-)src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java (-808 / +776 lines)
Lines 1-808 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
11
package org.eclipse.zest.layouts.algorithms;
12
12
13
import java.util.Date;
13
import java.util.HashMap;
14
import java.util.HashMap;
14
15
import java.util.Map;
15
import org.eclipse.zest.layouts.LayoutAlgorithm;
16
16
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
17
import org.eclipse.zest.layouts.LayoutStyles;
17
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
18
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
18
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
19
import org.eclipse.zest.layouts.dataStructures.InternalNode;
19
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
20
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
20
import org.eclipse.zest.layouts.interfaces.EntityLayout;
21
21
import org.eclipse.zest.layouts.interfaces.LayoutContext;
22
/**
22
import org.eclipse.zest.layouts.interfaces.LayoutListener;
23
 * The SpringLayoutAlgorithm has its own data repository and relation
23
import org.eclipse.zest.layouts.interfaces.NodeLayout;
24
 * repository. A user can populate the repository, specify the layout
24
import org.eclipse.zest.layouts.interfaces.SubgraphLayout;
25
 * conditions, do the computation and query the computed results.
25
26
 * <p>
26
/**
27
 * Instructions for using SpringLayoutAlgorithm: <br>
27
 * The SpringLayoutAlgorithm has its own data repository and relation
28
 * 1. Instantiate a SpringLayout object; <br>
28
 * repository. A user can populate the repository, specify the layout
29
 * 2. Populate the data repository using {@link #add add(...)}; <br>
29
 * conditions, do the computation and query the computed results.
30
 * 3. Populate the relation repository using
30
 * <p>
31
 * {@link #addRelation addRelation(...)}; <br>
31
 * Instructions for using SpringLayoutAlgorithm: <br>
32
 * 4. Execute {@link #compute compute()}; <br>
32
 * 1. Instantiate a SpringLayout object; <br>
33
 * 5. Execute {@link #fitWithinBounds fitWithinBounds(...)}; <br>
33
 * 2. Populate the data repository using {@link #add add(...)}; <br>
34
 * 6. Query the computed results(node size and node position).
34
 * 3. Populate the relation repository using {@link #addRelation
35
 * 
35
 * addRelation(...)}; <br>
36
 * @version 2.0
36
 * 4. Execute {@link #compute compute()}; <br>
37
 * @author Ian Bull
37
 * 5. Execute {@link #fitWithinBounds fitWithinBounds(...)}; <br>
38
 * @author Casey Best (version 1.0 by Jingwei Wu/Rob Lintern)
38
 * 6. Query the computed results(node size and node position).
39
 */
39
 * 
40
public class SpringLayoutAlgorithm extends ContinuousLayoutAlgorithm {
40
 * @version 2.0
41
41
 * @author Ian Bull
42
    private final static boolean DEFAULT_ANCHOR = false;
42
 * @author Casey Best (version 1.0 by Jingwei Wu/Rob Lintern)
43
43
 */
44
    /**
44
public class SpringLayoutAlgorithm implements LayoutAlgorithm {
45
     * The default value for the spring layout number of interations.
45
46
     */
46
	/**
47
    public static final int DEFAULT_SPRING_ITERATIONS = 1000;
47
	 * The default value for the spring layout number of interations.
48
48
	 */
49
    /**
49
	public static final int DEFAULT_SPRING_ITERATIONS = 1000;
50
     * the default value for the time algorithm runs.
50
51
     */
51
	/**
52
    public static final long MAX_SPRING_TIME = 10000;
52
	 * the default value for the time algorithm runs.
53
53
	 */
54
    /**
54
	public static final long MAX_SPRING_TIME = 10000;
55
     * The default value for positioning nodes randomly.
55
56
     */
56
	/**
57
    public static final boolean DEFAULT_SPRING_RANDOM = true;
57
	 * The default value for positioning nodes randomly.
58
58
	 */
59
    /**
59
	public static final boolean DEFAULT_SPRING_RANDOM = true;
60
     * The default value for ignoring unconnected nodes.
60
61
     */
61
	/**
62
    public static final boolean DEFAULT_SPRING_IGNORE_UNCON = true;
62
	 * The default value for the spring layout move-control.
63
63
	 */
64
    /**
64
	public static final double DEFAULT_SPRING_MOVE = 1.0f;
65
     * The default value for separating connected components.
65
66
     */
66
	/**
67
    public static final boolean DEFAULT_SPRING_SEPARATE_COMPONENTS = true;
67
	 * The default value for the spring layout strain-control.
68
68
	 */
69
    /**
69
	public static final double DEFAULT_SPRING_STRAIN = 1.0f;
70
     * The default value for the spring layout move-control.
70
71
     */
71
	/**
72
    public static final double DEFAULT_SPRING_MOVE = 1.0f;
72
	 * The default value for the spring layout length-control.
73
73
	 */
74
    /**
74
	public static final double DEFAULT_SPRING_LENGTH = 3.0f;
75
     * The default value for the spring layout strain-control.
75
76
     */
76
	/**
77
    public static final double DEFAULT_SPRING_STRAIN = 1.0f;
77
	 * The default value for the spring layout gravitation-control.
78
78
	 */
79
    /**
79
	public static final double DEFAULT_SPRING_GRAVITATION = 2.0f;
80
     * The default value for the spring layout length-control.
80
81
     */
81
	/**
82
    public static final double DEFAULT_SPRING_LENGTH = 1.0f;
82
	 * Minimum distance considered between nodes
83
83
	 */
84
    /**
84
	protected static final double MIN_DISTANCE = 1.0d;
85
     * The default value for the spring layout gravitation-control.
85
86
     */
86
	/**
87
    public static final double DEFAULT_SPRING_GRAVITATION = 1.0f;
87
	 * An arbitrarily small value in mathematics.
88
88
	 */
89
    /**
89
	protected static final double EPSILON = 0.001d;
90
     * The variable can be customized to set the number of iterations used.
90
91
     */
91
	/**
92
    private static int sprIterations = DEFAULT_SPRING_ITERATIONS;
92
	 * The variable can be customized to set the number of iterations used.
93
93
	 */
94
    /**
94
	private int sprIterations = DEFAULT_SPRING_ITERATIONS;
95
     * This variable can be customized to set the max number of MS the algorithm
95
96
     * should run
96
	/**
97
     */
97
	 * This variable can be customized to set the max number of MS the algorithm
98
    private static long maxTimeMS = MAX_SPRING_TIME;
98
	 * should run
99
99
	 */
100
    /**
100
	private long maxTimeMS = MAX_SPRING_TIME;
101
     * The variable can be customized to set whether or not the spring layout
101
102
     * nodes are positioned randomly before beginning iterations.
102
	/**
103
     */
103
	 * The variable can be customized to set whether or not the spring layout
104
    private static boolean sprRandom = DEFAULT_SPRING_RANDOM;
104
	 * nodes are positioned randomly before beginning iterations.
105
105
	 */
106
    /**
106
	private boolean sprRandom = DEFAULT_SPRING_RANDOM;
107
     * Minimum distance considered between nodes
107
108
     */
108
	/**
109
    protected static final double MIN_DISTANCE = 0.001d;
109
	 * The variable can be customized to set the spring layout move-control.
110
110
	 */
111
    /**
111
	private double sprMove = DEFAULT_SPRING_MOVE;
112
     * An arbitrarily small value in mathematics.
112
113
     */
113
	/**
114
    protected static final double EPSILON = 0.001d;
114
	 * The variable can be customized to set the spring layout strain-control.
115
115
	 */
116
    /**
116
	private double sprStrain = DEFAULT_SPRING_STRAIN;
117
     * The variable can be customerized to set the spring layout move-control.
117
118
     */
118
	/**
119
    private static double sprMove = DEFAULT_SPRING_MOVE;
119
	 * The variable can be customized to set the spring layout length-control.
120
120
	 */
121
    /**
121
	private double sprLength = DEFAULT_SPRING_LENGTH;
122
     * The variable can be customized to set the spring layout strain-control.
122
123
     */
123
	/**
124
    private static double sprStrain = DEFAULT_SPRING_STRAIN;
124
	 * The variable can be customized to set the spring layout
125
125
	 * gravitation-control.
126
    /**
126
	 */
127
     * The variable can be customized to set the spring layout length-control.
127
	private double sprGravitation = DEFAULT_SPRING_GRAVITATION;
128
     */
128
129
    private static double sprLength = DEFAULT_SPRING_LENGTH;
129
	/**
130
130
	 * Variable indicating whether the algorithm should resize elements.
131
    /**
131
	 */
132
     * The variable can be customized to set the spring layout
132
	private boolean resize = false;
133
     * gravitation-control.
133
134
     */
134
	private int iteration;
135
    private static double sprGravitation = DEFAULT_SPRING_GRAVITATION;
135
136
136
	private double[][] srcDestToSumOfWeights;
137
    /**
137
138
     * The largest movement of all vertices that has occured in the most recent
138
	private EntityLayout[] entities;
139
     * iteration.
139
140
     */
140
	private double[] forcesX, forcesY;
141
    private double largestMovement = 0;
141
142
142
	private double[] locationsX, locationsY;
143
143
144
    /**
144
	private double[] sizeW, sizeH;
145
     * Maps a src and dest object to the number of relations between them. Key
145
146
     * is src.toString() + dest.toString(), value is an Integer
146
	private DisplayIndependentRectangle bounds;
147
     */
147
148
    private Map srcDestToNumRelsMap;
148
	// private double boundsScale = 0.2;
149
149
	private double boundsScaleX = 0.2;
150
    /**
150
	private double boundsScaleY = 0.2;
151
     * Maps a src and dest object to the average weight of the relations between
151
152
     * them. Key is src.toString() + dest.toString(), value is a Double
152
	public boolean fitWithinBounds = true;
153
     */
153
154
    private Map srcDestToRelsAvgWeightMap;
154
	private LayoutContext context;
155
155
156
    /**
156
	class SpringLayoutListener implements LayoutListener {
157
     * Maps a relationship type to a weight. Key is a string, value is a Double
157
158
     */
158
		public boolean nodeMoved(LayoutContext context, NodeLayout node) {
159
    private static Map relTypeToWeightMap = new HashMap();
159
			// TODO Auto-generated method stub
160
160
			for (int i = 0; i < entities.length; i++) {
161
    private int iteration;
161
				if (entities[i] == node) {
162
162
					locationsX[i] = entities[i].getLocation().x;
163
    private int[][] srcDestToNumRels;
163
					locationsY[i] = entities[i].getLocation().y;
164
164
165
    private double[][] srcDestToRelsAvgWeight;
165
				}
166
166
167
    private double[] tempLocationsX;
167
			}
168
168
			return false;
169
    private double[] tempLocationsY;
169
		}
170
170
171
    private double[] forcesX;
171
		public boolean nodeResized(LayoutContext context, NodeLayout node) {
172
172
			// TODO Auto-generated method stub
173
    private double[] forcesY;
173
			return false;
174
174
		}
175
    private boolean[] anchors;
175
176
    
176
		public boolean subgraphMoved(LayoutContext context, SubgraphLayout subgraph) {
177
    private DisplayIndependentRectangle bounds = null;
177
			// TODO Auto-generated method stub
178
    
178
			return false;
179
    Date date = null;
179
		}
180
180
181
    /**
181
		public boolean subgraphResized(LayoutContext context, SubgraphLayout subgraph) {
182
     * Constructor.
182
			// TODO Auto-generated method stub
183
     */
183
			return false;
184
    public SpringLayoutAlgorithm( int styles ) {
184
		}
185
        super( styles );
185
186
        srcDestToNumRelsMap = new HashMap();
186
	}
187
        srcDestToRelsAvgWeightMap = new HashMap();
187
188
        date = new Date();
188
	public void applyLayout(boolean clean) {
189
    }
189
		if (!clean)
190
190
			return;
191
    
191
		while (performAnotherNonContinuousIteration()) {
192
    /**
192
			computeOneIteration();
193
     * Creates a sprint layout algoirthm with no style
193
		}
194
     *
194
		saveLocations();
195
     */
195
		if (resize)
196
    public SpringLayoutAlgorithm() {
196
			AlgorithmHelper.maximizeSizes(entities);
197
    	this( LayoutStyles.NONE );
197
198
	}
198
		if (fitWithinBounds) {
199
    
199
			DisplayIndependentRectangle bounds2 = new DisplayIndependentRectangle(bounds);
200
    public void setLayoutArea(double x, double y, double width, double height) {
200
			int insets = 4;
201
        bounds = new DisplayIndependentRectangle(x,y,width,height);
201
			bounds2.x += insets;
202
    }
202
			bounds2.y += insets;
203
203
			bounds2.width -= 2 * insets;
204
    /**
204
			bounds2.height -= 2 * insets;
205
     * Sets the spring layout move-control.
205
			AlgorithmHelper.fitWithinBounds(entities, bounds2, resize);
206
     * 
206
		}
207
     * @param move
207
208
     *            The move-control value.
208
	}
209
     */
209
210
    public void setSpringMove(double move) {
210
	public void setLayoutContext(LayoutContext context) {
211
        sprMove = move;
211
		this.context = context;
212
    }
212
		this.context.addLayoutListener(new SpringLayoutListener());
213
213
		initLayout();
214
    /**
214
	}
215
     * Returns the move-control value of this SpringLayoutAlgorithm in
215
216
     * double presion.
216
	public void performNIteration(int n) {
217
     * 
217
		if (iteration == 0) {
218
     * @return The move-control value.
218
			entities = context.getEntities();
219
     */
219
			loadLocations();
220
    public double getSpringMove() {
220
			initLayout();
221
        return sprMove;
221
		}
222
    }
222
		bounds = context.getBounds();
223
223
		for (int i = 0; i < n; i++) {
224
    /**
224
			computeOneIteration();
225
     * Sets the spring layout strain-control.
225
			saveLocations();
226
     * 
226
		}
227
     * @param strain
227
		context.flushChanges(false);
228
     *            The strain-control value.
228
	}
229
     */
229
230
    public void setSpringStrain(double strain) {
230
	public void performOneIteration() {
231
        sprStrain = strain;
231
		if (iteration == 0) {
232
    }
232
			entities = context.getEntities();
233
233
			loadLocations();
234
    /**
234
			initLayout();
235
     * Returns the strain-control value of this SpringLayoutAlgorithm in
235
		}
236
     * double presion.
236
		bounds = context.getBounds();
237
     * 
237
		computeOneIteration();
238
     * @return The strain-control value.
238
		saveLocations();
239
     */
239
		context.flushChanges(false);
240
    public double getSpringStrain() {
240
	}
241
        return sprStrain;
241
242
    }
242
	/**
243
243
	 * 
244
    /**
244
	 * @return true if this algorithm is set to resize elements
245
     * Sets the spring layout length-control.
245
	 */
246
     * 
246
	public boolean isResizing() {
247
     * @param length
247
		return resize;
248
     *            The length-control value.
248
	}
249
     */
249
250
    public void setSpringLength(double length) {
250
	/**
251
        sprLength = length;
251
	 * 
252
    }
252
	 * @param resizing
253
253
	 *            true if this algorithm should resize elements (default is
254
    /**
254
	 *            false)
255
     * Gets the max time this algorithm will run for
255
	 */
256
     * 
256
	public void setResizing(boolean resizing) {
257
     * @return
257
		resize = resizing;
258
     */
258
	}
259
    public long getSpringTimeout() {
259
260
        return maxTimeMS;
260
	/**
261
    }
261
	 * Sets the spring layout move-control.
262
262
	 * 
263
    /**
263
	 * @param move
264
     * Sets the spring timeout
264
	 *            The move-control value.
265
     * 
265
	 */
266
     * @param timeout
266
	public void setSpringMove(double move) {
267
     */
267
		sprMove = move;
268
    public void setSpringTimeout(long timeout) {
268
	}
269
        maxTimeMS = timeout;
269
270
    }
270
	/**
271
271
	 * Returns the move-control value of this SpringLayoutAlgorithm in double
272
    /**
272
	 * presion.
273
     * Returns the length-control value of this SpringLayoutAlgorithm in
273
	 * 
274
     * double presion.
274
	 * @return The move-control value.
275
     * 
275
	 */
276
     * @return The length-control value.
276
	public double getSpringMove() {
277
     */
277
		return sprMove;
278
    public double getSpringLength() {
278
	}
279
        return sprLength;
279
280
    }
280
	/**
281
281
	 * Sets the spring layout strain-control.
282
    /**
282
	 * 
283
     * Sets the spring layout gravitation-control.
283
	 * @param strain
284
     * 
284
	 *            The strain-control value.
285
     * @param gravitation
285
	 */
286
     *            The gravitation-control value.
286
	public void setSpringStrain(double strain) {
287
     */
287
		sprStrain = strain;
288
    public void setSpringGravitation(double gravitation) {
288
	}
289
        sprGravitation = gravitation;
289
290
    }
290
	/**
291
291
	 * Returns the strain-control value of this SpringLayoutAlgorithm in double
292
    /**
292
	 * presion.
293
     * Returns the gravitation-control value of this SpringLayoutAlgorithm
293
	 * 
294
     * in double presion.
294
	 * @return The strain-control value.
295
     * 
295
	 */
296
     * @return The gravitation-control value.
296
	public double getSpringStrain() {
297
     */
297
		return sprStrain;
298
    public double getSpringGravitation() {
298
	}
299
        return sprGravitation;
299
300
    }
300
	/**
301
301
	 * Sets the spring layout length-control.
302
    /**
302
	 * 
303
     * Sets the number of iterations to be used.
303
	 * @param length
304
     * 
304
	 *            The length-control value.
305
     * @param gravitation
305
	 */
306
     *            The number of iterations.
306
	public void setSpringLength(double length) {
307
     */
307
		sprLength = length;
308
    public void setIterations(int iterations) {
308
	}
309
        sprIterations = iterations;
309
310
    }
310
	/**
311
311
	 * Gets the max time this algorithm will run for
312
    /**
312
	 * 
313
     * Returns the number of iterations to be used.
313
	 * @return
314
     * 
314
	 */
315
     * @return The number of iterations.
315
	public long getSpringTimeout() {
316
     */
316
		return maxTimeMS;
317
    public int getIterations() {
317
	}
318
        return sprIterations;
318
319
    }
319
	/**
320
320
	 * Sets the spring timeout
321
    /**
321
	 * 
322
     * Sets whether or not this SpringLayoutAlgorithm will layout the
322
	 * @param timeout
323
     * nodes randomly before beginning iterations.
323
	 */
324
     * 
324
	public void setSpringTimeout(long timeout) {
325
     * @param random
325
		maxTimeMS = timeout;
326
     *            The random placement value.
326
	}
327
     */
327
328
    public void setRandom(boolean random) {
328
	/**
329
        sprRandom = random;
329
	 * Returns the length-control value of this SpringLayoutAlgorithm in double
330
    }
330
	 * presion.
331
331
	 * 
332
    /**
332
	 * @return The length-control value.
333
     * Returns whether or not this SpringLayoutAlgorithm will layout the
333
	 */
334
     * nodes randomly before beginning iterations.
334
	public double getSpringLength() {
335
     */
335
		return sprLength;
336
    public boolean getRandom() {
336
	}
337
        return sprRandom;
337
338
    }
338
	/**
339
339
	 * Sets the spring layout gravitation-control.
340
    public void setWeight(String relType, double weight) {
340
	 * 
341
        relTypeToWeightMap.put(relType, new Double(weight));
341
	 * @param gravitation
342
    }
342
	 *            The gravitation-control value.
343
343
	 */
344
    public double getWeight(String relType) {
344
	public void setSpringGravitation(double gravitation) {
345
        Double weight = (Double) relTypeToWeightMap.get(relType);
345
		sprGravitation = gravitation;
346
        return (weight == null) ? 1 : weight.doubleValue();
346
	}
347
    }
347
348
348
	/**
349
    /**
349
	 * Returns the gravitation-control value of this SpringLayoutAlgorithm in
350
     * Sets the default conditions.
350
	 * double presion.
351
     */
351
	 * 
352
    public void setDefaultConditions() {
352
	 * @return The gravitation-control value.
353
        // sprMove = DEFAULT_SPRING_MOVE;
353
	 */
354
        // sprStrain = DEFAULT_SPRING_STRAIN;
354
	public double getSpringGravitation() {
355
        // sprLength = DEFAULT_SPRING_LENGTH;
355
		return sprGravitation;
356
        // sprGravitation = DEFAULT_SPRING_GRAVITATION;
356
	}
357
        // sprIterations = DEFAULT_SPRING_ITERATIONS;
357
358
    }
358
	/**
359
359
	 * Sets the number of iterations to be used.
360
    /**
360
	 * 
361
     * Clean up after done
361
	 * @param gravitation
362
     * 
362
	 *            The number of iterations.
363
     * @param entitiesToLayout
363
	 */
364
     */
364
	public void setIterations(int iterations) {
365
    private void reset(InternalNode[] entitiesToLayout) {
365
		sprIterations = iterations;
366
        tempLocationsX = null;
366
	}
367
        tempLocationsY = null;
367
368
        forcesX = null;
368
	/**
369
        forcesY = null;
369
	 * Returns the number of iterations to be used.
370
        anchors = null;
370
	 * 
371
        setDefaultConditions();
371
	 * @return The number of iterations.
372
        srcDestToNumRelsMap = new HashMap();
372
	 */
373
        srcDestToRelsAvgWeightMap = new HashMap();
373
	public int getIterations() {
374
        relTypeToWeightMap = new HashMap();
374
		return sprIterations;
375
    }
375
	}
376
376
377
    private long startTime = 0;
377
	/**
378
378
	 * Sets whether or not this SpringLayoutAlgorithm will layout the nodes
379
    protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
379
	 * randomly before beginning iterations.
380
        // TODO: Filter out any non-wanted entities and relationships
380
	 * 
381
        // super.applyLayout(entitiesToLayout, relationshipsToConsider, x, y,
381
	 * @param random
382
        // width, height);
382
	 *            The random placement value.
383
        //InternalNode[] a_entitiesToLayout = (InternalNode[]) entitiesToLayout.toArray(new InternalNode[entitiesToLayout.size()]);
383
	 */
384
    	bounds = new DisplayIndependentRectangle(x,y,width,height);
384
	public void setRandom(boolean random) {
385
        tempLocationsX = new double[entitiesToLayout.length];
385
		sprRandom = random;
386
        tempLocationsY = new double[entitiesToLayout.length];
386
	}
387
        forcesX = new double[entitiesToLayout.length];
387
388
        forcesY = new double[entitiesToLayout.length];
388
	/**
389
        anchors = new boolean[entitiesToLayout.length];
389
	 * Returns whether or not this SpringLayoutAlgorithm will layout the nodes
390
        
390
	 * randomly before beginning iterations.
391
        for (int i = 0; i < entitiesToLayout.length; i++) {
391
	 */
392
            anchors[i] = DEFAULT_ANCHOR;
392
	public boolean getRandom() {
393
        }
393
		return sprRandom;
394
        for (int i = 0; i < relationshipsToConsider.length; i++) {
394
	}
395
            InternalRelationship layoutRelationship = relationshipsToConsider[i];
395
396
            addRelation(layoutRelationship);
396
	private long startTime = 0;
397
        }
397
398
398
	private int[] counter;
399
        // do the calculations
399
400
        preCompute(entitiesToLayout);
400
	private int[] counterX;
401
        startTime = date.getTime();
401
402
    }
402
	private int[] counterY;
403
403
404
    protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
404
	private void initLayout() {
405
        reset(entitiesToLayout);
405
		entities = context.getEntities();
406
    }
406
		bounds = context.getBounds();
407
407
		loadLocations();
408
    /**
408
409
     * Adds a simple relation between two nodes to the relation repository.
409
		srcDestToSumOfWeights = new double[entities.length][entities.length];
410
     * 
410
		HashMap entityToPosition = new HashMap();
411
     * @param layoutRelationship
411
		for (int i = 0; i < entities.length; i++) {
412
     *            The simple relation to be added
412
			entityToPosition.put(entities[i], new Integer(i));
413
     * @throws java.lang.NullPointerExcetption
413
		}
414
     *             If <code>sr</code> is null
414
415
     * @see SimpleRelation
415
		ConnectionLayout[] connections = context.getConnections();
416
     */
416
		for (int i = 0; i < connections.length; i++) {
417
    private void addRelation(InternalRelationship layoutRelationship) {
417
			ConnectionLayout connection = connections[i];
418
        if (layoutRelationship == null) {
418
			Integer source = (Integer) entityToPosition.get(getEntity(connection.getSource()));
419
            throw new IllegalArgumentException("The arguments can not be null!");
419
			Integer target = (Integer) entityToPosition.get(getEntity(connection.getTarget()));
420
        } else {
420
			if (source == null || target == null)
421
            double weight = layoutRelationship.getWeight();
421
				continue;
422
            weight = (weight <= 0 ? 0.1 : weight);
422
			double weight = connection.getWeight();
423
            String key1 = layoutRelationship.getSource().toString() + layoutRelationship.getDestination().toString();
423
			weight = (weight <= 0 ? 0.1 : weight);
424
            String key2 = layoutRelationship.getDestination().toString() + layoutRelationship.getSource().toString();
424
			srcDestToSumOfWeights[source.intValue()][target.intValue()] += weight;
425
            String[] keys = { key1, key2 };
425
			srcDestToSumOfWeights[target.intValue()][source.intValue()] += weight;
426
            for (int i = 0; i < keys.length; i++) {
426
		}
427
                String key = keys[i];
427
428
                Integer count = (Integer) srcDestToNumRelsMap.get(key);
428
		if (sprRandom)
429
                Double avgWeight = (Double) srcDestToRelsAvgWeightMap.get(key);
429
			placeRandomly(); // put vertices in random places
430
                if (count == null) {
430
431
                    count = new Integer(1);
431
		iteration = 1;
432
                    avgWeight = new Double(weight);
432
433
                } else {
433
		startTime = System.currentTimeMillis();
434
                    int newCount = count.intValue() + 1;
434
	}
435
                    double newAverage = (avgWeight.doubleValue() * count.doubleValue() + weight) / newCount;
435
436
                    avgWeight = new Double(newAverage);
436
	private EntityLayout getEntity(NodeLayout node) {
437
                    count = new Integer(newCount);
437
		if (!node.isPruned())
438
                }
438
			return node;
439
                srcDestToNumRelsMap.put(key, count);
439
		SubgraphLayout subgraph = node.getSubgraph();
440
                srcDestToRelsAvgWeightMap.put(key, avgWeight);
440
		if (subgraph.isGraphEntity())
441
            }
441
			return subgraph;
442
        }
442
		return null;
443
    }
443
	}
444
444
445
    private void preCompute(InternalNode [] entitiesToLayout) {
445
	private void loadLocations() {
446
        // count number of relationships between all nodes and the average
446
		if (locationsX == null || locationsX.length != entities.length) {
447
        // weight between them
447
			int length = entities.length;
448
        srcDestToNumRels = new int[entitiesToLayout.length][entitiesToLayout.length];
448
			locationsX = new double[length];
449
        srcDestToRelsAvgWeight = new double[entitiesToLayout.length][entitiesToLayout.length];
449
			locationsY = new double[length];
450
450
			sizeW = new double[length];
451
        for (int i = 0; i < entitiesToLayout.length - 1; i++) {
451
			sizeH = new double[length];
452
            InternalNode layoutEntity1 = entitiesToLayout[i];
452
			forcesX = new double[length];
453
            for (int j = i + 1; j < entitiesToLayout.length; j++) {
453
			forcesY = new double[length];
454
                InternalNode layoutEntity2 = entitiesToLayout[j];
454
			counterX = new int[length];
455
                srcDestToNumRels[i][j] = numRelations(layoutEntity1, layoutEntity2);
455
			counterY = new int[length];
456
                srcDestToNumRels[i][j] += numRelations(layoutEntity2, layoutEntity1);
456
		}
457
                srcDestToRelsAvgWeight[i][j] = avgWeight(layoutEntity1, layoutEntity2);
457
		for (int i = 0; i < entities.length; i++) {
458
            }
458
			DisplayIndependentPoint location = entities[i].getLocation();
459
        }
459
			locationsX[i] = location.x;
460
460
			locationsY[i] = location.y;
461
        if (sprRandom)
461
			DisplayIndependentDimension size = entities[i].getSize();
462
            placeRandomly(entitiesToLayout); // put vertices in random places
462
			sizeW[i] = size.width;
463
        else
463
			sizeH[i] = size.height;
464
            convertToUnitCoordinates(entitiesToLayout);
464
		}
465
465
	}
466
        iteration = 1;
466
467
        largestMovement = Double.MAX_VALUE;
467
	private void saveLocations() {
468
    }
468
		if (entities == null)
469
469
			return;
470
    // TODO: This is a complete Clone! (and not in a good way)
470
		for (int i = 0; i < entities.length; i++) {
471
    protected DisplayIndependentRectangle getLayoutBoundsTemp(InternalNode [] entitiesToLayout, boolean includeNodeSize) {
471
			entities[i].setLocation(locationsX[i], locationsY[i]);
472
        double rightSide = Double.MIN_VALUE;
472
		}
473
        double bottomSide = Double.MIN_VALUE;
473
	}
474
        double leftSide = Double.MAX_VALUE;
474
475
        double topSide = Double.MAX_VALUE;
475
	/**
476
        for (int i = 0; i < entitiesToLayout.length; i++) {
476
	 * Scales the current iteration counter based on how long the algorithm has
477
            double x = tempLocationsX[i];
477
	 * been running for. You can set the MaxTime in maxTimeMS!
478
            double y = tempLocationsY[i];
478
	 */
479
479
	private void setSprIterationsBasedOnTime() {
480
            leftSide = Math.min(x, leftSide);
480
		if (maxTimeMS <= 0)
481
            topSide = Math.min(y, topSide);
481
			return;
482
            rightSide = Math.max(x, rightSide);
482
483
            bottomSide = Math.max(y, bottomSide);
483
		long currentTime = System.currentTimeMillis();
484
484
		double fractionComplete = (double) ((double) (currentTime - startTime) / ((double) maxTimeMS));
485
        }
485
		int currentIteration = (int) (fractionComplete * sprIterations);
486
        return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide);
486
		if (currentIteration > iteration) {
487
    }
487
			iteration = currentIteration;
488
488
		}
489
    protected void convertNodePositionsBack(int i, InternalNode entityToConvert, double px, double py, double screenWidth, double screenHeight, DisplayIndependentRectangle layoutBounds) {
489
490
    	
490
	}
491
    	// If the node selected is outside the screen, map it to the boarder
491
492
    	if ( px > screenWidth ) px = screenWidth;
492
	protected boolean performAnotherNonContinuousIteration() {
493
    	if ( py > screenHeight ) py = screenHeight;
493
		setSprIterationsBasedOnTime();
494
    	
494
		return (iteration <= sprIterations);
495
    	if ( px < 0 ) px = 1;
495
	}
496
    	if ( py < 0 ) py = 1;
496
497
    	
497
	protected int getCurrentLayoutStep() {
498
        double x = (px / screenWidth) * layoutBounds.width + layoutBounds.x;
498
		return iteration;
499
        double y = (py / screenHeight) * layoutBounds.height + layoutBounds.y;
499
	}
500
        
500
501
        tempLocationsX[i] = x;
501
	protected int getTotalNumberOfLayoutSteps() {
502
        tempLocationsY[i] = y;
502
		return sprIterations;
503
        //setTempLocation(entityToConvert, new DisplayIndependentPoint(x, y));
503
	}
504
504
505
        if (entityToConvert.getInternalX() < 0) {
505
	protected void computeOneIteration() {
506
            // System.out.println("We have nodes less than 0 here!");
506
		computeForces();
507
        }
507
		computePositions();
508
508
		DisplayIndependentRectangle currentBounds = getLayoutBounds();
509
    }
509
		improveBoundScaleX(currentBounds);
510
510
		improveBoundScaleY(currentBounds);
511
    private void checkPreferredLocation(InternalNode [] entitiesToLayout, DisplayIndependentRectangle realBounds) {
511
		moveToCenter(currentBounds);
512
        // use 10% for the border - 5% on each side
512
		iteration++;
513
        double borderWidth = Math.min(realBounds.width, realBounds.height) / 10.0; 
513
	}
514
        DisplayIndependentRectangle screenBounds = new DisplayIndependentRectangle(realBounds.x + borderWidth / 2.0, realBounds.y + borderWidth / 2.0, realBounds.width - borderWidth, realBounds.height - borderWidth);
514
515
515
	/**
516
        DisplayIndependentRectangle layoutBounds = getLayoutBoundsTemp(entitiesToLayout, false);
516
	 * Puts vertices in random places, all between (0,0) and (1,1).
517
        for (int i = 0; i < entitiesToLayout.length; i++) {
517
	 */
518
            InternalNode layoutEntity = entitiesToLayout[i];
518
	public void placeRandomly() {
519
            if (layoutEntity.hasPreferredLocation()) {
519
		// If only one node in the data repository, put it in the middle
520
                convertNodePositionsBack(i, layoutEntity, layoutEntity.getPreferredX(), layoutEntity.getPreferredY(), screenBounds.width, screenBounds.height, layoutBounds);
520
		if (locationsX.length == 1) {
521
            }
521
			// If only one node in the data repository, put it in the middle
522
        }
522
			locationsX[0] = bounds.x + 0.5 * bounds.width;
523
    }
523
			locationsY[0] = bounds.y + 0.5 * bounds.height;
524
524
		} else {
525
    /**
525
			locationsX[0] = bounds.x;
526
     * Scales the current iteration counter based on how long the algorithm has
526
			locationsY[0] = bounds.y;
527
     * been running for. You can set the MaxTime in maxTimeMS!
527
			locationsX[1] = bounds.x + bounds.width;
528
     */
528
			locationsY[1] = bounds.y + bounds.height;
529
    private void setSprIterationsBasedOnTime() {
529
			for (int i = 2; i < locationsX.length; i++) {
530
        if (maxTimeMS <= 0)
530
				locationsX[i] = bounds.x + Math.random() * bounds.width;
531
            return;
531
				locationsY[i] = bounds.y + Math.random() * bounds.height;
532
532
			}
533
        long currentTime = date.getTime();
533
		}
534
        double fractionComplete = (double) ((double) (currentTime - startTime) / ((double) maxTimeMS));
534
	}
535
        int currentIteration = (int) (fractionComplete * sprIterations);
535
536
        if (currentIteration > iteration) {
536
	// /////////////////////////////////////////////////////////////////
537
            iteration = currentIteration;
537
	// /// Protected Methods /////
538
        }
538
	// /////////////////////////////////////////////////////////////////
539
539
540
    }
540
	/**
541
541
	 * Computes the force for each node in this SpringLayoutAlgorithm. The
542
    protected boolean performAnotherNonContinuousIteration() {
542
	 * computed force will be stored in the data repository
543
        setSprIterationsBasedOnTime();
543
	 */
544
        if (iteration <= sprIterations && largestMovement >= sprMove)
544
	protected void computeForces() {
545
            return true;
545
546
        else
546
		double forcesX[][] = new double[2][this.forcesX.length];
547
            return false;
547
		double forcesY[][] = new double[2][this.forcesX.length];
548
    }
548
		double locationsX[] = new double[this.forcesX.length];
549
549
		double locationsY[] = new double[this.forcesX.length];
550
    protected int getCurrentLayoutStep() {
550
551
        return iteration;
551
		// // initialize all forces to zero
552
    }
552
		for (int j = 0; j < 2; j++) {
553
553
			for (int i = 0; i < this.forcesX.length; i++) {
554
    protected int getTotalNumberOfLayoutSteps() {
554
				forcesX[j][i] = 0;
555
        return sprIterations;
555
				forcesY[j][i] = 0;
556
    }
556
				locationsX[i] = this.locationsX[i];
557
    
557
				locationsY[i] = this.locationsY[i];
558
    protected void computeOneIteration(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
558
			}
559
    	if ( bounds == null )
559
		}
560
    		bounds = new DisplayIndependentRectangle(x,y,width,height);
560
		// TODO: Again really really slow!
561
        checkPreferredLocation(entitiesToLayout, bounds );
561
562
        computeForces(entitiesToLayout);
562
		for (int k = 0; k < 2; k++) {
563
        largestMovement = Double.MAX_VALUE;
563
			for (int i = 0; i < this.locationsX.length; i++) {
564
        computePositions(entitiesToLayout);
564
565
        
565
				for (int j = i + 1; j < locationsX.length; j++) {
566
        for (int i = 0; i < entitiesToLayout.length; i++) {
566
					double dx = (locationsX[i] - locationsX[j]) / bounds.width / boundsScaleX;
567
            InternalNode layoutEntity = entitiesToLayout[i];
567
					double dy = (locationsY[i] - locationsY[j]) / bounds.height / boundsScaleY;
568
            layoutEntity.setInternalLocation(tempLocationsX[i], tempLocationsY[i]);
568
					double distance_sq = dx * dx + dy * dy;
569
        }        
569
					// make sure distance and distance squared not too small
570
        
570
					distance_sq = Math.max(MIN_DISTANCE * MIN_DISTANCE, distance_sq);
571
        defaultFitWithinBounds(entitiesToLayout, bounds);
571
					double distance = Math.sqrt(distance_sq);
572
572
573
        iteration++;
573
					// If there are relationships between srcObj and destObj
574
    }
574
					// then decrease force on srcObj (a pull) in direction of
575
        
575
					// destObj
576
    /**
576
					// If no relation between srcObj and destObj then increase
577
     * Puts vertices in random places, all between (0,0) and (1,1).
577
					// force on srcObj (a push) from direction of destObj.
578
     */
578
					double sumOfWeights = srcDestToSumOfWeights[i][j];
579
    public void placeRandomly(InternalNode[] entitiesToLayout) {
579
580
        // If only one node in the data repository, put it in the middle
580
					double f;
581
        if (entitiesToLayout.length == 1) {
581
					if (sumOfWeights > 0) {
582
            // If only one node in the data repository, put it in the middle
582
						// nodes are pulled towards each other
583
            tempLocationsX[0] = 0.5;
583
						f = -sprStrain * Math.log(distance / sprLength) * sumOfWeights;
584
            tempLocationsY[0] = 0.5;
584
					} else {
585
        } else {
585
						// nodes are repelled from each other
586
            for (int i = 0; i < entitiesToLayout.length; i++) {
586
						f = sprGravitation / (distance_sq);
587
                if (i == 0) {
587
					}
588
                    tempLocationsX[i] = 0.0;
588
					double dfx = f * dx / distance;
589
                    tempLocationsY[i] = 0.0;
589
					double dfy = f * dy / distance;
590
                } else if (i == 1) {
590
591
                    tempLocationsX[i] = 1.0;
591
					forcesX[k][i] += dfx;
592
                    tempLocationsY[i] = 1.0;
592
					forcesY[k][i] += dfy;
593
                } else {
593
594
                    tempLocationsX[i] = Math.random();
594
					forcesX[k][j] -= dfx;
595
                    tempLocationsY[i] = Math.random();
595
					forcesY[k][j] -= dfy;
596
                }
596
				}
597
            }
597
			}
598
        }
598
599
    }
599
			for (int i = 0; i < entities.length; i++) {
600
600
				if (entities[i].isMovable()) {
601
    // /////////////////////////////////////////////////////////////////
601
					double deltaX = sprMove * forcesX[k][i];
602
    // /// Protected Methods /////
602
					double deltaY = sprMove * forcesY[k][i];
603
    // /////////////////////////////////////////////////////////////////
603
604
604
					// constrain movement, so that nodes don't shoot way off to
605
    /**
605
					// the
606
     * Computes the force for each node in this SpringLayoutAlgorithm. The
606
					// edge
607
     * computed force will be stored in the data repository
607
					double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
608
     */
608
					double maxMovement = 0.2d * sprMove;
609
    protected void computeForces(InternalNode[] entitiesToLayout) {
609
					if (dist > maxMovement) {
610
610
						deltaX *= maxMovement / dist;
611
        // initialize all forces to zero
611
						deltaY *= maxMovement / dist;
612
        for (int i = 0; i < entitiesToLayout.length; i++) {
612
					}
613
            forcesX[i] = 0.0;
613
614
            forcesY[i] = 0.0;
614
					locationsX[i] += deltaX * bounds.width * boundsScaleX;
615
        }
615
					locationsY[i] += deltaY * bounds.height * boundsScaleY;
616
616
				}
617
        // TODO: Again really really slow!
617
			}
618
618
619
        for (int i = 0; i < entitiesToLayout.length - 1; i++) {
619
		}
620
            InternalNode sourceEntity = entitiesToLayout[i];
620
		// // initialize all forces to zero
621
            
621
		for (int i = 0; i < this.entities.length; i++) {
622
            double srcLocationX = tempLocationsX[i];
622
			if (forcesX[0][i] * forcesX[1][i] < 0) {
623
            double srcLocationY = tempLocationsY[i];
623
				this.forcesX[i] = 0;
624
            double fx = forcesX[i]; // force in x direction
624
				// } else if ( this.locationsX[i] < 0 ) {
625
            double fy = forcesY[i]; // force in y direction
625
				// this.forcesX[i] = forcesX[1][i] / 10;
626
            
626
				// } else if ( this.locationsX[i] > boundsScale * bounds.width)
627
627
				// {
628
            for (int j = i + 1; j < entitiesToLayout.length; j++) {
628
				// this.forcesX[i] = forcesX[1][i] / 10;
629
                InternalNode destinationEntity = entitiesToLayout[j];
629
			} else {
630
630
				this.forcesX[i] = forcesX[1][i];
631
                if (!destinationEntity.equals(sourceEntity)) {
631
			}
632
                    double destLocationX = tempLocationsX[j];
632
633
                    double destLocationY = tempLocationsY[j];
633
			if (forcesY[0][i] * forcesY[1][i] < 0) {
634
                    double dx = srcLocationX - destLocationX;
634
				this.forcesY[i] = 0;
635
                    double dy = srcLocationY- destLocationY;
635
				// } else if ( this.locationsY[i] < 0 ) {
636
                    double distance = Math.sqrt(dx * dx + dy * dy);
636
				// this.forcesY[i] = forcesY[1][i] / 10;
637
                    double distance_sq = distance * distance;
637
				// } else if ( this.locationsY[i] > boundsScale * bounds.height)
638
                    // make sure distance and distance squared not too small
638
				// {
639
                    distance = Math.max(MIN_DISTANCE, distance);
639
				// this.forcesY[i] = forcesY[1][i] / 10;
640
640
			} else {
641
                    // If there are relationships between srcObj and destObj
641
				this.forcesY[i] = forcesY[1][i];
642
                    // then decrease force on srcObj (a pull) in direction of destObj
642
			}
643
                    // If no relation between srcObj and destObj then increase
643
644
                    // force on srcObj (a push) from direction of destObj.
644
		}
645
                    int numRels = srcDestToNumRels[i][j];
645
646
                    double avgWeight = srcDestToRelsAvgWeight[i][j];
646
	}
647
                    if (numRels > 0) {
647
648
                        // nodes are pulled towards each other
648
	/**
649
                        double f = sprStrain * Math.log(distance / sprLength) * numRels * avgWeight;
649
	 * Computes the position for each node in this SpringLayoutAlgorithm. The
650
                        
650
	 * computed position will be stored in the data repository. position =
651
                        fx = fx - (f * dx / distance);
651
	 * position + sprMove * force
652
                        fy = fy - (f * dy / distance);
652
	 */
653
                        
653
	protected void computePositions() {
654
                    } else {
654
		for (int i = 0; i < entities.length; i++) {
655
                        // nodes are repelled from each other
655
			if (entities[i].isMovable()) {
656
                        //double f = Math.min(100, sprGravitation / (distance*distance));
656
				double deltaX = sprMove * forcesX[i];
657
                        double f = sprGravitation / (distance_sq);
657
				double deltaY = sprMove * forcesY[i];
658
                        fx = fx + (f * dx / distance);
658
659
                        fy = fy + (f * dy / distance);
659
				// constrain movement, so that nodes don't shoot way off to the
660
                    }
660
				// edge
661
661
				double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
662
                    // According to Newton, "for every action, there is an equal
662
				double maxMovement = 0.2d * sprMove;
663
                    // and opposite reaction."
663
				if (dist > maxMovement) {
664
                    // so give the dest an opposite force
664
					deltaX *= maxMovement / dist;
665
                    forcesX[j] = forcesX[j] - fx;
665
					deltaY *= maxMovement / dist;
666
                    forcesY[j] = forcesY[j] - fy;
666
				}
667
                } 
667
668
            } 
668
				locationsX[i] += deltaX * bounds.width * boundsScaleX;
669
669
				locationsY[i] += deltaY * bounds.height * boundsScaleY;
670
            /*
670
			}
671
             * //make sure forces aren't too big if (fx > 0 ) fx = Math.min(fx,
671
		}
672
             * 10*sprMove); else fx = Math.max(fx, -10*sprMove); if (fy > 0) fy =
672
	}
673
             * Math.min(fy, 10*sprMove); else fy = Math.max(fy, -10*sprMove);
673
674
             */
674
	private DisplayIndependentRectangle getLayoutBounds() {
675
            forcesX[i] = fx;
675
		double minX, maxX, minY, maxY;
676
            forcesY[i] = fy;
676
		minX = minY = Double.POSITIVE_INFINITY;
677
            // Remove the src object from the list of destinations since
677
		maxX = maxY = Double.NEGATIVE_INFINITY;
678
            // we've already calculated the force from it on all other
678
679
            // objects.
679
		for (int i = 0; i < locationsX.length; i++) {
680
            // dests.remove(srcObj);
680
			maxX = Math.max(maxX, locationsX[i] + sizeW[i] / 2);
681
681
			minX = Math.min(minX, locationsX[i] - sizeW[i] / 2);
682
        }
682
			maxY = Math.max(maxY, locationsY[i] + sizeH[i] / 2);
683
    }
683
			minY = Math.min(minY, locationsY[i] - sizeH[i] / 2);
684
684
		}
685
    /**
685
		return new DisplayIndependentRectangle(minX, minY, maxX - minX, maxY - minY);
686
     * Computes the position for each node in this SpringLayoutAlgorithm.
686
	}
687
     * The computed position will be stored in the data repository. position =
687
688
     * position + sprMove * force
688
	private void improveBoundScaleX(DisplayIndependentRectangle currentBounds) {
689
     */
689
		double boundaryProportionX = currentBounds.width / bounds.width;
690
    protected void computePositions(InternalNode[] entitiesToLayout) {
690
		// double boundaryProportion = Math.max(currentBounds.width /
691
        for (int i = 0; i < entitiesToLayout.length; i++) {
691
		// bounds.width, currentBounds.height / bounds.height);
692
            if (!anchors[i] || entitiesToLayout[i].hasPreferredLocation() ) {
692
693
                double oldX = tempLocationsX[i];
693
		// if (boundaryProportionX < 0.1)
694
                double oldY = tempLocationsY[i];
694
		// boundsScaleX *= 2;
695
                double deltaX = sprMove * forcesX[i];
695
		// else if (boundaryProportionX < 0.5)
696
                double deltaY = sprMove * forcesY[i];
696
		// boundsScaleX *= 1.4;
697
697
		// else if (boundaryProportionX < 0.8)
698
                // constrain movement, so that nodes don't shoot way off to the edge
698
		// boundsScaleX *= 1.1;
699
                double maxMovement = 0.2d * sprMove;
699
		if (boundaryProportionX < 0.9) {
700
                if (deltaX >= 0) {
700
			boundsScaleX *= 1.01;
701
                    deltaX = Math.min(deltaX, maxMovement);
701
702
                } else {
702
			//
703
                    deltaX = Math.max(deltaX, -maxMovement);
703
			// else if (boundaryProportionX > 1.8) {
704
                }
704
			// if (boundsScaleX < 0.01)
705
                if (deltaY >= 0) {
705
			// return;
706
                    deltaY = Math.min(deltaY, maxMovement);
706
			// boundsScaleX /= 1.05;
707
                } else {
707
		} else if (boundaryProportionX > 1) {
708
                    deltaY = Math.max(deltaY, -maxMovement);
708
			if (boundsScaleX < 0.01)
709
                }
709
				return;
710
                
710
			boundsScaleX /= 1.01;
711
711
		}
712
                largestMovement = Math.max(largestMovement, Math.abs(deltaX));
712
	}
713
                largestMovement = Math.max(largestMovement, Math.abs(deltaY));
713
714
714
	private void improveBoundScaleY(DisplayIndependentRectangle currentBounds) {
715
                double newX = oldX + deltaX;
715
		double boundaryProportionY = currentBounds.height / bounds.height;
716
                double newY = oldY + deltaY;
716
		// double boundaryProportion = Math.max(currentBounds.width /
717
                tempLocationsX[i] = newX;
717
		// bounds.width, currentBounds.height / bounds.height);
718
                tempLocationsY[i] = newY;
718
719
            }
719
		// if (boundaryProportionY < 0.1)
720
            
720
		// boundsScaleY *= 2;
721
        }
721
		// else if (boundaryProportionY < 0.5)
722
722
		// boundsScaleY *= 1.4;
723
    }
723
		// else if (boundaryProportionY < 0.8)
724
724
		// boundsScaleY *= 1.1;
725
    /**
725
		if (boundaryProportionY < 0.9) {
726
     * Converts the position for each node in this SpringLayoutAlgorithm
726
			boundsScaleY *= 1.01;
727
     * to unit coordinates in double precision. The computed positions will be
727
728
     * still stored in the data repository.
728
			// else if (boundaryProportionY > 1.8) {
729
     */
729
			// if (boundsScaleY < 0.01)
730
    protected void convertToUnitCoordinates(InternalNode[] entitiesToLayout) {
730
			// return;
731
        double minX = Double.MAX_VALUE;
731
			// boundsScaleY /= 1.05;
732
        double maxX = Double.MIN_VALUE;
732
		} else if (boundaryProportionY > 1) {
733
        double minY = Double.MAX_VALUE;
733
			if (boundsScaleY < 0.01)
734
        double maxY = Double.MIN_VALUE;
734
				return;
735
        for (int i = 0; i < entitiesToLayout.length; i++) {
735
			boundsScaleY /= 1.01;
736
            InternalNode layoutEntity = entitiesToLayout[i];
736
		}
737
            minX = Math.min(minX, layoutEntity.getInternalX());
737
	}
738
            minY = Math.min(minY, layoutEntity.getInternalY());
738
739
            maxX = Math.max(maxX, layoutEntity.getInternalX());
739
	// private void improveBoundsScale(DisplayIndependentRectangle
740
            maxY = Math.max(maxY, layoutEntity.getInternalY());
740
	// currentBounds) {
741
        }
741
	// double boundaryProportionX = currentBounds.width / bounds.width;
742
742
	// double boundaryProportionY = currentBounds.height / bounds.height;
743
        double spanX = maxX - minX;
743
	// // double boundaryProportion = Math.max(currentBounds.width /
744
        double spanY = maxY - minY;
744
	// // bounds.width, currentBounds.height / bounds.height);
745
        double maxSpan = Math.max(spanX, spanY);
745
	//
746
746
	// if (boundaryProportion < 0.1)
747
        if (maxSpan > EPSILON) {
747
	// boundsScale *= 2;
748
            for (int i = 0; i < entitiesToLayout.length; i++) {
748
	// else if (boundaryProportion < 0.5)
749
                InternalNode layoutEntity = entitiesToLayout[i];
749
	// boundsScale *= 1.4;
750
                double x = (layoutEntity.getInternalX() - minX) / spanX;
750
	// else if (boundaryProportion < 0.8)
751
                double y = (layoutEntity.getInternalY() - minY) / spanY;
751
	// boundsScale *= 1.1;
752
                tempLocationsX[i] = x;
752
	// else if (boundaryProportion < 0.99)
753
                tempLocationsY[i] = y;
753
	// boundsScale *= 1.05;
754
            }
754
	//
755
        } else {
755
	// else if (boundaryProportion > 1.8) {
756
            placeRandomly(entitiesToLayout);
756
	// if (boundsScale < 0.01)
757
        }
757
	// return;
758
    }
758
	// boundsScale /= 1.05;
759
759
	// }
760
    /**
760
	// else if (boundaryProportion > 1) {
761
     * Examines the number of specified relation between the <code>src</code>
761
	// if (boundsScale < 0.01)
762
     * and the <code>dest</code> that exist in this
762
	// return;
763
     * SpringLayoutAlgorithm's relation repository.
763
	// boundsScale /= 1.01;
764
     * 
764
	// }
765
     * @param src
765
	//
766
     *            The source part of the relaton to be examined.
766
	// }
767
     * @param dest
767
768
     *            The destination part of the relation to be examined.
768
	private void moveToCenter(DisplayIndependentRectangle currentBounds) {
769
     * @return The number of relations between src and dest.
769
		double moveX = (currentBounds.x + currentBounds.width / 2) - (bounds.x + bounds.width / 2);
770
     */
770
		double moveY = (currentBounds.y + currentBounds.height / 2) - (bounds.y + bounds.height / 2);
771
    private int numRelations(Object src, Object dest) {
771
		for (int i = 0; i < locationsX.length; i++) {
772
        String key = src.toString() + dest.toString();
772
			locationsX[i] -= moveX;
773
        Integer count = (Integer) srcDestToNumRelsMap.get(key);
773
			locationsY[i] -= moveY;
774
        int intCount = (count == null) ? 0 : count.intValue();
774
		}
775
        return intCount;
775
	}
776
    }
776
}
777
778
    /**
779
     * Returns the average weight between a src and dest object.
780
     * 
781
     * @param src
782
     * @param dest
783
     * @return The average weight between the given src and dest nodes
784
     */
785
    private double avgWeight(Object src, Object dest) {
786
        String key = src.toString() + dest.toString();
787
        Double avgWeight = (Double) srcDestToRelsAvgWeightMap.get(key);
788
        double doubleWeight = (avgWeight == null) ? 1 : avgWeight.doubleValue();
789
        return doubleWeight;
790
    }
791
792
793
    protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {
794
        if (asynchronous && continueous)
795
            return true;
796
        else if (asynchronous && !continueous)
797
            return true;
798
        else if (!asynchronous && continueous)
799
            return false;
800
        else if (!asynchronous && !continueous)
801
            return true;
802
803
        return false;
804
    }
805
806
807
}
808
(-)src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java (-580 / +178 lines)
Lines 1-580 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
11
package org.eclipse.zest.layouts.algorithms;
12
12
13
import java.util.ArrayList;
13
import java.util.Iterator;
14
import java.util.Arrays;
14
15
import java.util.Collection;
15
import org.eclipse.zest.layouts.LayoutAlgorithm;
16
import java.util.Collections;
16
import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver.TreeNode;
17
import java.util.Comparator;
17
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
18
import java.util.HashSet;
18
import org.eclipse.zest.layouts.interfaces.EntityLayout;
19
import java.util.Iterator;
19
import org.eclipse.zest.layouts.interfaces.LayoutContext;
20
import java.util.List;
20
21
import java.util.Set;
21
/**
22
22
 * The TreeLayoutAlgorithm class implements a simple algorithm to arrange graph
23
import org.eclipse.zest.layouts.LayoutStyles;
23
 * nodes in a layered tree-like layout.
24
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
24
 * 
25
import org.eclipse.zest.layouts.dataStructures.InternalNode;
25
 * @version 3.0
26
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
26
 * @author Mateusz Matela
27
import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship;
27
 * @author Casey Best and Rob Lintern (version 2.0)
28
28
 * @author Jingwei Wu (version 1.0)
29
29
 */
30
30
public class TreeLayoutAlgorithm implements LayoutAlgorithm {
31
/**
31
32
 * The TreeLayoutAlgorithm class implements a simple algorithm to
32
	/**
33
 * arrange graph nodes in a layered vertical tree-like layout. 
33
	 * Tree direction constant for which root is placed at the top and branches
34
 * 
34
	 * spread downwards
35
 * This is by no means an efficiently coded algorithm.
35
	 */
36
 *
36
	public final static int TOP_DOWN = 1;
37
 * @version  2.0
37
38
 * @author   Casey Best and Rob Lintern (version 1.0 by Jingwei Wu)
38
	/**
39
 */
39
	 * Tree direction constant for which root is placed at the bottom and
40
public class TreeLayoutAlgorithm extends AbstractLayoutAlgorithm {
40
	 * branches spread upwards
41
   
41
	 */
42
    private final static double DEFAULT_WEIGHT = 0;
42
	public final static int BOTTOM_UP = 2;
43
    private final static boolean DEFAULT_MARKED = false;
43
44
    
44
	/**
45
	private final static boolean AS_DESTINATION = false;
45
	 * Tree direction constant for which root is placed at the left and branches
46
	private final static boolean AS_SOURCE = true;
46
	 * spread to the right
47
    
47
	 */
48
    private final static int NUM_DESCENDENTS_INDEX = 0;
48
	public final static int LEFT_RIGHT = 3;
49
    private final static int NUM_LEVELS_INDEX = 1;
49
50
    
50
	/**
51
    private ArrayList treeRoots;
51
	 * Tree direction constant for which root is placed at the right and
52
    
52
	 * branches spread to the left
53
    private double boundsX;
53
	 */
54
    private double boundsY;
54
	public final static int RIGHT_LEFT = 4;
55
    private double boundsWidth;
55
56
    private double boundsHeight;
56
	private int direction = TOP_DOWN;
57
    private DisplayIndependentRectangle layoutBounds = null;
57
58
    
58
	private boolean resize = false;
59
    private List [] parentLists;
59
60
    private List [] childrenLists;
60
	private LayoutContext context;
61
    private double [] weights;
61
62
    private boolean [] markedArr;
62
	private DisplayIndependentRectangle bounds;
63
	
63
64
	/////////////////////////////////////////////////////////////////////////
64
	private double leafSize, layerSize;
65
	/////                        Constructors                           /////
65
66
	/////////////////////////////////////////////////////////////////////////
66
	private TreeLayoutObserver treeObserver;
67
67
68
	/**
68
	public TreeLayoutAlgorithm() {
69
	 * Constructs a new TreeLayoutAlgorithm object.
69
	}
70
	 */
70
71
	public TreeLayoutAlgorithm( int styles ) {
71
	public TreeLayoutAlgorithm(int direction) {
72
		super( styles );
72
		setDirection(direction);
73
	}
73
	}
74
	
74
75
	/**
75
	public int getDirection() {
76
	 * Tree layout algorithm Constructor with NO Style
76
		return direction;
77
	 *
77
	}
78
	 */
78
79
	public TreeLayoutAlgorithm() {
79
	public void setDirection(int direction) {
80
		this( LayoutStyles.NONE );
80
		if (direction == TOP_DOWN || direction == BOTTOM_UP || direction == LEFT_RIGHT || direction == RIGHT_LEFT)
81
	}
81
			this.direction = direction;
82
	
82
		else
83
	/////////////////////////////////////////////////////////////////////////
83
			throw new IllegalArgumentException("Invalid direction: " + direction);
84
	/////                        Public Methods                         /////
84
	}
85
	/////////////////////////////////////////////////////////////////////////
85
86
	
86
	/**
87
	public void setLayoutArea(double x, double y, double width, double height) {
87
	 * 
88
		throw new RuntimeException();
88
	 * @return true if this algorithm is set to resize elements
89
	}
89
	 */
90
	
90
	public boolean isResizing() {
91
	protected int getCurrentLayoutStep() {
91
		return resize;
92
		// TODO Auto-generated method stub
92
	}
93
		return 0;
93
94
	}
94
	/**
95
	
95
	 * 
96
	protected int getTotalNumberOfLayoutSteps() {
96
	 * @param resizing
97
		return 4;
97
	 *            true if this algorithm should resize elements (default is
98
	}
98
	 *            false)
99
99
	 */
100
	/**
100
	public void setResizing(boolean resizing) {
101
	 * Executes this TreeLayoutAlgorithm layout algorithm by referencing the
101
		resize = resizing;
102
	 * data stored in the repository system. Once done, the result
102
	}
103
	 * will be saved to the data repository.
103
104
	 * 
104
	public void setLayoutContext(LayoutContext context) {
105
	 * @param entitiesToLayout Apply the algorithm to these entities
105
		if (treeObserver != null) {
106
	 * @param relationshipsToConsider Only consider these relationships when applying the algorithm.
106
			treeObserver.stop();
107
	 * @param boundsX The left side of the bounds in which the layout can place the entities.
107
		}
108
	 * @param boundsY The top side of the bounds in which the layout can place the entities.
108
		this.context = context;
109
	 * @param boundsWidth The width of the bounds in which the layout can place the entities.
109
		treeObserver = new TreeLayoutObserver(context, null);
110
	 * @param boundsHeight The height of the bounds in which the layout can place the entities.
110
	}
111
	 * @throws RuntimeException Thrown if entitiesToLayout doesn't contain all of the endpoints for each relationship in relationshipsToConsider
111
112
	 */
112
	public void applyLayout(boolean clean) {
113
	protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
113
		if (!clean)
114
		// Filter unwanted entities and relationships
114
			return;
115
		//super.applyLayout (entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight);
115
116
116
		internalApplyLayout();
117
        parentLists = new List [entitiesToLayout.length];
117
118
        childrenLists = new List [entitiesToLayout.length];
118
		EntityLayout[] entities = context.getEntities();
119
        weights = new double [entitiesToLayout.length];
119
120
        markedArr = new boolean [entitiesToLayout.length];
120
		if (resize)
121
        for (int i = 0; i < entitiesToLayout.length; i++) {
121
			AlgorithmHelper.maximizeSizes(entities);
122
            parentLists[i] = new ArrayList();
122
123
            childrenLists[i] = new ArrayList();
123
		DisplayIndependentRectangle bounds2 = new DisplayIndependentRectangle(bounds);
124
            weights[i] = DEFAULT_WEIGHT;
124
		int insets = 4;
125
            markedArr[i] = DEFAULT_MARKED;
125
		bounds2.x += insets;
126
        }
126
		bounds2.y += insets;
127
        
127
		bounds2.width -= 2 * insets;
128
		this.boundsHeight = height;
128
		bounds2.height -= 2 * insets;
129
		this.boundsWidth = width;
129
		AlgorithmHelper.fitWithinBounds(entities, bounds2, resize);
130
		this.boundsX = x;
130
	}
131
		this.boundsY = y;
131
132
		layoutBounds = new DisplayIndependentRectangle(boundsX, boundsY, boundsWidth, boundsHeight);
132
	void internalApplyLayout() {
133
	
133
		TreeNode superRoot = treeObserver.getSuperRoot();
134
	}
134
		bounds = context.getBounds();
135
135
		if (direction == TOP_DOWN || direction == BOTTOM_UP) {
136
	protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) {
136
			leafSize = bounds.width / superRoot.numOfLeaves;
137
		
137
			layerSize = bounds.height / superRoot.height;
138
		if (entitiesToLayout.length > 0) {
138
		} else {
139
			int totalProgress = 4;	
139
			leafSize = bounds.height / superRoot.numOfLeaves;
140
			fireProgressEvent (1, totalProgress);
140
			layerSize = bounds.width / superRoot.height;
141
			
141
		}
142
			//List roots = new ArrayList();
142
		int leafCountSoFar = 0;
143
			treeRoots = new ArrayList();
143
		for (Iterator iterator = superRoot.getChildren().iterator(); iterator.hasNext();) {
144
			buildForest(treeRoots, entitiesToLayout, relationshipsToConsider);
144
			TreeNode rootInfo = (TreeNode) iterator.next();
145
			fireProgressEvent (2, totalProgress);
145
			computePositionRecursively(rootInfo, leafCountSoFar);
146
			computePositions(treeRoots, entitiesToLayout);
146
			leafCountSoFar = leafCountSoFar + rootInfo.numOfLeaves;
147
			fireProgressEvent (3, totalProgress);
147
		}
148
			defaultFitWithinBounds(entitiesToLayout, layoutBounds);
148
	}
149
149
150
		}
150
	/**
151
	}
151
	 * Computes positions recursively until the leaf nodes are reached.
152
	
152
	 */
153
	protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
153
	private void computePositionRecursively(TreeNode entityInfo, int relativePosition) {
154
		updateLayoutLocations(entitiesToLayout);
154
		double breadthPosition = relativePosition + entityInfo.numOfLeaves / 2.0;
155
		fireProgressEvent (4, 4);
155
		double depthPosition = (entityInfo.depth + 0.5);
156
	}
156
157
	
157
		switch (direction) {
158
	/**
158
		case TOP_DOWN:
159
	 * Returns the last found roots
159
			entityInfo.getNode().setLocation(breadthPosition * leafSize, depthPosition * layerSize);
160
	 */
160
			break;
161
	public List getRoots () {
161
		case BOTTOM_UP:
162
		return treeRoots;
162
			entityInfo.getNode().setLocation(breadthPosition * leafSize, bounds.height - depthPosition * layerSize);
163
	}
163
			break;
164
164
		case LEFT_RIGHT:
165
	/**
165
			entityInfo.getNode().setLocation(depthPosition * layerSize, breadthPosition * leafSize);
166
	 * Finds all the relationships in which the node <code>obj<code>
166
			break;
167
	 * plays the specified <code>role</code>.
167
		case RIGHT_LEFT:
168
	 * @param entity The node that concerns the relations to be found.
168
			entityInfo.getNode().setLocation(bounds.width - depthPosition * layerSize, breadthPosition * leafSize);
169
	 * @param role The role played by the <code>obj</code>. Its type
169
			break;
170
	 * must be of <code>ACTOR_ROLE</code> or <code>ACTEE_ROLE</code>.
170
		}
171
	 * @see SimpleRelationship
171
172
	 */
172
		for (Iterator iterator = entityInfo.children.iterator(); iterator.hasNext();) {
173
    private Collection findRelationships(Object entity, boolean objectAsSource, InternalRelationship [] relationshipsToConsider) {
173
			TreeNode childInfo = (TreeNode) iterator.next();
174
		Collection foundRels = new ArrayList();
174
			computePositionRecursively(childInfo, relativePosition);
175
        for (int i = 0; i < relationshipsToConsider.length; i++) {
175
			relativePosition += childInfo.numOfLeaves;
176
            InternalRelationship rel = relationshipsToConsider[i];
176
		}
177
			if (objectAsSource && rel.getSource().equals (entity)) {
177
	}
178
				foundRels.add(rel);
178
} 
179
			} else if (!objectAsSource && rel.getDestination().equals (entity)) {
180
				foundRels.add(rel);
181
			}
182
		}
183
		return foundRels;
184
	}
185
186
	/**
187
	 * Finds the relation that has the lowest index in the relation
188
	 * repository in which the node <code>obj<code> plays the specified
189
	 * <code>role</code>.
190
	 * @param obj The node that concerns the relations to be found.
191
	 * @param role The role played by the <code>obj</code>. Its type must
192
	 * be of <code>ACTOR_ROLE</code> or <code>ACTEE_ROLE</code>.
193
	 * @see SimpleRelationship
194
	 * @see SimpleRelationship#ACTOR_ROLE
195
	 * @see SimpleRelationship#ACTEE_ROLE
196
	 */
197
    private InternalRelationship findRelationship(Object entity, boolean objectAsSource, InternalRelationship [] relationshipsToConsider) {
198
		InternalRelationship relationship = null;
199
        for (int i = 0; i < relationshipsToConsider.length && relationship == null; i++) {
200
            InternalRelationship possibleRel = relationshipsToConsider[i];
201
			if (objectAsSource && possibleRel.getSource().equals (entity)) {
202
				relationship = possibleRel;
203
			} else if (!objectAsSource && possibleRel.getDestination().equals (entity)) {
204
				relationship = possibleRel;
205
			}
206
		}
207
		return relationship;
208
	}
209
210
211
212
	/////////////////////////////////////////////////////////////////////////
213
	/////                        Private Methods                        /////
214
	/////////////////////////////////////////////////////////////////////////
215
216
217
	/**
218
	 * Builds the tree forest that is used to calculate positions
219
	 * for each node in this TreeLayoutAlgorithm.
220
	 */
221
	private void buildForest(List roots, InternalNode [] entities, InternalRelationship [] relationships) {
222
		List unplacedEntities = new ArrayList (Arrays.asList(entities));
223
		buildForestRecursively(roots, unplacedEntities, entities, relationships);
224
	}
225
226
	/**
227
	 * Builds the forest recursively. All entities
228
	 * will be placed somewhere in the forest. 
229
	 */
230
	private void buildForestRecursively(List roots, List unplacedEntities, InternalNode [] entities, InternalRelationship [] relationships) {
231
		if (unplacedEntities.size() == 0) {
232
			return; // no more entities to place
233
		}
234
		
235
		// get the first entity in the list of unplaced entities, find its root, and build this root's tree
236
		InternalNode layoutEntity = (InternalNode) unplacedEntities.get(0);
237
		InternalNode rootEntity = findRootObjectRecursive(layoutEntity, new HashSet(), relationships);
238
        int rootEntityIndex = indexOfInternalNode(entities, rootEntity);
239
		buildTreeRecursively(rootEntity, rootEntityIndex, 0, entities, relationships);
240
		roots.add(rootEntity);
241
		
242
		// now see which nodes are left to be placed in a tree somewhere
243
		List unmarkedCopy = new ArrayList(unplacedEntities);
244
		for (Iterator iter = unmarkedCopy.iterator(); iter.hasNext();) {
245
            InternalNode tmpEntity = (InternalNode) iter.next();
246
            int tmpEntityIndex = indexOfInternalNode(entities, tmpEntity);
247
            boolean isMarked = markedArr[tmpEntityIndex];
248
 			if (isMarked) {
249
 			   unplacedEntities.remove(tmpEntity);
250
			}
251
		}
252
		buildForestRecursively(roots, unplacedEntities, entities, relationships);
253
	}
254
255
	/**
256
	 * Finds the root node that can be treated as the root of a tree.
257
	 * The found root node should be one of the unmarked nodes.
258
	 */
259
	private InternalNode findRootObjectRecursive(InternalNode currentEntity, Set seenAlready, InternalRelationship [] relationshipsToConsider) {
260
		InternalNode rootEntity = null;
261
		InternalRelationship rel = findRelationship(currentEntity, AS_DESTINATION, relationshipsToConsider);
262
		if (rel == null) {
263
			rootEntity = currentEntity;
264
		} else {
265
			InternalNode parentEntity = rel.getSource();
266
			if (!seenAlready.contains(parentEntity)) {
267
				seenAlready.add(parentEntity);
268
				rootEntity = findRootObjectRecursive(parentEntity, seenAlready, relationshipsToConsider);
269
			} else {
270
				rootEntity = currentEntity;
271
			}
272
		}
273
		return rootEntity;
274
	}
275
276
277
	
278
	/**
279
	 * Builds a tree of the passed in entity.
280
	 * The entity will pass a weight value to all of its children recursively.
281
	 */
282
	private void buildTreeRecursively(InternalNode layoutEntity, int i, double weight, InternalNode [] entities, final InternalRelationship [] relationships) {
283
		// No need to do further computation!
284
		if (layoutEntity == null) {
285
			return;
286
		}
287
288
		// A marked entity means that it has been added to the
289
		// forest, and its weight value needs to be modified.		
290
		if (markedArr[i]) {
291
			modifyWeightRecursively(layoutEntity, i, weight, new HashSet(), entities, relationships);
292
			return; //No need to do further computation.
293
		}
294
295
		// Mark this entity, set its weight value and create a new tree node.
296
        markedArr[i] = true;
297
		weights[i] = weight;
298
		
299
		// collect the children of this entity and put them in order
300
		Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships);
301
		List children = new ArrayList ();
302
		for (Iterator iter = rels.iterator(); iter.hasNext();) {
303
			InternalRelationship layoutRel = (InternalRelationship) iter.next();
304
			InternalNode childEntity = layoutRel.getDestination();
305
			children.add(childEntity);
306
		}
307
		
308
		if (comparator != null) {
309
			Collections.sort(children, comparator); 
310
		} else {
311
            // sort the children by level, then by number of descendents, then by number of children
312
            // TODO: SLOW
313
		    Collections.sort(children, new Comparator () {           
314
                public int compare(Object o1, Object o2) {
315
                    InternalNode node1 = (InternalNode) o1;
316
                    InternalNode node2 = (InternalNode) o2;
317
                    int [] numDescendentsAndLevel1 = new int [2];
318
                    int [] numDescendentsAndLevel2 = new int [2];
319
                    int level1 = numDescendentsAndLevel1[NUM_LEVELS_INDEX];
320
                    int level2 = numDescendentsAndLevel2[NUM_LEVELS_INDEX];
321
                    if (level1 == level2) {
322
                        getNumDescendentsAndLevel(node1, relationships, numDescendentsAndLevel1);
323
                        getNumDescendentsAndLevel(node2, relationships, numDescendentsAndLevel2);
324
                        int numDescendents1 = numDescendentsAndLevel1[NUM_DESCENDENTS_INDEX];
325
                        int numDescendents2 = numDescendentsAndLevel2[NUM_DESCENDENTS_INDEX];
326
                        if (numDescendents1 == numDescendents2) {
327
                            int numChildren1 = getNumChildren(node1, relationships);
328
                            int numChildren2 = getNumChildren(node1, relationships);
329
                            return numChildren2 - numChildren1;
330
                        } else {
331
                            return numDescendents2 - numDescendents1;
332
                        }
333
                    } else {
334
                        return level2 - level1;
335
                    }
336
                    //return getNumChildren(node2, relationships) - getNumChildren(node1, relationships);
337
                }           
338
            });
339
		}
340
		
341
		// map children to this parent, and vice versa
342
		for (Iterator iter = children.iterator(); iter.hasNext();) {
343
			InternalNode childEntity = (InternalNode) iter.next();
344
			int childEntityIndex = indexOfInternalNode(entities, childEntity);
345
			if (!childrenLists[i].contains(childEntity)) {
346
				childrenLists[i].add(childEntity);
347
			}
348
			if (!parentLists[childEntityIndex].contains(layoutEntity)) {
349
				parentLists[childEntityIndex].add(layoutEntity);
350
			}
351
		}
352
		
353
		for (Iterator iter = children.iterator(); iter.hasNext();) {
354
			InternalNode childEntity = (InternalNode) iter.next();
355
            int childEntityIndex = indexOfInternalNode(entities, childEntity);
356
			buildTreeRecursively(childEntity, childEntityIndex, weight + 1, entities, relationships);
357
		}
358
	}
359
    
360
    private int getNumChildren (InternalNode layoutEntity, InternalRelationship [] relationships) {
361
        return findRelationships(layoutEntity, AS_SOURCE, relationships).size();
362
    }
363
    
364
    private void getNumDescendentsAndLevel (InternalNode layoutEntity, InternalRelationship [] relationships, int [] numDescendentsAndLevel) {
365
        getNumDescendentsAndLevelRecursive(layoutEntity, relationships, new HashSet(), numDescendentsAndLevel, 0);
366
    }
367
    
368
    private void getNumDescendentsAndLevelRecursive (InternalNode layoutEntity, InternalRelationship [] relationships, Set seenAlready, int [] numDescendentsAndLevel, int currentLevel) {
369
        if (seenAlready.contains(layoutEntity)) {
370
            return;
371
        }
372
        seenAlready.add(layoutEntity);
373
        numDescendentsAndLevel[NUM_LEVELS_INDEX] = Math.max(numDescendentsAndLevel[NUM_LEVELS_INDEX], currentLevel);
374
        Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships);
375
        for (Iterator iter = rels.iterator(); iter.hasNext();) {
376
            InternalRelationship layoutRel = (InternalRelationship) iter.next();
377
            InternalNode childEntity = layoutRel.getDestination();
378
            numDescendentsAndLevel[NUM_DESCENDENTS_INDEX]++;
379
            getNumDescendentsAndLevelRecursive(childEntity, relationships, seenAlready, numDescendentsAndLevel, currentLevel + 1);
380
            
381
        }
382
    }
383
        
384
	
385
	/**
386
	 * Modifies the weight value of the marked node recursively.
387
	 */
388
	private void modifyWeightRecursively(InternalNode layoutEntity, int i, double weight, Set descendentsSeenSoFar, InternalNode [] entities, InternalRelationship [] relationships) {
389
        // No need to do further computation!
390
		if (layoutEntity == null) {
391
			return;
392
		}
393
394
		if (descendentsSeenSoFar.contains(layoutEntity)) {
395
			return; //No need to do further computation.
396
		}
397
		
398
		descendentsSeenSoFar.add(layoutEntity);
399
		// No need to do further computation!
400
		if (weight < weights[i]) {
401
			return;
402
		}
403
404
		weights[i] = weight;
405
		Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships);
406
		
407
		
408
		for (Iterator iter = rels.iterator(); iter.hasNext();) {
409
			InternalRelationship tmpRel = (InternalRelationship) iter.next();
410
			InternalNode tmpEntity = tmpRel.getDestination();
411
            int tmpEntityIndex = indexOfInternalNode(entities, tmpEntity);
412
			modifyWeightRecursively(tmpEntity, tmpEntityIndex, weight + 1, descendentsSeenSoFar, entities, relationships);
413
		}
414
	}
415
416
	/**
417
	 * Gets the maxium weight of a tree in the forest of this TreeLayoutAlgorithm.
418
	 */
419
	private double getMaxiumWeightRecursive(InternalNode layoutEntity, int i, Set seenAlready, InternalNode [] entities) {
420
		double result = 0;
421
        if (seenAlready.contains(layoutEntity)) {
422
            return result;
423
        }
424
        seenAlready.add(layoutEntity);
425
        List children = childrenLists[i];
426
		if (children.isEmpty()) {
427
			result = weights[i];
428
		} else {
429
			//TODO: SLOW
430
            for (Iterator iter = children.iterator(); iter.hasNext();) {
431
                InternalNode childEntity = (InternalNode) iter.next();
432
                int childEntityIndex = indexOfInternalNode(entities, childEntity);
433
                result = Math.max(result, getMaxiumWeightRecursive(childEntity, childEntityIndex, seenAlready, entities));
434
			}
435
		}
436
		return result;
437
	}
438
	
439
	/**
440
	 * Computes positions for each node in this TreeLayoutAlgorithm by
441
	 * referencing the forest that holds those nodes.
442
	 */
443
    private void computePositions(List roots, InternalNode [] entities) {
444
		// No need to do further computation!
445
		if (roots.size() == 0) {
446
			return;
447
		}
448
449
		int totalLeafCount = 0;
450
		double maxWeight = 0;
451
		for (int i = 0; i < roots.size(); i++) {
452
			InternalNode rootEntity = (InternalNode) roots.get(i);
453
            int rootEntityIndex = indexOfInternalNode(entities, rootEntity);
454
			totalLeafCount = totalLeafCount + getNumberOfLeaves(rootEntity, rootEntityIndex, entities);
455
			maxWeight = Math.max(maxWeight, getMaxiumWeightRecursive(rootEntity, rootEntityIndex, new HashSet(), entities) + 1.0);
456
		}
457
		
458
		double width = 1.0 / totalLeafCount;
459
		double height = 1.0 / maxWeight;
460
461
		int leafCountSoFar = 0;
462
		
463
		//TODO: SLOW!
464
		for (int i = 0; i < roots.size(); i++) {
465
			InternalNode rootEntity = (InternalNode) roots.get(i);
466
            int rootEntityIndex = indexOfInternalNode(entities, rootEntity);
467
			computePositionRecursively(rootEntity, rootEntityIndex, leafCountSoFar, width, height, new HashSet(), entities);
468
			leafCountSoFar = leafCountSoFar + getNumberOfLeaves(rootEntity, rootEntityIndex, entities);
469
		}
470
	}
471
	
472
	/**
473
	 * Computes positions recursively until the leaf nodes are reached.
474
	 */
475
	private void computePositionRecursively(InternalNode layoutEntity, int i, int relativePosition, double width, double height, Set seenAlready, InternalNode [] entities) {
476
        if (seenAlready.contains(layoutEntity)) {
477
			return;
478
		}
479
	    seenAlready.add(layoutEntity);
480
		double level = getLevel(layoutEntity, i, entities);
481
		int breadth = getNumberOfLeaves(layoutEntity, i, entities);
482
		double absHPosition = relativePosition + breadth / 2.0;
483
		double absVPosition = (level + 0.5);
484
485
		double posx = absHPosition * width;
486
		double posy = absVPosition * height;
487
        double weight = weights[i];
488
		posy = posy  + height * (weight - level);
489
		layoutEntity.setInternalLocation( posx, posy );
490
		
491
492
		int relativeCount = 0;
493
		List children = childrenLists[i];
494
		//TODO: Slow
495
        for (Iterator iter = children.iterator(); iter.hasNext();) {
496
            InternalNode childEntity = (InternalNode) iter.next();
497
            int childEntityIndex = indexOfInternalNode(entities, childEntity);
498
			computePositionRecursively(childEntity, childEntityIndex, relativePosition + relativeCount, width, height, seenAlready, entities);
499
			relativeCount = relativeCount + getNumberOfLeaves(childEntity, childEntityIndex, entities);
500
		}
501
	}
502
	
503
	private int getNumberOfLeaves (InternalNode layoutEntity, int i, InternalNode [] entities) {
504
	    return getNumberOfLeavesRecursive(layoutEntity, i, new HashSet(), entities);
505
	}
506
	
507
	private int getNumberOfLeavesRecursive(InternalNode layoutEntity, int i, Set seen, InternalNode [] entities) {
508
        int numLeaves = 0;
509
        List children = childrenLists[i];
510
        if (children.size() == 0) {
511
            numLeaves = 1;
512
        } else {
513
			//TODO: SLOW!
514
            for (Iterator iter = children.iterator(); iter.hasNext();) {
515
                InternalNode childEntity = (InternalNode) iter.next();
516
	            if (!seen.contains(childEntity)) {
517
		            seen.add (childEntity);
518
                    int childEntityIndex = indexOfInternalNode(entities, childEntity);
519
		            numLeaves += getNumberOfLeavesRecursive(childEntity, childEntityIndex, seen, entities);
520
	            } else {
521
	            	numLeaves = 1;
522
	            }
523
	        }
524
        }
525
        return numLeaves;
526
    }
527
	
528
	private int getLevel (InternalNode layoutEntity, int i, InternalNode [] entities) {
529
	    return getLevelRecursive(layoutEntity, i, new HashSet(), entities);
530
	}
531
	
532
	private int getLevelRecursive(InternalNode layoutEntity, int i, Set seen, InternalNode [] entities) {
533
        if (seen.contains(layoutEntity)) {
534
            return 0;
535
        }
536
        seen.add(layoutEntity);
537
		List parents = parentLists[i];
538
		int maxParentLevel = 0; 
539
		for (Iterator iter = parents.iterator(); iter.hasNext();) {
540
			InternalNode parentEntity = (InternalNode) iter.next();
541
            int parentEntityIndex = indexOfInternalNode(entities, parentEntity);
542
            int parentLevel = getLevelRecursive(parentEntity, parentEntityIndex, seen, entities) + 1;
543
            maxParentLevel = Math.max(maxParentLevel, parentLevel);
544
		}
545
        return maxParentLevel;
546
    }
547
    
548
    /**
549
     * Note: Use this as little as possible!
550
     * TODO limit the use of this method 
551
     * @param nodes
552
     * @param nodeToFind
553
     * @return
554
     */
555
    private int indexOfInternalNode (InternalNode [] nodes, InternalNode nodeToFind) {
556
        for (int i = 0; i < nodes.length; i++) {
557
            InternalNode node = nodes[i];
558
            if (node.equals(nodeToFind)) {
559
                return i;
560
            }
561
        }
562
        throw new RuntimeException("Couldn't find index of internal node: " + nodeToFind);
563
    }
564
565
566
	protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {
567
		if ( asynchronous && continueous ) {
568
			return false;
569
		} else if ( asynchronous && !continueous ) {
570
			return true;
571
		} else if ( !asynchronous && continueous ) {
572
			return false;
573
		} else if ( !asynchronous && !continueous ) {
574
			return true;
575
		}
576
		
577
		return false;
578
	}
579
580
} 
(-)src/org/eclipse/zest/layouts/algorithms/TreeLayoutObserver.java (+552 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: The Chisel Group - initial API and implementation
8
 *               Mateusz Matela 
9
 *               Ian Bull
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import java.util.ArrayList;
14
import java.util.Collections;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.Iterator;
18
import java.util.LinkedList;
19
import java.util.List;
20
import java.util.ListIterator;
21
import java.util.Set;
22
23
import org.eclipse.zest.layouts.interfaces.ConnectionLayout;
24
import org.eclipse.zest.layouts.interfaces.GraphStructureListener;
25
import org.eclipse.zest.layouts.interfaces.LayoutContext;
26
import org.eclipse.zest.layouts.interfaces.NodeLayout;
27
28
/**
29
 * A helper class for layout algorithms that are based on tree structure. It
30
 * keeps track of changes in observed layout context and stores current
31
 * information about the tree structure - children of each node and several
32
 * other parameters.
33
 */
34
public class TreeLayoutObserver {
35
36
	/**
37
	 * <code>TreeLayoutObserver</code> uses instance of this class to create
38
	 * instances of {@link TreeNode}. It may be extended and passed to
39
	 * <code>TreeLayoutObserver</code>'s constructor in order to build a tree
40
	 * structure made of <code>TreeNode</code>'s subclasses.
41
	 */
42
	public static class TreeNodeFactory {
43
		public TreeNode createTreeNode(NodeLayout nodeLayout, TreeLayoutObserver observer) {
44
			return new TreeNode(nodeLayout, observer);
45
		}
46
	}
47
48
	/**
49
	 * Represents a node in a tree structure and stores all information related
50
	 * to it. May be subclassed if additional data and behavior is necessary.
51
	 */
52
	public static class TreeNode {
53
		final protected NodeLayout node;
54
		final protected TreeLayoutObserver owner;
55
		protected int height = 0;
56
		protected int depth = -1;
57
		protected int numOfLeaves = 0;
58
		protected int numOfDescendants = 0;
59
		protected int order = 0;
60
		protected final List children = new ArrayList();
61
		protected TreeNode parent;
62
		protected boolean firstChild = false, lastChild = false;
63
64
		/**
65
		 * 
66
		 * @return node layout related to this tree node (null for
67
		 *         {@link TreeLayoutObserver#getSuperRoot() Super Root})
68
		 */
69
		public NodeLayout getNode() {
70
			return node;
71
		}
72
73
		/**
74
		 * 
75
		 * @return <code>TreeLayoutObserver</code> owning this tree node
76
		 */
77
		public TreeLayoutObserver getOwner() {
78
			return owner;
79
		}
80
81
		/**
82
		 * 
83
		 * @return height of this node in the tree (the longest distance to a
84
		 *         leaf, 0 for a leaf itself)
85
		 */
86
		public int getHeight() {
87
			return height;
88
		}
89
90
		/**
91
		 * 
92
		 * @return depth of this node in the tree (distance from root, 0 for a
93
		 *         root and -1 for {@link TreeLayoutObserver#getSuperRoot()
94
		 *         Super Root}
95
		 */
96
		public int getDepth() {
97
			return depth;
98
		}
99
100
		/**
101
		 * 
102
		 * @return number of all leaves descending from this node (1 for a leaf
103
		 *         itself)
104
		 */
105
		public int getNumOfLeaves() {
106
			return numOfLeaves;
107
		}
108
109
		/**
110
		 * 
111
		 * @return total number of descendants of this node (0 for leafs)
112
		 */
113
		public int getNumOfDescendants() {
114
			return numOfDescendants;
115
		}
116
117
		/**
118
		 * Returns order in which nodes are visited during Deep First Search.
119
		 * Children are visited in the same order as they were added to their
120
		 * layout context, unless {@link TreeLayoutObserver#recomputeTree()} was
121
		 * called after the nodes were added. In that case the order is
122
		 * determined by order of nodes returned by
123
		 * {@link NodeLayout#getSuccessingNodes()}. Leaves are assigned
124
		 * successive numbers starting from 0, other nodes have order equal to
125
		 * the smallest order of their children.
126
		 * 
127
		 * @return order of this node
128
		 */
129
		public int getOrder() {
130
			return order;
131
		}
132
133
		/**
134
		 * 
135
		 * @return an unmodifiable list of this node's children
136
		 */
137
		public List getChildren() {
138
			return Collections.unmodifiableList(children);
139
		}
140
141
		/**
142
		 * 
143
		 * @return this node's parent
144
		 */
145
		public TreeNode getParent() {
146
			return parent;
147
		}
148
149
		/**
150
		 * 
151
		 * @return true if this node is the first child of its parent (has the
152
		 *         smallest order)
153
		 */
154
		public boolean isFirstChild() {
155
			return firstChild;
156
		}
157
158
		/**
159
		 * 
160
		 * @return
161
		 */
162
		public boolean isLastChild() {
163
			return lastChild;
164
		}
165
166
		/**
167
		 * Creates a tree node related to given layout node
168
		 * 
169
		 * @param node
170
		 *            the layout node
171
		 * @param owner
172
		 *            <code>TreeLayoutObserver</code> owning created node
173
		 */
174
		protected TreeNode(NodeLayout node, TreeLayoutObserver owner) {
175
			this.node = node;
176
			this.owner = owner;
177
		}
178
179
		/**
180
		 * Adds given node to the list of this node's children and set its
181
		 * parent to this node.
182
		 * 
183
		 * @param child
184
		 *            node to add
185
		 */
186
		protected void addChild(TreeNode child) {
187
			children.add(child);
188
			child.parent = this;
189
		}
190
191
		/**
192
		 * Performs a DFS on the tree structure and calculates all parameters of
193
		 * its nodes. Should be called on
194
		 * {@link TreeLayoutObserver#getSuperRoot() Super Root}. Uses recurrence
195
		 * to go through all the nodes.
196
		 */
197
		protected void precomputeTree() {
198
			if (children.isEmpty()) {
199
				height = 0;
200
				numOfLeaves = 1;
201
				numOfDescendants = 0;
202
			} else {
203
				height = 0;
204
				numOfLeaves = 0;
205
				numOfDescendants = 0;
206
				for (ListIterator iterator = children.listIterator(); iterator.hasNext();) {
207
					TreeNode child = (TreeNode) iterator.next();
208
					child.depth = this.depth + 1;
209
					child.order = this.order + this.numOfLeaves;
210
					child.precomputeTree();
211
					child.firstChild = (this.numOfLeaves == 0);
212
					child.lastChild = !iterator.hasNext();
213
214
					this.height = Math.max(this.height, child.height + 1);
215
					this.numOfLeaves += child.numOfLeaves;
216
					this.numOfDescendants += child.numOfDescendants + 1;
217
				}
218
			}
219
		}
220
221
		/**
222
		 * Finds a node that is the best parent for this node. Add this node as
223
		 * a child of the found node.
224
		 */
225
		protected void findNewParent() {
226
			if (parent != null)
227
				parent.children.remove(this);
228
			NodeLayout[] predecessingNodes = node.getPredecessingNodes();
229
			parent = null;
230
			for (int i = 0; i < predecessingNodes.length; i++) {
231
				TreeNode potentialParent = (TreeNode) owner.layoutToTree.get(predecessingNodes[i]);
232
				if (!children.contains(potentialParent) && isBetterParent(potentialParent))
233
					parent = potentialParent;
234
			}
235
			if (parent == null)
236
				parent = owner.superRoot;
237
238
			parent.addChild(this);
239
		}
240
241
		/**
242
		 * Checks if a potential parent would be better for this node than its
243
		 * current parent. A better parent has smaller depth (with exception to
244
		 * {@link TreeLayoutObserver#getSuperRoot() Super Root}, which has depth
245
		 * equal to -1 but is never a better parent than any other node).
246
		 * 
247
		 * @param potentialParent
248
		 *            potential parent to check
249
		 * @return true if potentialParent can be a parent of this node and is
250
		 *         better than its current parent
251
		 */
252
		protected boolean isBetterParent(TreeNode potentialParent) {
253
			if (this.parent == null && !this.isAncestorOf(potentialParent))
254
				return true;
255
			if (potentialParent.depth <= this.depth && potentialParent.depth != -1)
256
				return true;
257
			if (this.parent.depth == -1 && potentialParent.depth >= 0 && !this.isAncestorOf(potentialParent))
258
				return true;
259
			return false;
260
		}
261
262
		/**
263
		 * 
264
		 * @param descendant
265
		 * @return true if this node is an ancestor if given descendant node
266
		 */
267
		public boolean isAncestorOf(TreeNode descendant) {
268
			while (descendant.depth > this.depth)
269
				descendant = descendant.parent;
270
			return descendant == this;
271
		}
272
	}
273
274
	/**
275
	 * A superclass for listeners that can be added to this observer to get
276
	 * notification whenever the tree structure changes.
277
	 */
278
	public static class TreeListener {
279
		/**
280
		 * Called when new node is added to the tree structure. The new node
281
		 * will not have any connections, so it will be a child of
282
		 * {@link TreeLayoutObserver#getSuperRoot() Super Root}
283
		 * 
284
		 * @param newNode
285
		 *            the added node
286
		 */
287
		public void nodeAdded(TreeNode newNode) {
288
			defaultHandle(newNode);
289
		}
290
291
		/**
292
		 * Called when a node is removed from the tree structure. The given node
293
		 * no longer exists in the tree at the moment of call.
294
		 * 
295
		 * @param removedNode
296
		 *            the removed node
297
		 */
298
		public void nodeRemoved(TreeNode removedNode) {
299
			defaultHandle(removedNode);
300
		}
301
302
		/**
303
		 * Called when a node changes its parent.
304
		 * 
305
		 * @param node
306
		 *            node that changes its parent
307
		 * @param previousParent
308
		 *            previous parent of the node
309
		 */
310
		public void parentChanged(TreeNode node, TreeNode previousParent) {
311
			defaultHandle(node);
312
		}
313
314
		/**
315
		 * A convenience method that can be overridden if a listener reacts the
316
		 * same way to all events. By default it's called in every event handler
317
		 * and does nothing.
318
		 * 
319
		 * @param changedNode
320
		 *            the node that has changed
321
		 */
322
		protected void defaultHandle(TreeNode changedNode) {
323
		}
324
	}
325
326
	private GraphStructureListener structureListener = new GraphStructureListener() {
327
328
		public boolean nodeRemoved(LayoutContext context, NodeLayout node) {
329
			TreeNode treeNode = (TreeNode) layoutToTree.get(node);
330
			treeNode.parent.children.remove(treeNode);
331
			superRoot.precomputeTree();
332
			for (Iterator iterator = treeListeners.iterator(); iterator.hasNext();) {
333
				TreeListener listener = (TreeListener) iterator.next();
334
				listener.nodeRemoved(treeNode);
335
			}
336
			return false;
337
		}
338
339
		public boolean nodeAdded(LayoutContext context, NodeLayout node) {
340
			TreeNode treeNode = getTreeNode(node);
341
			superRoot.addChild(treeNode);
342
			superRoot.precomputeTree();
343
			for (Iterator iterator = treeListeners.iterator(); iterator.hasNext();) {
344
				TreeListener listener = (TreeListener) iterator.next();
345
				listener.nodeAdded(treeNode);
346
			}
347
			return false;
348
		}
349
350
		public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection) {
351
			TreeNode node1 = (TreeNode) layoutToTree.get(connection.getSource());
352
			TreeNode node2 = (TreeNode) layoutToTree.get(connection.getTarget());
353
			if (node1.parent == node2) {
354
				node1.findNewParent();
355
				if (node1.parent != node2) {
356
					superRoot.precomputeTree();
357
					fireParentChanged(node1, node2);
358
				}
359
			}
360
			if (node2.parent == node1) {
361
				node2.findNewParent();
362
				if (node2.parent != node1) {
363
					superRoot.precomputeTree();
364
					fireParentChanged(node2, node1);
365
				}
366
			}
367
			return false;
368
		}
369
370
		public boolean connectionAdded(LayoutContext context, ConnectionLayout connection) {
371
			TreeNode source = (TreeNode) layoutToTree.get(connection.getSource());
372
			TreeNode target = (TreeNode) layoutToTree.get(connection.getTarget());
373
			if (source == target)
374
				return false;
375
			if (target.isBetterParent(source)) {
376
				TreeNode previousParent = target.parent;
377
				previousParent.children.remove(target);
378
				source.addChild(target);
379
				superRoot.precomputeTree();
380
				fireParentChanged(target, previousParent);
381
			}
382
			if (!connection.isDirected() && source.isBetterParent(target)) {
383
				TreeNode previousParent = source.parent;
384
				previousParent.children.remove(source);
385
				target.addChild(source);
386
				superRoot.precomputeTree();
387
				fireParentChanged(source, previousParent);
388
			}
389
			return false;
390
		}
391
392
		private void fireParentChanged(TreeNode node, TreeNode previousParent) {
393
			for (Iterator iterator = treeListeners.iterator(); iterator.hasNext();) {
394
				TreeListener listener = (TreeListener) iterator.next();
395
				listener.parentChanged(node, previousParent);
396
			}
397
		}
398
	};
399
400
	private final HashMap layoutToTree = new HashMap();
401
	private final TreeNodeFactory factory;
402
	private final LayoutContext context;
403
	private TreeNode superRoot;
404
	private ArrayList treeListeners = new ArrayList();
405
406
	/**
407
	 * Creates a
408
	 * 
409
	 * @param context
410
	 * @param nodeFactory
411
	 */
412
	public TreeLayoutObserver(LayoutContext context, TreeNodeFactory nodeFactory) {
413
		if (nodeFactory == null)
414
			this.factory = new TreeNodeFactory();
415
		else
416
			this.factory = nodeFactory;
417
		this.context = context;
418
		context.addGraphStructureListener(structureListener);
419
		recomputeTree();
420
	}
421
422
	/**
423
	 * Recomputes all the information about the tree structure (the same effect
424
	 * as creating new <code>TreeLayoutObserver</code>).
425
	 */
426
	public void recomputeTree() {
427
		superRoot = factory.createTreeNode(null, this);
428
		layoutToTree.put(null, superRoot);
429
		createTrees(context.getNodes());
430
	}
431
432
	/**
433
	 * Stops this observer from listening to changes in observed layout context.
434
	 * After calling this method the information about tree structure can be
435
	 * updated only when {@link #recomputeTree()} is called.
436
	 */
437
	public void stop() {
438
		context.removeGraphStructureListener(structureListener);
439
	}
440
441
	/**
442
	 * Returns Super Root, that is an artificial node being a common parent for
443
	 * all nodes in observed tree structure.
444
	 * 
445
	 * @return Super Root
446
	 */
447
	public TreeNode getSuperRoot() {
448
		return superRoot;
449
	}
450
451
	/**
452
	 * Returns a {@link TreeNode} related to given node layout. If such a
453
	 * <code>TreeNode</code> doesn't exist, it's created.
454
	 * 
455
	 * @param node
456
	 * @return
457
	 */
458
	public TreeNode getTreeNode(NodeLayout node) {
459
		TreeNode treeNode = (TreeNode) layoutToTree.get(node);
460
		if (treeNode == null) {
461
			treeNode = factory.createTreeNode(node, this);
462
			layoutToTree.put(node, treeNode);
463
		}
464
		return treeNode;
465
	}
466
467
	/**
468
	 * Adds a listener that will be informed about changes in tree structure.
469
	 * 
470
	 * @param listener
471
	 *            listener to add
472
	 */
473
	public void addTreeListener(TreeListener listener) {
474
		treeListeners.add(listener);
475
	}
476
477
	/**
478
	 * Removes a listener from list of listener to be informed about changes in
479
	 * tree structure.
480
	 * 
481
	 * @param listener
482
	 *            listener to remove
483
	 */
484
	public void removeTreeListener(TreeListener listener) {
485
		treeListeners.add(listener);
486
	}
487
488
	/**
489
	 * Builds a tree structure using BFS method. Created trees are children of
490
	 * {@link #superRoot}.
491
	 * 
492
	 * @param nodes
493
	 */
494
	private void createTrees(NodeLayout[] nodes) {
495
		HashSet alreadyVisited = new HashSet();
496
		LinkedList nodesToAdd = new LinkedList();
497
		for (int i = 0; i < nodes.length; i++) {
498
			NodeLayout root = findRoot(nodes[i], alreadyVisited);
499
			if (root != null) {
500
				alreadyVisited.add(root);
501
				nodesToAdd.addLast(new Object[] { root, superRoot });
502
			}
503
		}
504
		while (!nodesToAdd.isEmpty()) {
505
			Object[] dequeued = (Object[]) nodesToAdd.removeFirst();
506
			TreeNode currentNode = factory.createTreeNode((NodeLayout) dequeued[0], this);
507
			layoutToTree.put(dequeued[0], currentNode);
508
			TreeNode currentRoot = (TreeNode) dequeued[1];
509
510
			currentRoot.addChild(currentNode);
511
			NodeLayout[] children = currentNode.node.getSuccessingNodes();
512
			for (int i = 0; i < children.length; i++) {
513
				if (!alreadyVisited.contains(children[i])) {
514
					alreadyVisited.add(children[i]);
515
					nodesToAdd.addLast(new Object[] { children[i], currentNode });
516
				}
517
			}
518
		}
519
		superRoot.precomputeTree();
520
	}
521
522
	/**
523
	 * Searches for a root of a tree containing given node by continuously
524
	 * grabbing a predecessor of current node. If it reaches an node that exists
525
	 * in alreadyVisited set, it returns null. If it detects a cycle, it returns
526
	 * the first found node of that cycle. If it reaches a node that has no
527
	 * predecessors, it returns that node.
528
	 * 
529
	 * @param nodeLayout
530
	 *            starting node
531
	 * @param alreadyVisited
532
	 *            set of nodes that can't lay on path to the root (if one does,
533
	 *            method stops and returns null).
534
	 * @return
535
	 */
536
	private NodeLayout findRoot(NodeLayout nodeLayout, Set alreadyVisited) {
537
		HashSet alreadyVisitedRoot = new HashSet();
538
		while (true) {
539
			if (alreadyVisited.contains(nodeLayout))
540
				return null;
541
			if (alreadyVisitedRoot.contains(nodeLayout))
542
				return nodeLayout;
543
			alreadyVisitedRoot.add(nodeLayout);
544
			NodeLayout[] predecessingNodes = nodeLayout.getPredecessingNodes();
545
			if (predecessingNodes.length > 0) {
546
				nodeLayout = predecessingNodes[0];
547
			} else {
548
				return nodeLayout;
549
			}
550
		}
551
	}
552
}
(-)src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java (-52 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms;
12
13
import org.eclipse.zest.layouts.LayoutStyles;
14
15
/**
16
 * @version  2.0
17
 * @author   Casey Best and Rob Lintern (version 1.0 by Rob Lintern)
18
 */
19
public class VerticalLayoutAlgorithm extends GridLayoutAlgorithm {
20
21
	
22
	/**
23
	 * Veertical Layout Algorithm constructor with no styles.
24
	 *
25
	 */
26
	public VerticalLayoutAlgorithm() {
27
		this(LayoutStyles.NONE );
28
	}
29
	
30
	public VerticalLayoutAlgorithm(int styles) {
31
		super(styles);
32
	}
33
34
	/**
35
	 * Calculates and returns an array containing the number of columns, followed by the number of rows
36
	 */
37
	protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
38
		int cols = 1;
39
		int rows = numChildren;
40
		int[] result = {cols, rows};
41
		return result;
42
	}
43
	
44
	protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {
45
		if ( asynchronous && continueous ) return false;
46
		else if ( asynchronous && !continueous ) return true;
47
		else if ( !asynchronous && continueous ) return false;
48
		else if ( !asynchronous && !continueous ) return true;
49
		
50
		return false;
51
	}
52
}
(-)src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java (-119 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
12
package org.eclipse.zest.layouts.algorithms.internal;
13
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.Hashtable;
17
import java.util.Iterator;
18
import java.util.List;
19
20
import org.eclipse.zest.layouts.LayoutEntity;
21
import org.eclipse.zest.layouts.LayoutRelationship;
22
import org.eclipse.zest.layouts.algorithms.AbstractLayoutAlgorithm;
23
import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship;
24
25
26
/**
27
 * Checks for cycles in the given graph.
28
 * 
29
 * @author Casey Best
30
 */
31
public class CycleChecker {
32
	/**
33
	 * Tests if there is a directed cirlce in the graph formed by the given entities and relationships.
34
	 * @param entities The entities in the graph to check
35
	 * @param relationships The relationships in the graph to check
36
	 * @param cycle Populated with the cycle encountered, if there is one.
37
	 * @throws RuntimeException Thrown if entities doesn't contain all of the endpoints for each relationship in relationships
38
	 * @return <code>true</code> if there is a directed circle.
39
	 * Otherwise, <code>false</code>.
40
	 */
41
	public static boolean hasDirectedCircles(LayoutEntity[] entities, LayoutRelationship[] relationships, List cycle) {
42
		if (!AbstractLayoutAlgorithm.verifyInput(entities, relationships)) {
43
			throw new RuntimeException ("The endpoints of the relationships aren't contained in the entities list.");
44
		}
45
		//Enumeration enum;
46
		//Iterator iterator;
47
48
		Hashtable endPoints = new Hashtable();
49
50
		// Initialize the relation(transitive) vector.
51
        for (int i = 0; i < relationships.length; i++) {
52
            LayoutRelationship rel = relationships[i];
53
54
			//Add the relationship to the source endpoint
55
			Object subject = rel.getSourceInLayout();
56
			List rels = (List) endPoints.get(subject);
57
			if (rels == null) {
58
				rels = new ArrayList();
59
				endPoints.put(subject, rels);
60
			}
61
			if (!rels.contains(rel))
62
				rels.add(rel);
63
		}
64
		boolean hasCyle = hasCycle(new ArrayList(Arrays.asList(entities)), endPoints, cycle);
65
		return hasCyle;
66
	}
67
68
	/**
69
	 * Check passed in nodes for a cycle
70
	 */
71
	private static boolean hasCycle(List nodesToCheck, Hashtable endPoints, List cycle) {
72
		while (nodesToCheck.size() > 0) {
73
			Object checkNode = nodesToCheck.get(0);
74
			List checkedNodes = new ArrayList();
75
			if (hasCycle(checkNode, new ArrayList(), null, endPoints, checkedNodes, cycle)) {
76
				return true;
77
			}
78
			nodesToCheck.removeAll(checkedNodes);
79
		}
80
		return false;
81
	}
82
83
	/**
84
	 * Checks all the nodes attached to the nodeToCheck node for a cycle.  All nodes
85
	 * checked are placed in nodePathSoFar.
86
	 *
87
	 * @returns true if there is a cycle
88
	 */
89
	private static boolean hasCycle(Object nodeToCheck, List nodePathSoFar, SimpleRelationship cameFrom, Hashtable endPoints, List nodesChecked, List cycle) {
90
		if (nodePathSoFar.contains(nodeToCheck)) {
91
			cycle.addAll(nodePathSoFar);
92
			cycle.add(nodeToCheck);
93
			return true;
94
		}
95
		nodePathSoFar.add(nodeToCheck);
96
		nodesChecked.add(nodeToCheck);
97
98
		List relations = (List) endPoints.get(nodeToCheck);
99
		if (relations != null) {
100
			for (Iterator iter = relations.iterator(); iter.hasNext();) {
101
				SimpleRelationship rel = (SimpleRelationship) iter.next();
102
103
				if (cameFrom == null || !rel.equals(cameFrom)) {
104
					Object currentNode = null;
105
					currentNode = rel.getDestinationInLayout();
106
					if (hasCycle(currentNode, nodePathSoFar, rel, endPoints, nodesChecked, cycle)) {
107
						return true;
108
					}
109
				}
110
111
			}
112
		}
113
114
		nodePathSoFar.remove(nodeToCheck);
115
116
		return false;
117
	}
118
119
}
(-)src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java (-126 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.algorithms.internal;
12
13
import java.util.Comparator;
14
import java.util.TreeSet;
15
16
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
17
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
18
import org.eclipse.zest.layouts.dataStructures.InternalNode;
19
20
21
public class DynamicScreen {
22
	
23
	private TreeSet XCoords = null;
24
	private TreeSet YCoords = null;
25
	
26
	
27
	
28
	private DisplayIndependentRectangle screenBounds = null;
29
	
30
	
31
	double minX = 0.0;
32
	double minY = 0.0;
33
	double maxX = 0.0;
34
	double maxY = 0.0;
35
	public void cleanScreen() {
36
		minX = 0.0;
37
		minY = 0.0;
38
		maxX = 0.0;
39
		maxY = 0.0;
40
	}
41
	
42
	class XComparator implements Comparator {
43
		public int compare(Object arg0, Object arg1) {
44
			InternalNode n1 = (InternalNode)arg0;
45
			InternalNode n2 = (InternalNode)arg1;
46
			if ( n1.getInternalX() > n2.getInternalX() ) return +1;
47
			else if ( n1.getInternalX() < n2.getInternalX() ) return -1;
48
			else {
49
				return n1.toString().compareTo( n2.toString() );
50
			}
51
			
52
		}		
53
	}
54
	class YComparator implements Comparator {
55
		public int compare(Object arg0, Object arg1) {
56
			InternalNode n1 = (InternalNode)arg0;
57
			InternalNode n2 = (InternalNode)arg1;
58
			if ( n1.getInternalY() > n2.getInternalY() ) return +1;
59
			else if ( n1.getInternalY() < n2.getInternalY() ) return -1;
60
			else {
61
				return n1.toString().compareTo( n2.toString() );
62
			}
63
64
		}		
65
	}
66
	
67
	
68
69
	
70
	public DynamicScreen(int x, int y, int width, int height) {
71
		XCoords = new TreeSet(new XComparator());
72
		YCoords = new TreeSet(new YComparator());
73
		
74
		
75
		
76
		this.screenBounds = new DisplayIndependentRectangle(x,y,width,height);
77
	}
78
	
79
	
80
	public void removeNode( InternalNode node ) {
81
		XCoords.remove( node );
82
		YCoords.remove( node );
83
	}
84
	
85
	public void addNode( InternalNode node ) {		
86
		XCoords.add( node );
87
		YCoords.add( node );
88
	}
89
	
90
	public DisplayIndependentPoint getScreenLocation( InternalNode node ) {
91
		
92
		DisplayIndependentRectangle layoutBounds = calculateBounds();
93
		
94
		double x = (layoutBounds.width == 0) ? 0 : (node.getInternalX() - layoutBounds.x) / layoutBounds.width;
95
		double y = (layoutBounds.height == 0) ? 0 : (node.getInternalY() - layoutBounds.y) / layoutBounds.height;		
96
		
97
		x = screenBounds.x + x * screenBounds.width;
98
		y = screenBounds.y + y * screenBounds.height;
99
		
100
		return new DisplayIndependentPoint( x, y );
101
	}
102
	 
103
	public DisplayIndependentPoint getVirtualLocation( DisplayIndependentPoint point ) {
104
		
105
		DisplayIndependentRectangle layoutBounds = calculateBounds();
106
		
107
		
108
		double x = (point.x/screenBounds.width) * layoutBounds.width + layoutBounds.x;
109
		double y = (point.y/screenBounds.height)  * layoutBounds.height + layoutBounds.y;
110
		
111
		return new DisplayIndependentPoint( x, y );
112
	}
113
	
114
	private DisplayIndependentRectangle calculateBounds() {		
115
		InternalNode n1 = (InternalNode) XCoords.first();
116
		InternalNode n2 = (InternalNode) XCoords.last();
117
		InternalNode n3 = (InternalNode) YCoords.first();
118
		InternalNode n4 = (InternalNode) YCoords.last();
119
		double x = n1.getInternalX();
120
		double width = n2.getInternalX();
121
		double y = n3.getInternalY();
122
		double height = n4.getInternalY();
123
		return new DisplayIndependentRectangle(x, y, width - x, height - y);
124
	}
125
126
}
(-)src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java (-32 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.constraints;
12
13
/**
14
 * @author Ian Bull
15
 * @author Chris Bennett
16
 */
17
public class BasicEdgeConstraints implements LayoutConstraint {
18
19
	// These should all be accessed directly.  
20
	public boolean isBiDirectional = false;
21
	public int weight = 1;
22
	
23
	/*
24
	 * (non-Javadoc)
25
	 * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear()
26
	 */
27
	public void clear() {
28
		this.isBiDirectional = false;
29
		this.weight = 1;
30
	}
31
	
32
}
(-)src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java (-46 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.constraints;
12
13
/**
14
 * 
15
 * @author Chris Bennett
16
 *
17
 */
18
public class BasicEntityConstraint implements LayoutConstraint {
19
	
20
	
21
	public boolean hasPreferredLocation = false;
22
	
23
	public double preferredX;
24
	public double preferredY;
25
	
26
	public boolean hasPreferredSize = false;
27
	public double preferredWidth;
28
	public double preferredHeight;
29
	
30
	public BasicEntityConstraint() {
31
		clear();
32
	} 
33
	
34
	/*
35
	 * (non-Javadoc)
36
	 * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear()
37
	 */
38
	public void clear() {
39
		this.hasPreferredLocation = false;
40
		this.hasPreferredSize = false;
41
		this.preferredX = 0.0;
42
		this.preferredY = 0.0;
43
		this.preferredWidth = 0.0;
44
		this.preferredHeight = 0.0;
45
	}
46
}
(-)src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java (-31 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.constraints;
12
13
/**
14
 * A layout constraint that uses priorities
15
 * @author Ian Bull
16
 */
17
public class EntityPriorityConstraint implements LayoutConstraint {
18
19
	// A priority that can be set for nodes.  This could be used
20
	// for a treemap layout
21
	public double priority = 1.0;
22
	
23
	/*
24
	 * (non-Javadoc)
25
	 * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear()
26
	 */
27
	public void clear() {
28
		this.priority = 1.0;
29
	}
30
31
}
(-)src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java (-33 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.constraints;
12
13
/**
14
 * @author Ian Bull
15
 * @author Chris Bennett
16
 */
17
public class LabelLayoutConstraint implements LayoutConstraint {
18
	
19
	// These should be accessed directly
20
	public String label;
21
	public int pointSize;
22
	
23
	/*
24
	 * (non-Javadoc)
25
	 * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear()
26
	 */
27
	public void clear() {
28
		label = null;
29
		pointSize = 1;
30
	}
31
	
32
}
33
(-)src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java (-26 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.constraints;
12
13
/**
14
 * @author Ian Bull
15
 * @author Chris Bennett
16
 */
17
public interface LayoutConstraint {
18
19
	// Empty interface
20
	
21
	/**
22
	 * This method clears all the fields of the layout constraints.
23
	 * This should not be called outside the layout package
24
	 */
25
	public void clear();
26
}
(-)src/org/eclipse/zest/layouts/dataStructures/BendPoint.java (-54 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.dataStructures;
12
13
import org.eclipse.zest.layouts.LayoutBendPoint;
14
15
/**
16
 * Implements a single bend point in a graph relationship.
17
 * 
18
 * @author Ian Bull
19
 * @author Chris Bennett
20
 */
21
public class BendPoint extends DisplayIndependentPoint implements LayoutBendPoint {
22
	
23
	private boolean isControlPoint = false; // is this a control point (for use in curves)
24
25
	public BendPoint(double x, double y) {
26
		super(x, y);
27
	}
28
29
	public BendPoint(double x, double y, boolean isControlPoint) {
30
		this(x, y);
31
		this.isControlPoint = isControlPoint;
32
	}
33
34
	public double getX() {
35
		return x;
36
	}
37
38
	public double getY() {
39
		return y;
40
	}
41
42
	public void setX(double x) {
43
		this.x = x;
44
	}
45
46
	public void setY(double y) {
47
		this.y = y;
48
	}
49
50
	public boolean getIsControlPoint() {
51
		return isControlPoint;
52
	}
53
54
}
(-)src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentDimension.java (-35 / +35 lines)
Lines 1-35 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.dataStructures;
11
package org.eclipse.zest.layouts.dataStructures;
12
12
13
/**
13
/**
14
 * This is a dimension that isn't dependent on awt, swt, or any other library,
14
 * This is a dimension that isn't dependent on awt, swt, or any other library,
15
 * except layout.
15
 * except layout.
16
 * 
16
 * 
17
 * @author Casey Best
17
 * @author Casey Best
18
 */
18
 */
19
public class DisplayIndependentDimension {
19
public class DisplayIndependentDimension {
20
	public double width, height;
20
	public double width, height;
21
	
21
	
22
	public DisplayIndependentDimension (double width, double height) {
22
	public DisplayIndependentDimension (double width, double height) {
23
		this.width = width;
23
		this.width = width;
24
		this.height = height;
24
		this.height = height;
25
	}
25
	}
26
26
27
	public DisplayIndependentDimension (DisplayIndependentDimension dimension) {
27
	public DisplayIndependentDimension (DisplayIndependentDimension dimension) {
28
		this.width = dimension.width;
28
		this.width = dimension.width;
29
		this.height = dimension.height;
29
		this.height = dimension.height;
30
	}
30
	}
31
31
32
	public String toString() {
32
	public String toString() {
33
		return "(" + width + ", " + height + ")";
33
		return "(" + width + ", " + height + ")";
34
	}
34
	}
35
}
35
}
(-)src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentPoint.java (-84 / +84 lines)
Lines 1-84 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.dataStructures;
11
package org.eclipse.zest.layouts.dataStructures;
12
12
13
/**
13
/**
14
 * This is a point that isn't dependent on awt, swt, or any other library,
14
 * This is a point that isn't dependent on awt, swt, or any other library,
15
 * except layout.
15
 * except layout.
16
 * 
16
 * 
17
 * @author Casey Best
17
 * @author Casey Best
18
 */
18
 */
19
public class DisplayIndependentPoint {
19
public class DisplayIndependentPoint {
20
	public double x, y;
20
	public double x, y;
21
	
21
	
22
	
22
	
23
	public boolean equals( Object o ) {
23
	public boolean equals( Object o ) {
24
		DisplayIndependentPoint that = (DisplayIndependentPoint) o;
24
		DisplayIndependentPoint that = (DisplayIndependentPoint) o;
25
		if ( this.x == that.x && this.y == that.y ) return true;
25
		if ( this.x == that.x && this.y == that.y ) return true;
26
		else return false;
26
		else return false;
27
	}
27
	}
28
	
28
	
29
	public DisplayIndependentPoint (double x, double y) {
29
	public DisplayIndependentPoint (double x, double y) {
30
		this.x = x;
30
		this.x = x;
31
		this.y = y;
31
		this.y = y;
32
	}
32
	}
33
33
34
	public DisplayIndependentPoint (DisplayIndependentPoint point) {
34
	public DisplayIndependentPoint (DisplayIndependentPoint point) {
35
		this.x = point.x;
35
		this.x = point.x;
36
		this.y = point.y;
36
		this.y = point.y;
37
	}
37
	}
38
38
39
	public String toString() {
39
	public String toString() {
40
		return "(" + x + ", " + y + ")";
40
		return "(" + x + ", " + y + ")";
41
	}
41
	}
42
	
42
	
43
	/**
43
	/**
44
	 * Create a new point based on the current point but in a new coordinate system
44
	 * Create a new point based on the current point but in a new coordinate system
45
	 * @param currentBounds
45
	 * @param currentBounds
46
	 * @param targetBounds
46
	 * @param targetBounds
47
	 * @return
47
	 * @return
48
	 */
48
	 */
49
	public DisplayIndependentPoint convert(DisplayIndependentRectangle currentBounds ,
49
	public DisplayIndependentPoint convert(DisplayIndependentRectangle currentBounds ,
50
			DisplayIndependentRectangle targetBounds) {
50
			DisplayIndependentRectangle targetBounds) {
51
		double currentWidth = currentBounds.width;
51
		double currentWidth = currentBounds.width;
52
		double currentHeight = currentBounds.height;
52
		double currentHeight = currentBounds.height;
53
		
53
		
54
        double newX = (currentBounds.width == 0) ? 0 : 
54
        double newX = (currentBounds.width == 0) ? 0 : 
55
        	(x / currentWidth) * targetBounds.width + targetBounds.x;
55
        	(x / currentWidth) * targetBounds.width + targetBounds.x;
56
        double newY = (currentBounds.height == 0) ? 0 : 
56
        double newY = (currentBounds.height == 0) ? 0 : 
57
        	(y / currentHeight) * targetBounds.height + targetBounds.y;
57
        	(y / currentHeight) * targetBounds.height + targetBounds.y;
58
        return new DisplayIndependentPoint( newX, newY );
58
        return new DisplayIndependentPoint( newX, newY );
59
	}
59
	}
60
	
60
	
61
	/**
61
	/**
62
	 * Converts this point based on the current x, y values to a percentage  
62
	 * Converts this point based on the current x, y values to a percentage  
63
	 * of the specified coordinate system
63
	 * of the specified coordinate system
64
	 * @param bounds
64
	 * @param bounds
65
	 * @return
65
	 * @return
66
	 */
66
	 */
67
	public DisplayIndependentPoint convertToPercent(DisplayIndependentRectangle bounds) {
67
	public DisplayIndependentPoint convertToPercent(DisplayIndependentRectangle bounds) {
68
        double newX = (bounds.width == 0) ? 0 : ((double)(x - bounds.x))/ bounds.width;
68
        double newX = (bounds.width == 0) ? 0 : ((double)(x - bounds.x))/ bounds.width;
69
        double newY = (bounds.height == 0) ? 0 : ((double)(y - bounds.y))/bounds.height;
69
        double newY = (bounds.height == 0) ? 0 : ((double)(y - bounds.y))/bounds.height;
70
        return new DisplayIndependentPoint(newX, newY);
70
        return new DisplayIndependentPoint(newX, newY);
71
	}
71
	}
72
	
72
	
73
	/**
73
	/**
74
	 * Converts this point based on the current x, y values from a percentage  
74
	 * Converts this point based on the current x, y values from a percentage  
75
	 * of the specified coordinate system
75
	 * of the specified coordinate system
76
	 * @param bounds
76
	 * @param bounds
77
	 * @return 
77
	 * @return 
78
	 */
78
	 */
79
	public DisplayIndependentPoint convertFromPercent(DisplayIndependentRectangle bounds) {
79
	public DisplayIndependentPoint convertFromPercent(DisplayIndependentRectangle bounds) {
80
		double newX = bounds.x + x * bounds.width;
80
		double newX = bounds.x + x * bounds.width;
81
		double newY = bounds.y + y * bounds.height;
81
		double newY = bounds.y + y * bounds.height;
82
        return new DisplayIndependentPoint(newX, newY);
82
        return new DisplayIndependentPoint(newX, newY);
83
	}
83
	}
84
}
84
}
(-)src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentRectangle.java (-55 / +55 lines)
Lines 1-55 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
2
 * Copyright (c) 2005-2010 The Chisel Group and others. All rights reserved. This
3
 * All rights reserved. This program and the accompanying materials
3
 * program and the accompanying materials are made available under the terms of
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * which accompanies this distribution, and is available at
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 *
7
 * Contributors: The Chisel Group - initial API and implementation
8
 * Contributors:
8
 *               Mateusz Matela 
9
 *     The Chisel Group, University of Victoria
9
 *               Ian Bull
10
 *******************************************************************************/
10
 ******************************************************************************/
11
package org.eclipse.zest.layouts.dataStructures;
11
package org.eclipse.zest.layouts.dataStructures;
12
12
13
13
14
/**
14
/**
15
 * This is a rectangle that isn't dependent on awt, swt, or any other library,
15
 * This is a rectangle that isn't dependent on awt, swt, or any other library,
16
 * except layout.
16
 * except layout.
17
 * 
17
 * 
18
 * @author Casey Best
18
 * @author Casey Best
19
 */
19
 */
20
public class DisplayIndependentRectangle {
20
public class DisplayIndependentRectangle {
21
	
21
	
22
	public double x, y, width, height;
22
	public double x, y, width, height;
23
	
23
	
24
	public DisplayIndependentRectangle() {
24
	public DisplayIndependentRectangle() {
25
		this.x = 0;
25
		this.x = 0;
26
		this.y = 0;
26
		this.y = 0;
27
		this.width = 0;
27
		this.width = 0;
28
		this.height = 0;
28
		this.height = 0;
29
	}
29
	}
30
	
30
	
31
	public DisplayIndependentRectangle(double x, double y, double width, double height) {
31
	public DisplayIndependentRectangle(double x, double y, double width, double height) {
32
		this.x = x;
32
		this.x = x;
33
		this.y = y;
33
		this.y = y;
34
		this.width = width;
34
		this.width = width;
35
		this.height = height;
35
		this.height = height;
36
	}
36
	}
37
37
38
	public DisplayIndependentRectangle(DisplayIndependentRectangle rect) {
38
	public DisplayIndependentRectangle(DisplayIndependentRectangle rect) {
39
		this.x = rect.x;
39
		this.x = rect.x;
40
		this.y = rect.y;
40
		this.y = rect.y;
41
		this.width = rect.width;
41
		this.width = rect.width;
42
		this.height = rect.height;
42
		this.height = rect.height;
43
	}
43
	}
44
	
44
	
45
	public String toString() {
45
	public String toString() {
46
		return "(" + x + ", " + y + ", " + width + ", " + height + ")";
46
		return "(" + x + ", " + y + ", " + width + ", " + height + ")";
47
	}
47
	}
48
	
48
	
49
	public boolean intersects(DisplayIndependentRectangle rect) {
49
	public boolean intersects(DisplayIndependentRectangle rect) {
50
		return rect.x < x + width 
50
		return rect.x < x + width 
51
			&& rect.y < y + height 
51
			&& rect.y < y + height 
52
			&& rect.x + rect.width > x 
52
			&& rect.x + rect.width > x 
53
			&& rect.y + rect.height > y;
53
			&& rect.y + rect.height > y;
54
	}
54
	}
55
}
55
}
(-)src/org/eclipse/zest/layouts/dataStructures/InternalNode.java (-240 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.dataStructures;
12
13
import java.util.HashMap;
14
15
import org.eclipse.zest.layouts.LayoutEntity;
16
import org.eclipse.zest.layouts.constraints.BasicEntityConstraint;
17
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
18
19
/**
20
 * @author Ian Bull
21
 */
22
public class InternalNode implements Comparable, LayoutEntity {
23
	
24
	private LayoutEntity entity = null;
25
	private HashMap attributeMap = new HashMap();
26
	BasicEntityConstraint basicEntityConstraint = new BasicEntityConstraint();
27
	
28
	public InternalNode( LayoutEntity entity ) {
29
		this.entity = entity;
30
		this.entity.setLayoutInformation(this);
31
		this.layoutWidth = entity.getWidthInLayout();
32
		this.layoutHeight = entity.getHeightInLayout();
33
		entity.populateLayoutConstraint(basicEntityConstraint);
34
	}
35
	
36
	public LayoutEntity getLayoutEntity() {
37
		return this.entity;
38
	}
39
	
40
	public double getPreferredX() {
41
		return basicEntityConstraint.preferredX;
42
43
	}
44
	
45
	public double getPreferredY() {
46
		return basicEntityConstraint.preferredY;
47
	}
48
	
49
	public boolean hasPreferredLocation() {
50
		return basicEntityConstraint.hasPreferredLocation;
51
	}
52
	
53
	double dx, dy;
54
	public void setDx( double x ) {
55
		this.dx = x;
56
	}
57
	public void setDy( double y ) {
58
		this.dy = y;
59
	}
60
	public double getDx() {
61
		return this.dx;
62
	}
63
	public double getDy() {
64
		return this.dy;
65
	}	
66
	
67
	public double getCurrentX() {
68
		return entity.getXInLayout();
69
	}
70
	public double getCurrentY() {
71
		return entity.getYInLayout();
72
	}
73
		
74
	public void setLocation( double x, double y ) {
75
		entity.setLocationInLayout( x, y );
76
	}
77
	public void setSize( double width, double height ) {
78
		entity.setSizeInLayout( width, height );
79
	}
80
	
81
	double normalizedX      = 0.0;
82
	double normalizedY      = 0.0;
83
	double normalizedWidth  = 0.0;
84
	double normalizedHeight = 0.0;
85
	
86
	public void setInternalLocation( double x, double y ) {
87
		//entity.setLocationInLayout(x,y);
88
		
89
		normalizedX = x;
90
		normalizedY = y;
91
		
92
	}
93
	
94
	public DisplayIndependentPoint getInternalLocation() {
95
		return new DisplayIndependentPoint(getInternalX(),getInternalY());
96
	}
97
	
98
	public void setInternalSize( double width, double height ) {
99
		normalizedWidth = width;
100
		normalizedHeight = height;
101
	}
102
103
	public double getInternalX() {
104
		//return entity.getXInLayout();
105
		return normalizedX;
106
	}
107
	public double getInternalY() {
108
		//return entity.getYInLayout();
109
		return normalizedY;
110
	}
111
	public double getInternalWidth() {
112
		return normalizedWidth;
113
	}
114
	public double getInternalHeight() {
115
		return normalizedHeight;
116
	}
117
118
	
119
	/**
120
	 * An algorithm may require a place to store information.  Use this structure for that purpose.
121
	 */
122
	public void setAttributeInLayout (Object attribute, Object value) {
123
		attributeMap.put(attribute, value);
124
	}
125
	
126
	/**
127
	 * An algorithm may require a place to store information.  Use this structure for that purpose.
128
	 */
129
	public Object getAttributeInLayout (Object attribute) {
130
		return attributeMap.get( attribute );
131
	}
132
133
	
134
	//TODO: Fix all these preferred stuff!!!!! NOW!
135
	
136
	public boolean hasPreferredWidth () {
137
		return false;
138
	    //return enity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_WIDTH) != null;
139
	}
140
	
141
	public double getPreferredWidth () {
142
		return 0.0;
143
//	    if (hasPreferredWidth()) {
144
//	        return ((Double)entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_WIDTH)).doubleValue();
145
//	    } else {
146
//	        return 10.0;
147
//	    }
148
	}
149
	
150
	public boolean hasPreferredHeight () {
151
		return false;
152
	//    return entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_HEIGHT) != null;
153
	}
154
	
155
	public double getPreferredHeight () {
156
		return 0.0;
157
//	    if (hasPreferredHeight()) {
158
//	        return ((Double)entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_HEIGHT)).doubleValue();
159
//	    } else {
160
//	        return 10.0;
161
//	    }
162
	}
163
	
164
	/* (non-Javadoc)
165
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
166
	 */
167
	public int compareTo(Object arg0) {
168
		return 0;
169
	}
170
	
171
	/* (non-Javadoc)
172
	 * @see java.lang.Object#toString()
173
	 */
174
	public String toString() {
175
		return (entity != null ? entity.toString() : "");
176
	}
177
	
178
	double layoutHeight;
179
	double layoutWidth;
180
	double layoutX;
181
	double layoutY;
182
	Object layoutInfo;
183
184
	public double getHeightInLayout() {
185
		// TODO Auto-generated method stub
186
		return layoutHeight;
187
	}
188
189
	public Object getLayoutInformation() {
190
		// TODO Auto-generated method stub
191
		return this.layoutInfo;
192
	}
193
194
	public double getWidthInLayout() {
195
		// TODO Auto-generated method stub
196
		return layoutWidth;
197
	}
198
199
	public double getXInLayout() {
200
		// TODO Auto-generated method stub
201
		return layoutX;
202
	}
203
204
	public double getYInLayout() {
205
		// TODO Auto-generated method stub
206
		return layoutY;
207
	}
208
209
	public void populateLayoutConstraint(LayoutConstraint constraint) {
210
		// TODO Auto-generated method stub
211
		
212
	}
213
214
	public void setLayoutInformation(Object internalEntity) {
215
		this.layoutInfo = internalEntity;
216
		
217
	}
218
219
	public void setLocationInLayout(double x, double y) {
220
		// TODO Auto-generated method stub
221
		this.layoutX = x;
222
		this.layoutY = y;
223
		
224
	}
225
226
	public void setSizeInLayout(double width, double height) {
227
		this.layoutWidth = width;
228
		this.layoutHeight = height;	
229
	}
230
231
	public Object getGraphData() {
232
		return null;
233
	}
234
235
	public void setGraphData(Object o) {
236
		// TODO Auto-generated method stub
237
		
238
	}
239
	
240
}
(-)src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java (-138 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.dataStructures;
12
13
import java.util.LinkedList;
14
import java.util.List;
15
16
import org.eclipse.zest.layouts.LayoutBendPoint;
17
import org.eclipse.zest.layouts.LayoutEntity;
18
import org.eclipse.zest.layouts.LayoutRelationship;
19
import org.eclipse.zest.layouts.constraints.BasicEdgeConstraints;
20
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
21
22
/**
23
 * @author Ian Bull
24
 */
25
public class InternalRelationship implements LayoutRelationship{
26
	
27
	private LayoutRelationship externalRelationship;
28
	private InternalNode source;
29
	private InternalNode destination;
30
	private Object layoutInfo;
31
	private List bendPoints = new LinkedList();
32
	BasicEdgeConstraints basicEdgeConstraints = new BasicEdgeConstraints();
33
	
34
	public InternalRelationship( LayoutRelationship externalRelationship, InternalNode source, InternalNode destination) {
35
		this.externalRelationship = externalRelationship;
36
		this.externalRelationship.setLayoutInformation(this);
37
		this.source = source;
38
		this.destination = destination;
39
		this.externalRelationship.populateLayoutConstraint(basicEdgeConstraints);
40
	}
41
	
42
	public LayoutRelationship getLayoutRelationship() {
43
		return externalRelationship;
44
	}
45
	
46
	public InternalNode getSource() {
47
		if ( this.source == null ) {
48
			throw new RuntimeException("Source is null");
49
		}
50
		return this.source;
51
	}
52
	
53
	public InternalNode getDestination() {
54
		if ( this.destination == null ) {
55
			throw new RuntimeException("Dest is null");
56
		}
57
		return this.destination;
58
	}
59
	
60
	public double getWeight() {
61
		return this.basicEdgeConstraints.weight;
62
	}
63
	
64
	public boolean isBidirectional() {
65
		return this.basicEdgeConstraints.isBiDirectional;
66
	}
67
	
68
	/**
69
	 * Ensure this is called in order of source to target node position.
70
	 * @param x
71
	 * @param y
72
	 */
73
	public void addBendPoint(double x, double y) {
74
		bendPoints.add(new BendPoint(x, y));
75
	}
76
	
77
	/**
78
	 * Ensure this is called in order of source to target node position.
79
	 * Specifies if the bendpoint is a curve control point
80
	 * @param x
81
	 * @param y
82
	 * @param isControlPoint
83
	 */
84
	public void addBendPoint(double x, double y, boolean isControlPoint) {
85
		bendPoints.add(new BendPoint(x, y, isControlPoint));
86
	}
87
	
88
	public List getBendPoints() {
89
		return this.bendPoints;
90
	}
91
92
	public void clearBendPoints() {
93
		// TODO Auto-generated method stub
94
		
95
	}
96
97
	
98
	
99
	public LayoutEntity getDestinationInLayout() {
100
		// TODO Auto-generated method stub
101
		return destination;
102
	}
103
104
	
105
	public Object getLayoutInformation() {
106
		// TODO Auto-generated method stub
107
		return layoutInfo;
108
	}
109
110
	public LayoutEntity getSourceInLayout() {
111
		// TODO Auto-generated method stub
112
		return source;
113
	}
114
115
	public void populateLayoutConstraint(LayoutConstraint constraint) {
116
		// TODO Auto-generated method stub
117
		
118
	}
119
120
	public void setBendPoints(LayoutBendPoint[] bendPoints) {
121
		// TODO Auto-generated method stub
122
		
123
	}
124
125
	public void setLayoutInformation(Object layoutInformation) {
126
		this.layoutInfo = layoutInformation;
127
		
128
	}
129
130
	public Object getGraphData() {
131
		return null;
132
	}
133
134
	public void setGraphData(Object o) {
135
		
136
	}
137
138
}
(-)src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java (-30 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.exampleStructures;
12
13
import org.eclipse.zest.layouts.Filter;
14
import org.eclipse.zest.layouts.LayoutItem;
15
16
/**
17
 * A very simple example of a filter.  This filter never filters
18
 * any object.
19
 * 
20
 * @author Casey Best
21
 */
22
public class SimpleFilter implements Filter {
23
24
	/**
25
	 * Doesn't filter anything
26
	 */
27
	public boolean isObjectFiltered(LayoutItem object) {
28
		return false;
29
	}
30
}
(-)src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java (-140 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.exampleStructures;
12
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.List;
17
import java.util.Map;
18
19
import org.eclipse.zest.layouts.LayoutEntity;
20
import org.eclipse.zest.layouts.LayoutGraph;
21
import org.eclipse.zest.layouts.LayoutRelationship;
22
23
24
/**
25
 * Create a very simple graph that can be used in the layout algorithms
26
 * 
27
 * @author Casey Best
28
 * @author Chris Callendar
29
 */
30
public class SimpleGraph implements LayoutGraph {
31
	
32
	Map objectsToNodes;
33
	List relationships;
34
	
35
	public SimpleGraph() {
36
		objectsToNodes = new HashMap();
37
		relationships = new ArrayList();
38
	}
39
	
40
	/**
41
	 * Adds the node.
42
	 * @param node	The node to add.
43
	 */
44
	public void addEntity(LayoutEntity node) {
45
		if (node instanceof SimpleNode) {
46
			objectsToNodes.put(((SimpleNode)node).getRealObject(), node);
47
		}
48
	}
49
	
50
	/**
51
	 * Creates a LayoutEntity containing an object.
52
	 */
53
	public LayoutEntity addObjectNode(Object object) {
54
	    SimpleNode simpleNode = (SimpleNode) objectsToNodes.get (object);
55
		if (simpleNode == null) {
56
			simpleNode = new SimpleNode(object);
57
			objectsToNodes.put (object, simpleNode);
58
		}
59
		return simpleNode;
60
		
61
	}
62
	
63
	/**
64
	 * Add a relationship between two objects.  Layout algorithms need to know
65
	 * whether a relationship is one way or bi-directional.  This method assumes that 
66
	 * all relationships are bi-direcional and have the same weight. 
67
	 */
68
	public void addObjectRelationship (Object sourceNode, Object destinationNode) {
69
		addObjectRelationship(sourceNode, destinationNode, true, 1);
70
	}
71
	
72
	/**
73
	 * Add a relationship between two objects.  Layout algorithms need to know
74
	 * whether a relationship is one way or bi-directional.  
75
	 */
76
	public void addObjectRelationship (Object sourceObject, Object destinationObject, boolean bidirectional, int weight) {
77
		addObjectNode(sourceObject);
78
		addObjectNode(destinationObject);
79
		SimpleNode sourceNode = (SimpleNode) objectsToNodes.get(sourceObject);
80
		SimpleNode destinationNode = (SimpleNode) objectsToNodes.get(destinationObject);
81
		SimpleRelationship simpleRelationship = new SimpleRelationship(sourceNode, destinationNode, bidirectional, weight);
82
		relationships.add(simpleRelationship);
83
	}
84
85
	/* (non-Javadoc)
86
	 * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutEntity, ca.uvic.cs.chisel.layouts.LayoutEntity)
87
	 */
88
	public void addRelationship(LayoutEntity srcNode, LayoutEntity destNode) {
89
		addRelationship(srcNode, destNode, true, 1);
90
	}
91
	
92
	/* (non-Javadoc)
93
	 * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutEntity, ca.uvic.cs.chisel.layouts.LayoutEntity, boolean, int)
94
	 */
95
	public void addRelationship(LayoutEntity srcNode, LayoutEntity destNode, boolean bidirectional, int weight) {
96
		addEntity(srcNode);
97
		addEntity(destNode);
98
		SimpleRelationship rel = new SimpleRelationship(srcNode, destNode, bidirectional, weight);
99
		relationships.add(rel);
100
	}
101
	
102
	/* (non-Javadoc)
103
	 * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutRelationship)
104
	 */
105
	public void addRelationship(LayoutRelationship relationship) {
106
		relationships.add(relationship);
107
	}
108
109
	/**
110
	 * Returns a list of SimpleNodes that represent the objects added to this graph using addNode.  Note that
111
	 * any manipulation to this graph was done on the SimpleNodes, not the real objects.  You
112
	 * must still manipulate them yourself.
113
	 */
114
	public List getEntities() {
115
		return new ArrayList (objectsToNodes.values());
116
	}
117
118
	/**
119
	 * Returns a list of SimpleRelationships that represent the objects added to this graph using addRelationship.
120
	 */
121
	public List getRelationships() {
122
		return relationships;
123
	}
124
	
125
	/**
126
	 * Checks the relationships to see if they are all bidirectional. 
127
	 * @return boolean if all edges are bidirectional.
128
	 */
129
	public boolean isBidirectional() {
130
		boolean isBidirectional = true;
131
		for (Iterator iter = relationships.iterator(); iter.hasNext(); ) {
132
			SimpleRelationship rel = (SimpleRelationship) iter.next();
133
			if (!rel.isBidirectionalInLayout()) {
134
				isBidirectional = false;
135
				break;
136
			}
137
		}
138
		return isBidirectional;
139
	}
140
}
(-)src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java (-318 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.exampleStructures;
12
13
import java.util.Comparator;
14
import java.util.HashMap;
15
import java.util.LinkedList;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.TreeSet;
19
20
import org.eclipse.zest.layouts.LayoutEntity;
21
import org.eclipse.zest.layouts.constraints.BasicEntityConstraint;
22
import org.eclipse.zest.layouts.constraints.EntityPriorityConstraint;
23
import org.eclipse.zest.layouts.constraints.LabelLayoutConstraint;
24
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
25
26
/**
27
 * Rerpresents a simple node that can be used in the layout algorithms.
28
 * 
29
 * @author Ian Bull
30
 * @author Casey Best (Version 1 by Rob Lintern)
31
 * @version 2
32
 */
33
public class SimpleNode implements LayoutEntity {
34
	private static Object NODE_NORMAL_COLOR;
35
	private static Object NODE_SELECTED_COLOR; 
36
	private static Object NODE_ADJACENT_COLOR;	
37
    private static Object BORDER_NORMAL_COLOR;
38
	private static Object BORDER_SELECTED_COLOR;
39
	private static Object BORDER_ADJACENT_COLOR;	
40
41
    private static final int BORDER_NORMAL_STROKE = 1;
42
	private static final int BORDER_STROKE_SELECTED = 2;
43
	
44
	/**
45
	 * A list of layout dependent attributes
46
	 */
47
	private Map attributes;
48
49
	protected double x, y, width, height;
50
	protected Object realObject;
51
	private boolean ignoreInLayout = false;
52
	
53
	private Object colour = null;
54
	private Object borderColor = null;
55
	private int borderWidth;
56
	
57
	private TreeSet listOfRels = null;
58
	
59
	private Object internalNode;
60
61
	
62
	/**
63
	 * Constructs a new SimpleNode.
64
	 */
65
	public SimpleNode(Object realObject) {
66
		this (realObject, -1, -1, 110, 110);
67
	}
68
	
69
	class UniqueCompare implements Comparator {
70
		public int compare(Object o1, Object o2) {
71
			// TODO this may not always be a unique comparison
72
			return o1.toString().compareTo( o2.toString() );
73
		}
74
	}
75
	
76
	/**
77
	 * Constructs a new SimpleNode.
78
	 */
79
	public SimpleNode(Object realObject, double x, double y, double width, double height) {
80
		this.realObject = realObject;
81
		this.x = x;
82
		this.y = y;
83
		this.width = width;
84
		this.height = height;
85
		this.attributes = new HashMap();
86
		this.borderWidth = BORDER_NORMAL_STROKE;
87
		listOfRels = new TreeSet( new UniqueCompare() );
88
        this.colour = NODE_NORMAL_COLOR;
89
        this.borderColor = BORDER_NORMAL_COLOR;
90
	}
91
    
92
    public static void setNodeColors (Object nodeNormalColor, Object borderNormalColor, Object nodeSelectedColor, Object nodeAdjacentColor, Object borderSelectedColor, Object borderAdjacentColor) {
93
        NODE_NORMAL_COLOR = nodeNormalColor;
94
        BORDER_NORMAL_COLOR = borderNormalColor;
95
        NODE_SELECTED_COLOR = nodeSelectedColor;
96
        NODE_ADJACENT_COLOR = nodeAdjacentColor;
97
        BORDER_SELECTED_COLOR = borderSelectedColor;
98
        BORDER_ADJACENT_COLOR = borderAdjacentColor;       
99
    }
100
	
101
	public void addRelationship( SimpleRelationship rel ) {
102
		listOfRels.add( rel );
103
	}
104
	
105
	
106
	public SimpleRelationship[] getRelationships() {
107
		int size = listOfRels.size();
108
		return (SimpleRelationship[]) this.listOfRels.toArray( new SimpleRelationship[ size ] );
109
	}
110
	
111
	public List getRelatedEntities() {
112
		int size = listOfRels.size();
113
		SimpleRelationship[] a_listOfRels = (SimpleRelationship[]) this.listOfRels.toArray( new SimpleRelationship[ size ] );
114
		LinkedList listOfRelatedEntities = new LinkedList(); 
115
		for (int i = 0; i < a_listOfRels.length; i++) {
116
			SimpleRelationship rel = a_listOfRels[ i ];
117
			if ( rel.sourceEntity != this && rel.destinationEntity != this  ) {
118
				throw new RuntimeException("Problem, we have a relationship and we are not the source or the dest");
119
			}
120
			if ( rel.sourceEntity != this ) {
121
				listOfRelatedEntities.add( rel.sourceEntity );
122
			}
123
			if ( rel.destinationEntity != this ) {
124
				listOfRelatedEntities.add( rel.destinationEntity );
125
			}
126
		
127
		}
128
		return listOfRelatedEntities;
129
	}
130
	
131
	
132
	
133
	/**
134
	 * Ignores this entity in the layout
135
	 * @param ignore Should this entity be ignored
136
	 */
137
	public void ignoreInLayout( boolean ignore ) {
138
		this.ignoreInLayout = ignore;
139
	}
140
	
141
	public Object getRealObject() {
142
		return realObject;
143
	}
144
	
145
	public boolean hasPreferredLocation() {
146
		return this.ignoreInLayout;
147
	}
148
149
	/**
150
	 * Gets the x position of this SimpleNode.
151
	 */
152
	public double getX() {
153
		return x;
154
	}
155
	
156
	/**
157
	 * Gets the y position of this SimpleNode.
158
	 */
159
	public double getY() {
160
		return y;
161
	}
162
163
	/**
164
	 * Get the size of this node
165
	 */
166
	public double getWidth() {
167
		return width;
168
	}
169
170
	/**
171
	 * Get the size of this node
172
	 */
173
	public double getHeight() {
174
		return height;
175
	}
176
		
177
	public void setSizeInLayout(double width, double height) {
178
		if (!ignoreInLayout) {
179
			this.width = width;
180
			this.height = height;
181
		}
182
	}
183
	
184
	public void setLocation( double x, double y ) {
185
		this.x = x;
186
		this.y = y;		
187
	}
188
	
189
	public void setLocationInLayout(double x, double y) {
190
		if (!ignoreInLayout) {
191
			this.x = x;
192
			this.y = y;
193
		}
194
	}
195
	
196
	/**
197
	 * An algorithm may require a place to store information.  Use this structure for that purpose.
198
	 */
199
	public void setAttributeInLayout (Object attribute, Object value) {
200
		attributes.put (attribute, value);
201
	}
202
	
203
	/**
204
	 * An algorithm may require a place to store information.  Use this structure for that purpose.
205
	 */
206
	public Object getAttributeInLayout (Object attribute) {
207
		return attributes.get (attribute);
208
	}
209
210
	public boolean equals(Object object) {
211
		boolean result = false;
212
		if (object instanceof SimpleNode) {
213
			SimpleNode node = (SimpleNode)object;
214
			result = realObject.equals (node.getRealObject());
215
		}
216
		return result;
217
	}
218
	
219
	public int hashCode() {
220
		return realObject.hashCode();
221
	}
222
	
223
	// all objects are equal
224
	public int compareTo(Object arg0) {
225
		return 0;
226
	}
227
	
228
	public String toString() {
229
		return realObject.toString();
230
	}
231
	
232
	public void setSelected() {
233
		this.colour = NODE_SELECTED_COLOR;
234
		this.borderColor = BORDER_SELECTED_COLOR;
235
		this.borderWidth = BORDER_STROKE_SELECTED;
236
	}
237
	
238
	public void setUnSelected() {
239
		this.colour = NODE_NORMAL_COLOR;
240
		this.borderColor = BORDER_NORMAL_COLOR;
241
		this.borderWidth = BORDER_NORMAL_STROKE;
242
	}
243
	
244
	public void setAdjacent() {
245
		this.colour = NODE_ADJACENT_COLOR;
246
		this.borderColor = BORDER_ADJACENT_COLOR;
247
		this.borderWidth = BORDER_STROKE_SELECTED;
248
	}
249
    
250
	public Object getColor() {
251
		return this.colour;
252
	}
253
	
254
	public int getBorderWidth() {
255
		return borderWidth;
256
	}
257
		
258
	public Object getBorderColor() {
259
		return borderColor;
260
	}
261
	
262
	/* (non-Javadoc)
263
	 * @see ca.uvic.cs.chisel.layouts.LayoutEntity#getInternalEntity()
264
	 */
265
	public Object getLayoutInformation() {
266
		return internalNode;
267
	}
268
269
	/* (non-Javadoc)
270
	 * @see ca.uvic.cs.chisel.layouts.LayoutEntity#setInternalEntity(java.lang.Object)
271
	 */
272
	public void setLayoutInformation(Object internalEntity) {
273
		this.internalNode = internalEntity;
274
	}
275
276
	/**
277
	 * Populate the specified layout constraint
278
	 */	
279
	public void populateLayoutConstraint(LayoutConstraint constraint) {
280
		if ( constraint instanceof LabelLayoutConstraint ) {
281
			LabelLayoutConstraint labelConstraint = (LabelLayoutConstraint) constraint;
282
			labelConstraint.label = realObject.toString();
283
			labelConstraint.pointSize = 18;
284
		}		
285
		else if ( constraint instanceof BasicEntityConstraint ) {
286
			// noop
287
		}
288
		else if ( constraint instanceof EntityPriorityConstraint ) {
289
			EntityPriorityConstraint priorityConstraint = (EntityPriorityConstraint) constraint;
290
			priorityConstraint.priority = Math.random() * 10 + 1;
291
		}
292
	}
293
294
	public double getHeightInLayout() {
295
		return this.height;
296
	}
297
298
	public double getWidthInLayout() {
299
		return this.width;
300
	}
301
302
	public double getXInLayout() {
303
		return this.x;
304
	}
305
306
	public double getYInLayout() {
307
		return this.y;
308
	}
309
310
	public Object getGraphData() {
311
		return null;
312
	}
313
314
	public void setGraphData(Object o) {
315
	
316
	}
317
	
318
}
(-)src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java (-274 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.exampleStructures;
12
13
import java.util.HashMap;
14
import java.util.Map;
15
16
import org.eclipse.zest.layouts.LayoutBendPoint;
17
import org.eclipse.zest.layouts.LayoutEntity;
18
import org.eclipse.zest.layouts.LayoutRelationship;
19
import org.eclipse.zest.layouts.constraints.BasicEdgeConstraints;
20
import org.eclipse.zest.layouts.constraints.LabelLayoutConstraint;
21
import org.eclipse.zest.layouts.constraints.LayoutConstraint;
22
import org.eclipse.zest.layouts.dataStructures.BendPoint;
23
24
25
/**
26
 * The SimpleRelation class describes the relationship between
27
 * two objects: source and destination.  Each relationship
28
 * has a weight and direction associated with it.
29
 * Note: The source object is at the beginning of the relationship.
30
 * Note: The destination object is at the end of the relationship.
31
 *
32
 * @version  2.0
33
 * @author   Casey Best (version 1.0 by Jingwei Wu)
34
 * @author Chris Bennett
35
 */
36
public class SimpleRelationship implements LayoutRelationship {
37
	
38
	private static int DEFAULT_REL_LINE_WIDTH = 1;
39
	private static int DEFAULT_REL_LINE_WIDTH_SELECTED = DEFAULT_REL_LINE_WIDTH + 2;
40
	private static Object DEFAULT_RELATIONSHIP_COLOR;
41
	private static Object DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR;	
42
	
43
	/** The line width for this relationship. */
44
	private int lineWidth  = DEFAULT_REL_LINE_WIDTH;
45
	
46
	/** The color for this relationship. */
47
	private Object color = DEFAULT_RELATIONSHIP_COLOR;
48
	
49
	/**
50
	 * A list of layout dependent attributes
51
	 */
52
	private Map attributes;
53
54
	/**
55
	 * The sourceEntity of this SimpleRelation.
56
	 */
57
	protected LayoutEntity sourceEntity;
58
59
	/**
60
	 * The object of this SimpleRelation.
61
	 */
62
	protected LayoutEntity destinationEntity;
63
64
	/**
65
	 * If directional, algorithms must note the direction of the relationship.
66
	 * If not directional, algorithms are to ignore which direction the relationship is going.
67
	 * Switching the source and destination should make no difference. 
68
	 */
69
	protected boolean bidirectional;
70
71
	/**
72
	 * The weight given to this relation.
73
	 */
74
	private double weight;
75
	
76
	private Object internalRelationship;
77
	
78
	private LayoutBendPoint[] bendPoints;
79
	
80
	private String label;
81
	
82
	/**
83
	 * Constructor.
84
	 * @param sourceEntity The sourceEntity of this SimpleRelation.
85
	 * @param destinationEntity The object of this SimpleRelation.
86
	 * @param bidirectional Determines if the <code>sourceEntity</code> and
87
	 * <code>destinationEntity</code> are equal(exchangeable).
88
	 * @throws java.lang.NullPointerException If either <code>sourceEntity
89
	 * </code> or <code>destinationEntity</code> is <code>null</code>.
90
	 */
91
	public SimpleRelationship(LayoutEntity sourceEntity, LayoutEntity destinationEntity, boolean bidirectional) {
92
		this (sourceEntity, destinationEntity, bidirectional, 1);
93
	}
94
	
95
	/**
96
	 * Constructor.
97
	 * @param sourceEntity The sourceEntity of this SimpleRelation.
98
	 * @param destinationEntity The destinationEntity of this SimpleRelation.
99
	 * @param exchangeable Determines if the <code>sourceEntity</code> and
100
	 * <code>destinationEntity</code> are equal(exchangeable).
101
	 * @throws java.lang.NullPointerException If either <code>sourceEntity
102
	 * </code> or <code>destinationEntity</code> is <code>null</code>.
103
	 */
104
	public SimpleRelationship(LayoutEntity sourceEntity, LayoutEntity destinationEntity, boolean bidirectional, double weight) {
105
		this.destinationEntity = destinationEntity;
106
		this.sourceEntity = sourceEntity;
107
		this.bidirectional = bidirectional;
108
		this.weight = weight;
109
		this.attributes = new HashMap();
110
		this.lineWidth = DEFAULT_REL_LINE_WIDTH;
111
		this.color = DEFAULT_RELATIONSHIP_COLOR;
112
	}
113
114
	/**
115
	 * Gets the sourceEntity of this SimpleRelation whether the relation is
116
	 * exchangeable or not.
117
	 * @return The sourceEntity.
118
	 */
119
	public LayoutEntity getSourceInLayout() {
120
		return sourceEntity;
121
	}
122
123
	/**
124
	 * Gets the destinationEntity of this SimpleRelation whether the relation is
125
	 * exchangeable or not.
126
	 * @return The destinationEntity of this SimpleRelation.
127
	 */
128
	public LayoutEntity getDestinationInLayout() {
129
		return destinationEntity;
130
	}
131
132
	/**
133
	 * If bidirectional, the direction of the relationship doesn't matter.  Switching the source and destination should make no difference.
134
	 * If not bidirectional, layout algorithms need to take into account the direction of the relationship.  The direction is based on the
135
	 * source and destination entities.
136
	 */
137
	public boolean isBidirectionalInLayout() {
138
		return bidirectional;
139
	}
140
141
	public void setWeightInLayout(double weight) {
142
		this.weight = weight;
143
	}
144
145
	public double getWeightInLayout() {
146
		return weight;
147
	}
148
149
	/**
150
	 * An algorithm may require a place to store information.  Use this structure for that purpose.
151
	 */
152
	public void setAttributeInLayout (String attribute, Object value) {
153
		attributes.put (attribute, value);
154
	}
155
	
156
	/**
157
	 * An algorithm may require a place to store information.  Use this structure for that purpose.
158
	 */
159
	public Object getAttributeInLayout (String attribute) {
160
		return attributes.get (attribute);
161
	}
162
163
	public String toString() {
164
		String arrow = (isBidirectionalInLayout() ? " <-> " : " -> "); 
165
		return "(" + sourceEntity + arrow + destinationEntity + ")";
166
	}
167
	
168
	public int getLineWidth() {
169
		return this.lineWidth;
170
	}
171
	
172
	public void setLineWidth( int lineWidth ) {
173
		this.lineWidth = lineWidth;
174
	}
175
	
176
	public void resetLineWidth() {
177
		this.lineWidth = DEFAULT_REL_LINE_WIDTH;
178
	}
179
	
180
	public static void setDefaultSize(int i) {
181
		DEFAULT_REL_LINE_WIDTH = i;
182
		DEFAULT_REL_LINE_WIDTH_SELECTED = DEFAULT_REL_LINE_WIDTH + 2;
183
	}
184
	
185
	public void setSelected() {
186
		this.color = DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR;
187
		this.lineWidth = DEFAULT_REL_LINE_WIDTH_SELECTED;
188
	}
189
	
190
	public void setUnSelected() {
191
		this.color = DEFAULT_RELATIONSHIP_COLOR;
192
		this.lineWidth = DEFAULT_REL_LINE_WIDTH;
193
	}
194
	
195
	public Object getColor() {
196
		return color;
197
	}
198
	
199
	public void setColor(Object c) {
200
		this.color = c;
201
	}
202
	
203
	public static void setDefaultColor(Object c) {
204
		DEFAULT_RELATIONSHIP_COLOR = c;
205
	}
206
	
207
	public static void setDefaultHighlightColor(Object c) {
208
		DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR = c;
209
	}
210
211
	/* (non-Javadoc)
212
	 * @see ca.uvic.cs.chisel.layouts.LayoutRelationship#getInternalRelationship()
213
	 */
214
	public Object getLayoutInformation() {
215
		return internalRelationship;
216
	}
217
218
	/* (non-Javadoc)
219
	 * @see ca.uvic.cs.chisel.layouts.LayoutRelationship#setInternalRelationship(java.lang.Object)
220
	 */
221
	public void setLayoutInformation(Object layoutInformation) {
222
		this.internalRelationship = layoutInformation;
223
	}
224
225
226
	public void setBendPoints(LayoutBendPoint[] bendPoints) {
227
		this.bendPoints = bendPoints;
228
	}
229
	
230
	public LayoutBendPoint[] getBendPoints() {
231
		return this.bendPoints;
232
	}
233
	
234
	public void clearBendPoints() {
235
		this.bendPoints = new BendPoint[0];
236
	}
237
	
238
	
239
	public void setDestinationInLayout(LayoutEntity destination) {
240
		this.destinationEntity = destination;
241
	}
242
	
243
	/**
244
	 * Set the label for this edge (available in the label layout constraint). 
245
	 */	
246
	public void setLabel(String label) {
247
		this.label = label;
248
	}
249
250
	/**
251
	 * Populate the specified layout constraint
252
	 */	
253
	public void populateLayoutConstraint(LayoutConstraint constraint) {
254
		if ( constraint instanceof LabelLayoutConstraint ) {
255
			LabelLayoutConstraint labelConstraint = (LabelLayoutConstraint) constraint;
256
			labelConstraint.label = this.label;
257
			labelConstraint.pointSize = 18;
258
		}			
259
		else if ( constraint instanceof BasicEdgeConstraints ) {
260
			// noop
261
			
262
		}
263
	}
264
265
	public Object getGraphData() {
266
		return null;
267
	}
268
269
	public void setGraphData(Object o) {
270
		
271
	}
272
273
	
274
}
(-)src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java (-785 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials are made
4
 * available under the terms of the Eclipse Public License v1.0 which
5
 * accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 * Contributors: The Chisel Group, University of Victoria
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.exampleUses;
11
12
import java.util.ArrayList;
13
import java.util.Date;
14
import java.util.Iterator;
15
import java.util.List;
16
17
import org.eclipse.core.runtime.IProgressMonitor;
18
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
19
import org.eclipse.zest.layouts.InvalidLayoutConfiguration;
20
import org.eclipse.zest.layouts.LayoutAlgorithm;
21
import org.eclipse.zest.layouts.LayoutBendPoint;
22
import org.eclipse.zest.layouts.LayoutEntity;
23
import org.eclipse.zest.layouts.LayoutRelationship;
24
import org.eclipse.zest.layouts.LayoutStyles;
25
import org.eclipse.zest.layouts.algorithms.GridLayoutAlgorithm;
26
import org.eclipse.zest.layouts.algorithms.HorizontalLayoutAlgorithm;
27
import org.eclipse.zest.layouts.algorithms.HorizontalTreeLayoutAlgorithm;
28
import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm;
29
import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm;
30
import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
31
import org.eclipse.zest.layouts.algorithms.VerticalLayoutAlgorithm;
32
import org.eclipse.zest.layouts.exampleStructures.SimpleNode;
33
import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship;
34
import org.eclipse.zest.layouts.progress.ProgressEvent;
35
import org.eclipse.zest.layouts.progress.ProgressListener;
36
import org.eclipse.swt.SWT;
37
import org.eclipse.swt.SWTError;
38
import org.eclipse.swt.events.ControlEvent;
39
import org.eclipse.swt.events.ControlListener;
40
import org.eclipse.swt.events.DisposeEvent;
41
import org.eclipse.swt.events.DisposeListener;
42
import org.eclipse.swt.events.MouseEvent;
43
import org.eclipse.swt.events.MouseListener;
44
import org.eclipse.swt.events.MouseMoveListener;
45
import org.eclipse.swt.events.PaintEvent;
46
import org.eclipse.swt.events.PaintListener;
47
import org.eclipse.swt.events.SelectionEvent;
48
import org.eclipse.swt.events.SelectionListener;
49
import org.eclipse.swt.graphics.Color;
50
import org.eclipse.swt.graphics.GC;
51
import org.eclipse.swt.graphics.Image;
52
import org.eclipse.swt.graphics.Point;
53
import org.eclipse.swt.graphics.Rectangle;
54
import org.eclipse.swt.layout.FillLayout;
55
import org.eclipse.swt.layout.GridData;
56
import org.eclipse.swt.layout.GridLayout;
57
import org.eclipse.swt.widgets.Canvas;
58
import org.eclipse.swt.widgets.Composite;
59
import org.eclipse.swt.widgets.Display;
60
import org.eclipse.swt.widgets.Label;
61
import org.eclipse.swt.widgets.Shell;
62
import org.eclipse.swt.widgets.ToolBar;
63
import org.eclipse.swt.widgets.ToolItem;
64
65
/**
66
 * @author Rob Lintern
67
 * @author Ian Bull
68
 * A simple example of using layout algorithms with a SWT application.
69
 */
70
public class SimpleSWTExample {
71
72
	private static final Color BLACK = new Color(Display.getDefault(), 0, 0, 0);
73
	private static final Color NODE_NORMAL_COLOR = new Color(Display.getDefault(), 225, 225, 255);
74
	private static final Color NODE_SELECTED_COLOR = new Color(Display.getDefault(), 255, 125, 125);
75
	private static final Color NODE_ADJACENT_COLOR = new Color(Display.getDefault(), 255, 200, 125);
76
	private static final Color BORDER_NORMAL_COLOR = new Color(Display.getDefault(), 0, 0, 0);
77
	private static final Color BORDER_SELECTED_COLOR = new Color(Display.getDefault(), 255, 0, 0);
78
	private static final Color BORDER_ADJACENT_COLOR = new Color(Display.getDefault(), 255, 128, 0);
79
	private static final Color RELATIONSHIP_COLOR = new Color(Display.getDefault(), 192, 192, 225);
80
	private static final Color RELATIONSHIP_HIGHLIGHT_COLOR = new Color(Display.getDefault(), 255, 200, 125);
81
82
	private static final String[] NAMES = new String[] { "Peggy", "Rob", "Ian", "Chris", "Simon", "Wendy", "Steven", "Kim", "Neil", "Dave", "John", "Suzanne", "Jody", "Casey", "Bjorn", "Peter", "Erin", "Lisa", "Jennie", "Liz", "Bert", "Ryan", "Nick", "Amy", "Lee", "Me", "You", "Max", "NCI", "OWL",
83
			"Ed", "Jamie", "Protege", "Matt", "Bryan", "Pete", "Sam", "Bob", "Katie", "Bill", "Josh", "Davor", "Ken", "Jacob", "Norm", "Jim", "Maya", "Jill", "Kit", "Jo", "Joe", "Andrew", "Charles", "Pat", "Patrick", "Jeremy", "Mike", "Michael", "Patricia", "Marg", "Terry", "Emily", "Ben", "Holly",
84
			"Joanna", "Joanne", "Evan", "Tom", "Dan", "Eric", "Corey", "Meghan", "Kevin", "Nina", "Ron", "Daniel", "David", "Jeff", "Nathan", "Amanda", "Phil", "Tricia", "Steph", "Stewart", "Stuart", "Bull", "Lintern", "Callendar", "Thompson", "Rigby", "Adam", "Judith", "Cynthia", "Sarah", "Sara",
85
			"Roger", "Andy", "Kris", "Mark", "Shane", "Spence", "Ivy", "Ivanna", "Julie", "Justin", "Emile", "Toby", "Robin", "Rich", "Kathy", "Cathy", "Nicky", "Ricky", "Danny", "Anne", "Ann", "Jen", "Robert", "Calvin", "Alvin", "Scott", "Kumar" };
86
87
	//private static final boolean RENDER_HIGH_QUALITY = true;
88
89
	private static final int INITIAL_PANEL_WIDTH = 800;
90
	private static final int INITIAL_PANEL_HEIGHT = 600;
91
	private static final double INITIAL_NODE_WIDTH = 20;
92
	private static final double INITIAL_NODE_HEIGHT = 15;
93
94
	protected static ArrayList algorithms = new ArrayList();
95
	{
96
		algorithms.add(new SpringLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING));
97
		algorithms.add(new TreeLayoutAlgorithm(LayoutStyles.NONE));
98
		algorithms.add(new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE));
99
		algorithms.add(new RadialLayoutAlgorithm(LayoutStyles.NONE));
100
		algorithms.add(new GridLayoutAlgorithm(LayoutStyles.NONE));
101
		algorithms.add(new HorizontalLayoutAlgorithm(LayoutStyles.NONE));
102
		algorithms.add(new VerticalLayoutAlgorithm(LayoutStyles.NONE));
103
	}
104
105
	protected static ArrayList algorithmNames = new ArrayList();
106
	{
107
		algorithmNames.add("Spring");
108
		algorithmNames.add("Fade");
109
		algorithmNames.add("Tree - V");
110
		algorithmNames.add("Tree - H");
111
		algorithmNames.add("Radial");
112
		algorithmNames.add("Grid");
113
		algorithmNames.add("Horizontal");
114
		algorithmNames.add("Vertical");
115
	}
116
117
	protected static ArrayList algorithmAnimates = new ArrayList();
118
	{
119
		algorithmAnimates.add(Boolean.TRUE);
120
		algorithmAnimates.add(Boolean.TRUE);
121
		algorithmAnimates.add(Boolean.FALSE);
122
		algorithmAnimates.add(Boolean.FALSE);
123
		algorithmAnimates.add(Boolean.FALSE);
124
		algorithmAnimates.add(Boolean.FALSE);
125
		algorithmAnimates.add(Boolean.FALSE);
126
		algorithmAnimates.add(Boolean.FALSE);
127
	}
128
129
	//private long updateGUICount = 0;
130
	private boolean animate = true;
131
	private static boolean continuous = false;
132
	private static boolean asynchronously = false;
133
134
	private Shell mainShell;
135
	private Composite mainComposite;
136
	private List entities;
137
	private List relationships;
138
139
	private ToolBar toolBar;
140
	private Label lblProgress;
141
142
	private LayoutAlgorithm currentLayoutAlgorithm;
143
	protected SimpleNode selectedEntity;
144
	protected SimpleNode hoverEntity;
145
146
	protected Point mouseDownPoint;
147
	protected Point selectedEntityPositionAtMouseDown;
148
	private long idCount = 0;
149
150
	public SimpleSWTExample(Display display) {
151
		mainShell = new Shell(display);
152
		mainShell.addControlListener(new ControlListener() {
153
			public void controlMoved(ControlEvent e) {
154
			}
155
156
			public void controlResized(ControlEvent e) {
157
				mainShell.layout(true);
158
			}
159
		});
160
		GridLayout gridLayout = new GridLayout(1, true);
161
		mainShell.setLayout(gridLayout);
162
		GridData toolbarGridData = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.VERTICAL_ALIGN_BEGINNING, true, true);
163
		toolBar = new ToolBar(mainShell, SWT.HORIZONTAL);
164
		toolBar.setLayoutData(toolbarGridData);
165
		toolBar.setLayout(new FillLayout(SWT.HORIZONTAL));
166
167
		GridData progressGridData = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.VERTICAL_ALIGN_END, true, false);
168
		progressGridData.widthHint = 300;
169
		lblProgress = new Label(mainShell, SWT.NONE);
170
		lblProgress.setLayoutData(progressGridData);
171
		lblProgress.setText("Progress: ");
172
173
		for (int i = 0; i < algorithms.size(); i++) {
174
			final LayoutAlgorithm algorithm = (LayoutAlgorithm) algorithms.get(i);
175
			String algorithmName = (String) algorithmNames.get(i);
176
			final boolean algorithmAnimate = ((Boolean) algorithmAnimates.get(i)).booleanValue();
177
			ToolItem algorithmButton = new ToolItem(toolBar, SWT.PUSH);
178
			algorithmButton.setText(algorithmName);
179
180
			new ToolItem(toolBar, SWT.SEPARATOR);
181
182
			algorithmButton.addSelectionListener(new SelectionListener() {
183
				public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
184
					currentLayoutAlgorithm = algorithm;
185
					algorithm.setEntityAspectRatio((double) mainComposite.getClientArea().width / (double) mainComposite.getClientArea().height);
186
					animate = algorithmAnimate;
187
					performLayout(false);
188
				}
189
190
				public void widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent e) {
191
				}
192
			});
193
		}
194
195
		ToolItem redrawButton = new ToolItem(toolBar, SWT.PUSH);
196
		redrawButton.setText("Redraw");
197
		redrawButton.addSelectionListener(new SelectionListener() {
198
			public void widgetSelected(SelectionEvent e) {
199
				mainComposite.redraw();
200
			}
201
202
			public void widgetDefaultSelected(SelectionEvent e) {
203
			}
204
		});
205
206
		ToolItem stopButton = new ToolItem(toolBar, SWT.PUSH);
207
		stopButton.setText("Stop");
208
		stopButton.addSelectionListener(new SelectionListener() {
209
			public void widgetSelected(SelectionEvent e) {
210
				currentLayoutAlgorithm.stop();
211
			}
212
213
			public void widgetDefaultSelected(SelectionEvent e) {
214
			}
215
		});
216
217
		ToolItem continuousButton = new ToolItem(toolBar, SWT.CHECK);
218
		continuousButton.setText("Continuous");
219
		continuousButton.addSelectionListener(new SelectionListener() {
220
			public void widgetSelected(SelectionEvent e) {
221
				setContinuous();
222
			}
223
224
			public void widgetDefaultSelected(SelectionEvent e) {
225
			}
226
		});
227
228
		ToolItem asynchronousButton = new ToolItem(toolBar, SWT.CHECK);
229
		asynchronousButton.setText("Asynchronous");
230
		asynchronousButton.addSelectionListener(new SelectionListener() {
231
			public void widgetSelected(SelectionEvent e) {
232
				setAsynchronously();
233
			}
234
235
			public void widgetDefaultSelected(SelectionEvent e) {
236
			}
237
		});
238
239
		createMainPanel();
240
		SimpleNode.setNodeColors(NODE_NORMAL_COLOR, BORDER_NORMAL_COLOR, NODE_SELECTED_COLOR, NODE_ADJACENT_COLOR, BORDER_SELECTED_COLOR, BORDER_ADJACENT_COLOR);
241
		SimpleRelationship.setDefaultColor(RELATIONSHIP_COLOR);
242
		SimpleRelationship.setDefaultHighlightColor(RELATIONSHIP_HIGHLIGHT_COLOR);
243
		createTreeGraph(4, 3, false);
244
		mainShell.pack();
245
		//mainShell.setSize(INITIAL_PANEL_WIDTH + 100, INITIAL_PANEL_HEIGHT + 200);
246
	}
247
248
	public void setAsynchronously() {
249
		if (asynchronously) {
250
			asynchronously = false;
251
		} else {
252
			asynchronously = true;
253
		}
254
255
	}
256
257
	public void setContinuous() {
258
		if (continuous) {
259
			continuous = false;
260
		} else {
261
			continuous = true;
262
		}
263
	}
264
265
	IProgressMonitor progressMonitor = null;
266
	ProgressMonitorDialog pmd = null;
267
	boolean GUI_UPDATING = false;
268
269
	private void performLayout(boolean placeRandomly) {
270
271
		if (!continuous) {
272
		}
273
274
		if (currentLayoutAlgorithm.isRunning()) {
275
			throw new RuntimeException("Layout is already running");
276
		}
277
		if (placeRandomly) {
278
			placeRandomly();
279
		}
280
		ProgressListener progressListener = new ProgressListener() {
281
282
			int lastStep = 0;
283
284
			class progressSync implements Runnable {
285
286
				public static final int PROGRESS_UPDATED = 1;
287
				public static final int PROGRESS_STARTED = 2;
288
				public static final int PROGRESS_ENDED = 3;
289
				public static final int UPDATE_GUI = 4;
290
291
				private int progressState = -1;
292
				private ProgressEvent e;
293
294
				public progressSync(int progressState, final ProgressEvent e) {
295
					this.progressState = progressState;
296
					this.e = e;
297
298
				}
299
300
				public void run() {
301
302
					switch (progressState) {
303
					case PROGRESS_STARTED:
304
						if (!continuous) {
305
							pmd = new ProgressMonitorDialog(getShell());
306
							progressMonitor = pmd.getProgressMonitor();
307
							pmd.open();
308
							progressMonitor.beginTask("Layout Running...", e.getTotalNumberOfSteps());
309
						}
310
						break;
311
312
					case PROGRESS_UPDATED:
313
						if (!continuous) {
314
							progressMonitor.worked(e.getStepsCompleted() - lastStep);
315
							lastStep = e.getStepsCompleted();
316
						}
317
						break;
318
319
					case PROGRESS_ENDED:
320
						if (!continuous) {
321
							progressMonitor.done();
322
							pmd.close();
323
						}
324
						updateGUI();
325
						mainShell.redraw();
326
						break;
327
					case UPDATE_GUI:
328
						updateGUI();
329
						GUI_UPDATING = false;
330
						break;
331
					}
332
					mainComposite.redraw();
333
334
				}
335
336
			}
337
338
			public void progressUpdated(final ProgressEvent e) {
339
				if (asynchronously) {
340
					if (!mainComposite.isDisposed()) {
341
						Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_UPDATED, e));
342
						if (!GUI_UPDATING) {
343
							GUI_UPDATING = true;
344
							Display.getDefault().asyncExec(new progressSync(progressSync.UPDATE_GUI, e));
345
						}
346
					}
347
				} else {
348
					if (!mainComposite.isDisposed()) {
349
						new progressSync(progressSync.PROGRESS_UPDATED, e).run();
350
					}
351
				}
352
353
			}
354
355
			public void progressStarted(ProgressEvent e) {
356
				if (asynchronously) {
357
					if (!mainComposite.isDisposed()) {
358
						Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_STARTED, e));
359
					}
360
				} else {
361
					if (!mainComposite.isDisposed()) {
362
						new progressSync(progressSync.PROGRESS_STARTED, e).run();
363
					}
364
				}
365
366
			}
367
368
			public void progressEnded(ProgressEvent e) {
369
				if (asynchronously) {
370
					if (!mainComposite.isDisposed()) {
371
						Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_ENDED, e));
372
					}
373
				} else {
374
					if (!mainComposite.isDisposed()) {
375
						new progressSync(progressSync.PROGRESS_ENDED, e).run();
376
					}
377
				}
378
				currentLayoutAlgorithm.removeProgressListener(this);
379
				Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_UPDATED, e));
380
			}
381
		};
382
		currentLayoutAlgorithm.addProgressListener(progressListener);
383
384
		try {
385
			LayoutEntity[] layoutEntities = new LayoutEntity[entities.size()];
386
			entities.toArray(layoutEntities);
387
			LayoutRelationship[] layoutRelationships = new LayoutRelationship[relationships.size()];
388
			relationships.toArray(layoutRelationships);
389
			currentLayoutAlgorithm.applyLayout(layoutEntities, layoutRelationships, 0, 0, mainComposite.getClientArea().width - 30, mainComposite.getClientArea().height - 17, asynchronously, continuous);
390
			if (!animate) {
391
				updateGUI();
392
			}
393
		} catch (InvalidLayoutConfiguration e) {
394
			e.printStackTrace();
395
		}
396
	}
397
398
	private Shell getShell() {
399
		return mainShell;
400
	}
401
402
	private void createMainPanel() {
403
		mainComposite = new Canvas(mainShell, SWT.NO_BACKGROUND);
404
		GridData mainGridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL, GridData.VERTICAL_ALIGN_FILL, true, true);
405
		mainGridData.widthHint = INITIAL_PANEL_WIDTH;
406
		mainGridData.heightHint = INITIAL_PANEL_HEIGHT;
407
		mainComposite.setLayoutData(mainGridData);
408
		mainComposite.addPaintListener(new GraphPaintListener());
409
410
		mainComposite.setBackground(new Color(Display.getCurrent(), 255, 255, 255));
411
		mainComposite.setLayout(null);
412
413
		mainComposite.addMouseMoveListener(new MouseMoveListener() {
414
415
			public void mouseMove(MouseEvent e) {
416
417
				if (selectedEntity == null) {
418
					// Nothing selected, lets use a mouse hover
419
					SimpleNode oldEntity = hoverEntity;
420
					hoverEntity = null;
421
422
					for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) {
423
						SimpleNode entity = (SimpleNode) iter.next();
424
						double x = entity.getX();
425
						double y = entity.getY();
426
						double w = entity.getWidth();
427
						double h = entity.getHeight();
428
						Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h);
429
						if (rect.contains(e.x, e.y)) {
430
							hoverEntity = entity;
431
							hoverEntity.ignoreInLayout(true);
432
							hoverEntity.setSelected();
433
							break;
434
						}
435
					}
436
					if (oldEntity != null && oldEntity != hoverEntity) {
437
						oldEntity.ignoreInLayout(false);
438
						oldEntity.setUnSelected();
439
					}
440
				}
441
442
			}
443
444
		});
445
		mainComposite.addMouseListener(new MouseListener() {
446
447
			public void mouseDoubleClick(MouseEvent e) {
448
			}
449
450
			public void mouseDown(MouseEvent e) {
451
				selectedEntity = null;
452
				hoverEntity = null;
453
				for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) {
454
					SimpleNode entity = (SimpleNode) iter.next();
455
					double x = entity.getX();
456
					double y = entity.getY();
457
					double w = entity.getWidth();
458
					double h = entity.getHeight();
459
					Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h);
460
					if (rect.contains(e.x, e.y)) {
461
						selectedEntity = entity;
462
					}
463
				}
464
				if (selectedEntity != null) {
465
					mouseDownPoint = new Point(e.x, e.y);
466
					selectedEntityPositionAtMouseDown = new Point((int) selectedEntity.getX(), (int) selectedEntity.getY());
467
					selectedEntity.ignoreInLayout(true);
468
					selectedEntity.setSelected();
469
				} else {
470
					mouseDownPoint = null;
471
					selectedEntityPositionAtMouseDown = null;
472
				}
473
			}
474
475
			public void mouseUp(MouseEvent e) {
476
				if (selectedEntity != null) {
477
					selectedEntity.ignoreInLayout(false);
478
					selectedEntity.setUnSelected();
479
					List relatedNodes = selectedEntity.getRelatedEntities();
480
					for (Iterator iter = relatedNodes.iterator(); iter.hasNext();) {
481
						SimpleNode element = (SimpleNode) iter.next();
482
						element.setUnSelected();
483
					}
484
					SimpleRelationship[] rels = selectedEntity.getRelationships();
485
					for (int i = 0; i < rels.length; i++) {
486
						rels[i].resetLineWidth();
487
					}
488
				}
489
				selectedEntity = null;
490
				mouseDownPoint = null;
491
				selectedEntityPositionAtMouseDown = null;
492
			}
493
		});
494
495
		// stops the algorithm when the window is closed
496
		mainComposite.addDisposeListener(new DisposeListener() {
497
			public void widgetDisposed(DisposeEvent e) {
498
				if (currentLayoutAlgorithm != null) {
499
					currentLayoutAlgorithm.stop();
500
				}
501
			}
502
		});
503
504
		mainComposite.addMouseMoveListener(new MouseMoveListener() {
505
506
			public void mouseMove(MouseEvent e) {
507
				if (selectedEntity != null && mouseDownPoint != null) {
508
					double dx = e.x - mouseDownPoint.x;
509
					double dy = e.y - mouseDownPoint.y;
510
511
					selectedEntity.setLocation(selectedEntityPositionAtMouseDown.x + dx, selectedEntityPositionAtMouseDown.y + dy);
512
					mainComposite.redraw();
513
				}
514
			}
515
		});
516
	}
517
518
	static int lastUpdateCall = 0;
519
520
	/**
521
	 * 
522
	 * @param maxLevels Max number of levels wanted in tree	
523
	 * @param maxChildren Max number of children for each node in the tree
524
	 * @param random Whether or not to pick random number of levels (from 1 to maxLevels) and 
525
	 * random number of children (from 1 to maxChildren)
526
	 */
527
	private void createTreeGraph(int maxLevels, int maxChildren, boolean random) {
528
		entities = new ArrayList();
529
		relationships = new ArrayList();
530
531
		// ccallendar - testing out having 2 roots 
532
		SimpleNode root = createSimpleNode(getNextID());
533
		entities.add(root);
534
		SimpleNode root2 = createSimpleNode(getNextID());
535
		entities.add(root2);
536
		// end
537
538
		SimpleNode currentParent = createSimpleNode(getNextID());
539
		entities.add(currentParent);
540
541
		// ccallendar - adding relationships from the parent to the 2 roots
542
		SimpleRelationship rel = new SimpleRelationship(root, currentParent, false);
543
		root.addRelationship(rel);
544
		currentParent.addRelationship(rel);
545
		relationships.add(rel);
546
		rel = new SimpleRelationship(root2, currentParent, false);
547
		root2.addRelationship(rel);
548
		currentParent.addRelationship(rel);
549
		relationships.add(rel);
550
		// end 
551
552
		int levels = random ? (int) (Math.random() * maxLevels + 1) : maxLevels;
553
		createTreeGraphRecursive(currentParent, maxChildren, levels, 1, random);
554
	}
555
556
	private void createTreeGraphRecursive(SimpleNode currentParentNode, int maxChildren, int maxLevel, int level, boolean random) {
557
		if (level > maxLevel) {
558
			return;
559
		}
560
561
		int numChildren = random ? (int) (Math.random() * maxChildren + 1) : maxChildren;
562
		for (int child = 0; child < numChildren; child++) {
563
			SimpleNode childNode = createSimpleNode(getNextID());
564
			entities.add(childNode);
565
			SimpleRelationship rel = new SimpleRelationship(currentParentNode, childNode, false);
566
			childNode.addRelationship(rel);
567
			currentParentNode.addRelationship(rel);
568
			relationships.add(rel);
569
			SimpleRelationship.setDefaultSize(2);
570
			createTreeGraphRecursive(childNode, maxChildren, maxLevel, level + 1, random);
571
		}
572
	}
573
574
	private int repeats = 0;
575
576
	/**
577
	 * Gets the next name from the names list.
578
	 * Once all the names have been used up the names are
579
	 * repeated with a '1' after the name.
580
	 * @return String name
581
	 */
582
	private String getNextID() {
583
		if (idCount >= NAMES.length) {
584
			idCount = 0;
585
			repeats++;
586
		}
587
		String id = NAMES[(int) idCount];
588
		if (repeats > 0) {
589
			id += "_" + repeats;
590
		}
591
		idCount++;
592
		return id;
593
	}
594
595
	/** Places nodes randomly on the screen **/
596
	private void placeRandomly() {
597
		for (Iterator iter = entities.iterator(); iter.hasNext();) {
598
			SimpleNode simpleNode = (SimpleNode) iter.next();
599
			double x = Math.random() * INITIAL_PANEL_WIDTH - INITIAL_NODE_WIDTH;
600
			double y = Math.random() * INITIAL_PANEL_HEIGHT - INITIAL_NODE_HEIGHT;
601
			simpleNode.setLocationInLayout(x, y);
602
		}
603
	}
604
605
	/**
606
	 * Creates a SimpleNode
607
	 * @param name
608
	 * @return SimpleNode
609
	 */
610
	private SimpleNode createSimpleNode(String name) {
611
		SimpleNode simpleNode = new SimpleNode(name);
612
		int w = name.length() * 8; // an initial approximation of the width
613
		simpleNode.setSizeInLayout(Math.max(w, INITIAL_NODE_WIDTH), INITIAL_NODE_HEIGHT);
614
		return simpleNode;
615
	}
616
617
	private void updateGUI() {
618
		if (!mainComposite.isDisposed()) {
619
			mainComposite.redraw();
620
			//mainComposite.update();
621
		}
622
	}
623
624
	static Display display = null;
625
626
	public static void main(String[] args) {
627
		display = Display.getDefault();
628
		SimpleSWTExample simpleSWTExample = new SimpleSWTExample(display);
629
		Shell shell = simpleSWTExample.getShell();
630
		//shell.pack();
631
		shell.open();
632
		while (!shell.isDisposed()) {
633
			if (!display.readAndDispatch()) {
634
				display.sleep();
635
			}
636
		}
637
		display.dispose();
638
	}
639
640
	/**
641
	 * Implements a paint listener to display nodes and edges  
642
	 */
643
	private class GraphPaintListener implements PaintListener {
644
645
		long lastPaint;
646
647
		public void paintControl(PaintEvent e) {
648
			Date date = new Date();
649
			long currentTime = date.getTime();
650
			if (currentTime - lastPaint < 40) {
651
				return;
652
			} else {
653
				lastPaint = currentTime;
654
			}
655
			if (Display.getDefault() == null || e.width == 0 || e.height == 0) {
656
				return;
657
			}
658
			long startTime = date.getTime();
659
660
			// do a bit of our own double-buffering to stop flickering
661
			Image imageBuffer;
662
663
			try {
664
				imageBuffer = new Image(Display.getDefault(), e.width, e.height);
665
			} catch (SWTError noMoreHandles) {
666
				imageBuffer = null;
667
				noMoreHandles.printStackTrace();
668
				return;
669
			} catch (IllegalArgumentException tooBig) {
670
				imageBuffer = null;
671
				tooBig.printStackTrace();
672
				return;
673
			}
674
675
			GC gcBuffer = new GC(imageBuffer);
676
677
			// paint the relationships 
678
			for (Iterator iter = relationships.iterator(); iter.hasNext();) {
679
				SimpleRelationship rel = (SimpleRelationship) iter.next();
680
				SimpleNode src = (SimpleNode) rel.getSourceInLayout();
681
				SimpleNode dest = (SimpleNode) rel.getDestinationInLayout();
682
683
				// highlight the adjacent nodes if one of the nodes is selected
684
				if (src.equals(selectedEntity)) {
685
					dest.setAdjacent();
686
					rel.setSelected();
687
				} else if (dest.equals(selectedEntity)) {
688
					src.setAdjacent();
689
					rel.setSelected();
690
				} else {
691
					rel.setUnSelected();
692
				}
693
694
				// Add bend points if required
695
				if ((rel).getBendPoints() != null && (rel).getBendPoints().length > 0) {
696
					src = drawBendPoints(rel, gcBuffer); // change source to last bendpoint
697
				}
698
699
				double srcX = src.getX() + src.getWidth() / 2.0;
700
				double srcY = src.getY() + src.getHeight() / 2.0;
701
				double destX = dest.getX() + dest.getWidth() / 2.0;
702
				double destY = dest.getY() + dest.getHeight() / 2.0;
703
				drawEdge(srcX, srcY, destX, destY, rel, gcBuffer);
704
705
			}
706
707
			// paint the nodes
708
			for (Iterator iter = entities.iterator(); iter.hasNext();) {
709
				SimpleNode entity = (SimpleNode) iter.next();
710
711
				String name = entity.toString();
712
				Point textSize = gcBuffer.stringExtent(name);
713
				int entityX = (int) entity.getX();
714
				int entityY = (int) entity.getY();
715
				//TODO: What about resize from the layout algorithm
716
				int entityWidth = Math.max((int) entity.getWidth(), textSize.x + 8);
717
				int entityHeight = Math.max((int) entity.getHeight(), textSize.y + 2);
718
719
				gcBuffer.setBackground((Color) entity.getColor());
720
				gcBuffer.fillRoundRectangle(entityX, entityY, entityWidth, entityHeight, 8, 8);
721
722
				// position the text in the middle of the node
723
				int x = (int) (entityX + (entityWidth / 2.0)) - (textSize.x / 2);
724
				gcBuffer.setForeground(BLACK);
725
				gcBuffer.drawString(name, x, entityY);
726
				gcBuffer.setForeground((Color) entity.getBorderColor());
727
				gcBuffer.setLineWidth(entity.getBorderWidth());
728
				gcBuffer.drawRoundRectangle(entityX, entityY, entityWidth, entityHeight, 8, 8);
729
			}
730
731
			e.gc.drawImage(imageBuffer, 0, 0);
732
			imageBuffer.dispose();
733
			gcBuffer.dispose();
734
735
			long time = date.getTime() - startTime;
736
			if (time > 200) {
737
			}
738
		}
739
740
		/**
741
		 * Draw an edge
742
		 * @param gcBuffer
743
		 * @param srcX
744
		 * @param srcY
745
		 * @param destX
746
		 * @param destY
747
		 * @param rel
748
		 */
749
		private void drawEdge(double srcX, double srcY, double destX, double destY, SimpleRelationship rel, GC gcBuffer) {
750
			gcBuffer.setForeground((Color) rel.getColor());
751
			gcBuffer.setLineWidth(rel.getLineWidth());
752
			gcBuffer.drawLine((int) srcX, (int) srcY, (int) destX, (int) destY);
753
		}
754
755
		/**
756
		 * Draws a set of lines between bendpoints
757
		 * TODO - This does not always draw outside the node.
758
		 * @param relationship
759
		 * @param bendNodes
760
		 * @param bendEdges
761
		 * @return the last bendpoint entity or null if there are no bendpoints
762
		 */
763
		private SimpleNode drawBendPoints(SimpleRelationship rel, GC gcBuffer) {
764
			final String DUMMY_TITLE = "dummy";
765
			LayoutBendPoint[] bendPoints = (rel).getBendPoints();
766
			LayoutBendPoint bp;
767
			SimpleNode startEntity = (SimpleNode) rel.getSourceInLayout();
768
			SimpleNode destEntity = null;
769
770
			double srcX = startEntity.getX() + startEntity.getWidth() / 2.0;
771
			double srcY = startEntity.getY() + startEntity.getHeight() / 2.0;
772
			for (int i = 1; i < bendPoints.length - 1; i++) {
773
				bp = bendPoints[i];
774
				destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01);
775
				drawEdge(srcX, srcY, bp.getX(), bp.getY(), rel, gcBuffer);
776
				startEntity = destEntity;
777
				srcX = startEntity.getX();
778
				srcY = startEntity.getY();
779
			}
780
			return destEntity;
781
		}
782
783
	}
784
785
}
(-)src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java (-702 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.exampleUses;
12
13
import java.awt.BasicStroke;
14
import java.awt.BorderLayout;
15
import java.awt.Color;
16
import java.awt.Cursor;
17
import java.awt.Dimension;
18
import java.awt.Graphics;
19
import java.awt.Graphics2D;
20
import java.awt.Point;
21
import java.awt.Polygon;
22
import java.awt.RenderingHints;
23
import java.awt.Shape;
24
import java.awt.Stroke;
25
import java.awt.Toolkit;
26
import java.awt.event.ActionEvent;
27
import java.awt.event.ActionListener;
28
import java.awt.event.MouseAdapter;
29
import java.awt.event.MouseEvent;
30
import java.awt.event.MouseMotionListener;
31
import java.awt.event.WindowAdapter;
32
import java.awt.event.WindowEvent;
33
import java.awt.geom.AffineTransform;
34
import java.awt.geom.GeneralPath;
35
import java.awt.geom.Point2D;
36
import java.awt.geom.Rectangle2D;
37
import java.lang.reflect.InvocationTargetException;
38
import java.util.ArrayList;
39
import java.util.Comparator;
40
import java.util.Iterator;
41
import java.util.List;
42
43
import javax.swing.JButton;
44
import javax.swing.JFrame;
45
import javax.swing.JLabel;
46
import javax.swing.JPanel;
47
import javax.swing.JScrollPane;
48
import javax.swing.JToggleButton;
49
import javax.swing.JToolBar;
50
import javax.swing.SwingUtilities;
51
52
import org.eclipse.zest.layouts.InvalidLayoutConfiguration;
53
import org.eclipse.zest.layouts.LayoutAlgorithm;
54
import org.eclipse.zest.layouts.LayoutBendPoint;
55
import org.eclipse.zest.layouts.LayoutEntity;
56
import org.eclipse.zest.layouts.LayoutRelationship;
57
import org.eclipse.zest.layouts.LayoutStyles;
58
import org.eclipse.zest.layouts.algorithms.GridLayoutAlgorithm;
59
import org.eclipse.zest.layouts.algorithms.HorizontalLayoutAlgorithm;
60
import org.eclipse.zest.layouts.algorithms.HorizontalTreeLayoutAlgorithm;
61
import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm;
62
import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm;
63
import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
64
import org.eclipse.zest.layouts.algorithms.VerticalLayoutAlgorithm;
65
import org.eclipse.zest.layouts.exampleStructures.SimpleNode;
66
import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship;
67
import org.eclipse.zest.layouts.progress.ProgressEvent;
68
import org.eclipse.zest.layouts.progress.ProgressListener;
69
70
/**
71
 * @author Rob Lintern
72
 * @author Chris Bennett
73
 *
74
 * A simple example of using layout algorithms with a Swing application.
75
 */
76
public class SimpleSwingExample {
77
	private static final Color NODE_NORMAL_COLOR = new Color(225, 225, 255);
78
	private static final Color NODE_SELECTED_COLOR = new Color(255, 125, 125);
79
	//private static final Color NODE_ADJACENT_COLOR = new Color (255, 200, 125); 
80
	private static final Color BORDER_NORMAL_COLOR = new Color(0, 0, 0);
81
	private static final Color BORDER_SELECTED_COLOR = new Color(255, 0, 0);
82
	//private static final Color BORDER_ADJACENT_COLOR = new Color (255, 128, 0);   
83
	private static final Stroke BORDER_NORMAL_STROKE = new BasicStroke(1.0f);
84
	private static final Stroke BORDER_SELECTED_STROKE = new BasicStroke(2.0f);
85
	private static final Color RELATIONSHIP_NORMAL_COLOR = Color.BLUE;
86
	//private static final Color RELATIONSHIP_HIGHLIGHT_COLOR = new Color (255, 200, 125); 
87
88
	public static SpringLayoutAlgorithm SPRING = new SpringLayoutAlgorithm(LayoutStyles.NONE);
89
	public static TreeLayoutAlgorithm TREE_VERT = new TreeLayoutAlgorithm(LayoutStyles.NONE);
90
	public static HorizontalTreeLayoutAlgorithm TREE_HORIZ = new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE);
91
	public static RadialLayoutAlgorithm RADIAL = new RadialLayoutAlgorithm(LayoutStyles.NONE);
92
	public static GridLayoutAlgorithm GRID = new GridLayoutAlgorithm(LayoutStyles.NONE);
93
	public static HorizontalLayoutAlgorithm HORIZ = new HorizontalLayoutAlgorithm(LayoutStyles.NONE);
94
	public static VerticalLayoutAlgorithm VERT = new VerticalLayoutAlgorithm(LayoutStyles.NONE);
95
96
	private List algorithms = new ArrayList();
97
	private List algorithmNames = new ArrayList();
98
99
	private static final int INITIAL_PANEL_WIDTH = 700;
100
	private static final int INITIAL_PANEL_HEIGHT = 500;
101
102
	private static final boolean RENDER_HIGH_QUALITY = true;
103
104
	private static final double INITIAL_NODE_WIDTH = 20;
105
	private static final double INITIAL_NODE_HEIGHT = 20;
106
	private static final int ARROW_HALF_WIDTH = 4;
107
	private static final int ARROW_HALF_HEIGHT = 6;
108
	private static final Shape ARROW_SHAPE = new Polygon(new int[] { -ARROW_HALF_HEIGHT, ARROW_HALF_HEIGHT, -ARROW_HALF_HEIGHT }, new int[] { -ARROW_HALF_WIDTH, 0, ARROW_HALF_WIDTH }, 3);
109
	private static final Stroke ARROW_BORDER_STROKE = new BasicStroke(0.5f);
110
	private static final Color ARROW_HEAD_FILL_COLOR = new Color(125, 255, 125);
111
	private static final Color ARROW_HEAD_BORDER_COLOR = Color.BLACK;
112
113
	public static final String DEFAULT_NODE_SHAPE = "oval";
114
115
	private long updateGUICount = 0;
116
117
	private JFrame mainFrame;
118
	private JPanel mainPanel;
119
	private List entities;
120
	private List relationships;
121
	private JToolBar toolBar;
122
	private JLabel lblProgress;
123
	private JToggleButton btnContinuous;
124
	private JToggleButton btnAsynchronous;
125
	private JButton btnStop;
126
127
	private LayoutAlgorithm currentLayoutAlgorithm;
128
	protected String currentLayoutAlgorithmName;
129
	protected SimpleNode selectedEntity;
130
	protected Point mouseDownPoint;
131
	protected Point selectedEntityPositionAtMouseDown;
132
	private long idCount;
133
	protected String currentNodeShape = DEFAULT_NODE_SHAPE; // e.g., oval, rectangle
134
135
	public SimpleSwingExample() {
136
137
	}
138
139
	protected void addAlgorithm(LayoutAlgorithm algorithm, String name, boolean animate) {
140
		algorithms.add(algorithm);
141
		algorithmNames.add(name);
142
	}
143
144
	public void start() {
145
146
		mainFrame = new JFrame("Simple Swing Layout Example");
147
		toolBar = new JToolBar();
148
		mainFrame.getContentPane().setLayout(new BorderLayout());
149
		mainFrame.getContentPane().add(toolBar, BorderLayout.NORTH);
150
		lblProgress = new JLabel("Progress: ");
151
		mainFrame.getContentPane().add(lblProgress, BorderLayout.SOUTH);
152
153
		createMainPanel();
154
		mainFrame.addWindowListener(new WindowAdapter() {
155
			public void windowClosing(WindowEvent e) {
156
				stop();
157
				mainFrame.dispose();
158
159
			}
160
		});
161
162
		btnContinuous = new JToggleButton("continuous", false);
163
		btnAsynchronous = new JToggleButton("asynchronous", false);
164
165
		toolBar.add(btnContinuous);
166
		toolBar.add(btnAsynchronous);
167
168
		btnStop = new JButton("Stop");
169
		btnStop.addActionListener(new ActionListener() {
170
			public void actionPerformed(ActionEvent e) {
171
				stop();
172
			}
173
		});
174
		toolBar.add(btnStop);
175
176
		JButton btnCreateGraph = new JButton("New graph");
177
		btnCreateGraph.addActionListener(new ActionListener() {
178
			public void actionPerformed(ActionEvent e) {
179
				stop();
180
				createGraph(true);
181
			}
182
		});
183
		toolBar.add(btnCreateGraph);
184
		JButton btnCreateTree = new JButton("New tree");
185
		btnCreateTree.addActionListener(new ActionListener() {
186
			public void actionPerformed(ActionEvent e) {
187
				stop();
188
				createGraph(false);
189
			}
190
		});
191
		toolBar.add(btnCreateTree);
192
193
		createGraph(false);
194
195
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
196
		mainFrame.setLocation((int) (screenSize.getWidth() - INITIAL_PANEL_WIDTH) / 2, (int) (screenSize.getHeight() - INITIAL_PANEL_HEIGHT) / 2);
197
		mainFrame.pack();
198
		mainFrame.setVisible(true);
199
		mainFrame.repaint();
200
201
		try {
202
			SwingUtilities.invokeAndWait(new Runnable() {
203
204
				public void run() {
205
					SPRING = new SpringLayoutAlgorithm(LayoutStyles.NONE);
206
					TREE_VERT = new TreeLayoutAlgorithm(LayoutStyles.NONE);
207
					TREE_HORIZ = new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE);
208
					RADIAL = new RadialLayoutAlgorithm(LayoutStyles.NONE);
209
					GRID = new GridLayoutAlgorithm(LayoutStyles.NONE);
210
					HORIZ = new HorizontalLayoutAlgorithm(LayoutStyles.NONE);
211
					VERT = new VerticalLayoutAlgorithm(LayoutStyles.NONE);
212
213
					SPRING.setIterations(1000);
214
					// initialize layouts
215
					TREE_VERT.setComparator(new Comparator() {
216
						public int compare(Object o1, Object o2) {
217
							if (o1 instanceof Comparable && o2 instanceof Comparable) {
218
								return ((Comparable) o1).compareTo(o2);
219
							}
220
							return 0;
221
						}
222
223
					});
224
					GRID.setRowPadding(20);
225
					addAlgorithm(SPRING, "Spring", false);
226
					addAlgorithm(TREE_VERT, "Tree-V", false);
227
					addAlgorithm(TREE_HORIZ, "Tree-H", false);
228
					addAlgorithm(RADIAL, "Radial", false);
229
					addAlgorithm(GRID, "Grid", false);
230
					addAlgorithm(HORIZ, "Horiz", false);
231
					addAlgorithm(VERT, "Vert", false);
232
233
					for (int i = 0; i < algorithms.size(); i++) {
234
						final LayoutAlgorithm algorithm = (LayoutAlgorithm) algorithms.get(i);
235
						final String algorithmName = (String) algorithmNames.get(i);
236
						//final boolean algorithmAnimate = ((Boolean)algorithmAnimates.get(i)).booleanValue();
237
						JButton algorithmButton = new JButton(algorithmName);
238
						algorithmButton.addActionListener(new ActionListener() {
239
							public void actionPerformed(ActionEvent e) {
240
								currentLayoutAlgorithm = algorithm;
241
								currentLayoutAlgorithmName = algorithmName;
242
								algorithm.setEntityAspectRatio((double) mainPanel.getWidth() / (double) mainPanel.getHeight());
243
								//animate = algorithmAnimate;
244
								performLayout();
245
							}
246
						});
247
						toolBar.add(algorithmButton);
248
					}
249
				}
250
			});
251
		} catch (InterruptedException e1) {
252
			// TODO Auto-generated catch block
253
			e1.printStackTrace();
254
		} catch (InvocationTargetException e1) {
255
			// TODO Auto-generated catch block
256
			e1.printStackTrace();
257
		}
258
259
	}
260
261
	private void stop() {
262
		if (currentLayoutAlgorithm != null && currentLayoutAlgorithm.isRunning()) {
263
			currentLayoutAlgorithm.stop();
264
		}
265
	}
266
267
	protected void performLayout() {
268
		stop();
269
		final Cursor cursor = mainFrame.getCursor();
270
		updateGUICount = 0;
271
		placeRandomly();
272
		final boolean continuous = btnContinuous.isSelected();
273
		final boolean asynchronous = btnAsynchronous.isSelected();
274
		ProgressListener progressListener = new ProgressListener() {
275
			public void progressUpdated(final ProgressEvent e) {
276
				//if (asynchronous) {
277
				updateGUI();
278
				//}
279
				lblProgress.setText("Progress: " + e.getStepsCompleted() + " of " + e.getTotalNumberOfSteps() + " completed ...");
280
				lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight());
281
			}
282
283
			public void progressStarted(ProgressEvent e) {
284
				if (!asynchronous) {
285
					mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
286
				}
287
				lblProgress.setText("Layout started ...");
288
				lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight());
289
			}
290
291
			public void progressEnded(ProgressEvent e) {
292
				lblProgress.setText("Layout completed ...");
293
				lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight());
294
				currentLayoutAlgorithm.removeProgressListener(this);
295
				if (!asynchronous) {
296
					mainFrame.setCursor(cursor);
297
				}
298
			}
299
		};
300
301
		currentLayoutAlgorithm.addProgressListener(progressListener);
302
303
		try {
304
			final LayoutEntity[] layoutEntities = new LayoutEntity[entities.size()];
305
			entities.toArray(layoutEntities);
306
			final LayoutRelationship[] layoutRelationships = new LayoutRelationship[relationships.size()];
307
			relationships.toArray(layoutRelationships);
308
			SwingUtilities.invokeLater(new Runnable() {
309
				public void run() {
310
					try {
311
						currentLayoutAlgorithm.applyLayout(layoutEntities, layoutRelationships, 0, 0, mainPanel.getWidth(), mainPanel.getHeight(), asynchronous, continuous);
312
					} catch (InvalidLayoutConfiguration e) {
313
						// TODO Auto-generated catch block
314
						e.printStackTrace();
315
					}
316
317
				}
318
319
			});
320
			//if (!animate) {
321
			updateGUI();
322
			//}
323
			// reset
324
			currentNodeShape = DEFAULT_NODE_SHAPE;
325
		} catch (StackOverflowError e) {
326
			e.printStackTrace();
327
		} finally {
328
		}
329
	}
330
331
	private void createMainPanel() {
332
333
		mainPanel = new MainPanel(); // see below for class definition
334
		mainPanel.setPreferredSize(new Dimension(INITIAL_PANEL_WIDTH, INITIAL_PANEL_HEIGHT));
335
		mainPanel.setBackground(Color.WHITE);
336
		mainPanel.setLayout(null);
337
		mainFrame.getContentPane().add(new JScrollPane(mainPanel), BorderLayout.CENTER);
338
339
		mainPanel.addMouseListener(new MouseAdapter() {
340
			public void mousePressed(MouseEvent e) {
341
				selectedEntity = null;
342
				for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) {
343
					SimpleNode entity = (SimpleNode) iter.next();
344
					double x = entity.getX();
345
					double y = entity.getY();
346
					double w = entity.getWidth();
347
					double h = entity.getHeight();
348
					Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
349
					if (rect.contains(e.getX(), e.getY())) {
350
						selectedEntity = entity;
351
					}
352
				}
353
				if (selectedEntity != null) {
354
					mouseDownPoint = e.getPoint();
355
					selectedEntityPositionAtMouseDown = new Point((int) selectedEntity.getX(), (int) selectedEntity.getY());
356
				} else {
357
					mouseDownPoint = null;
358
					selectedEntityPositionAtMouseDown = null;
359
				}
360
				updateGUI();
361
			}
362
363
			public void mouseReleased(MouseEvent e) {
364
				selectedEntity = null;
365
				mouseDownPoint = null;
366
				selectedEntityPositionAtMouseDown = null;
367
				updateGUI();
368
			}
369
		});
370
371
		mainPanel.addMouseMotionListener(new MouseMotionListener() {
372
			public void mouseDragged(MouseEvent e) {
373
				//                if (selectedEntity != null) {
374
				//					//TODO: Add mouse moving
375
				//                    //selectedEntity.setLocationInLayout(selectedEntityPositionAtMouseDown.x + dx, selectedEntityPositionAtMouseDown.y + dy);
376
				//                    updateGUI();
377
				//                } 
378
			}
379
380
			public void mouseMoved(MouseEvent e) {
381
			}
382
		});
383
	}
384
385
	private void createGraph(boolean addNonTreeRels) {
386
		entities = new ArrayList();
387
		relationships = new ArrayList();
388
		selectedEntity = null;
389
390
		createTreeGraph(2, 4, 2, 5, true, true, addNonTreeRels);
391
		//        createCustomGraph();
392
393
		placeRandomly();
394
		mainPanel.repaint();
395
	}
396
397
	/**
398
	 * 
399
	 * @param maxLevels Max number of levels wanted in tree	
400
	 * @param maxChildren Max number of children for each node in the tree
401
	 * @param randomNumChildren Whether or not to pick random number of levels (from 1 to maxLevels) and 
402
	 * random number of children (from 1 to maxChildren)
403
	 */
404
	private void createTreeGraph(int minChildren, int maxChildren, int minLevels, int maxLevels, boolean randomNumChildren, boolean randomLevels, boolean addNonTreeRels) {
405
		LayoutEntity currentParent = createSimpleNode(getNextID());
406
		entities.add(currentParent);
407
		createTreeGraphRecursive(currentParent, minChildren, maxChildren, minLevels, maxLevels, 1, randomNumChildren, randomLevels, addNonTreeRels);
408
	}
409
410
	private void createTreeGraphRecursive(LayoutEntity currentParentNode, int minChildren, int maxChildren, int minLevel, int maxLevel, int level, boolean randomNumChildren, boolean randomLevels, boolean addNonTreeRels) {
411
		if (level > maxLevel) {
412
			return;
413
		}
414
		if (randomLevels) {
415
			if (level > minLevel) {
416
				double zeroToOne = Math.random();
417
				if (zeroToOne < 0.75) {
418
					return;
419
				}
420
			}
421
		}
422
		int numChildren = randomNumChildren ? Math.max(minChildren, (int) (Math.random() * maxChildren + 1)) : maxChildren;
423
		for (int i = 0; i < numChildren; i++) {
424
			LayoutEntity newNode = createSimpleNode(getNextID());
425
			entities.add(newNode);
426
			if (addNonTreeRels && entities.size() % 5 == 0) {
427
				int index = (int) (Math.random() * entities.size());
428
				LayoutRelationship rel = new SimpleRelationship((LayoutEntity) entities.get(index), newNode, false);
429
				relationships.add(rel);
430
			}
431
			LayoutRelationship rel = new SimpleRelationship(currentParentNode, newNode, false);
432
			relationships.add(rel);
433
			createTreeGraphRecursive(newNode, minChildren, maxChildren, minLevel, maxLevel, level + 1, randomNumChildren, randomLevels, addNonTreeRels);
434
		}
435
	}
436
437
	/**
438
	 * Call this from createGraph in place of createTreeGraph 
439
	 * this for debugging and testing.
440
	 */
441
	/*    private void createCustomGraph() {
442
	 LayoutEntity A = createSimpleNode("1");
443
	 LayoutEntity B = createSimpleNode("10");
444
	 LayoutEntity _1 = createSimpleNode("100");
445
	 entities.add(A);
446
	 entities.add(B);
447
	 entities.add(_1);
448
	 relationships.add(new SimpleRelationship (A, B, false));
449
	 relationships.add(new SimpleRelationship (A, _1, false));
450
	 relationships.add(new SimpleRelationship (_1, A, false));
451
	 }
452
	 */
453
	private String getNextID() {
454
		String id = "" + idCount;
455
		idCount++;
456
		return id;
457
	}
458
459
	/** Places nodes randomly on the screen **/
460
	private void placeRandomly() {
461
		for (Iterator iter = entities.iterator(); iter.hasNext();) {
462
			SimpleNode simpleNode = (SimpleNode) iter.next();
463
			double x = Math.random() * INITIAL_PANEL_WIDTH - INITIAL_NODE_WIDTH;
464
			double y = Math.random() * INITIAL_PANEL_HEIGHT - INITIAL_NODE_HEIGHT;
465
			simpleNode.setLocationInLayout(x, y);
466
			simpleNode.setSizeInLayout(INITIAL_NODE_WIDTH, INITIAL_NODE_HEIGHT);
467
		}
468
	}
469
470
	/**
471
	 * Creates a SimpleNode
472
	 * @param name
473
	 * @return
474
	 */
475
	private SimpleNode createSimpleNode(String name) {
476
		SimpleNode simpleNode = new SimpleNode(name);
477
		return simpleNode;
478
	}
479
480
	private void updateGUI() {
481
		updateGUICount++;
482
		if (updateGUICount > 0) {
483
			mainPanel.paintImmediately(0, 0, mainPanel.getWidth(), mainPanel.getHeight());
484
		}
485
	}
486
487
	private static Point2D.Double getEllipseIntersectionPoint(double theta, double ellipseWidth, double ellipseHeight) {
488
		double nhalfw = ellipseWidth / 2.0; // half elllipse width
489
		double nhalfh = ellipseHeight / 2.0; // half ellipse height
490
		double tanTheta = Math.tan(theta);
491
492
		double a = nhalfw;
493
		double b = nhalfh;
494
		double x = (a * b) / Math.sqrt(Math.pow(b, 2) + Math.pow(a, 2) * Math.pow(tanTheta, 2));
495
		if ((theta > Math.PI / 2.0 && theta < 1.5 * Math.PI) || (theta < -Math.PI / 2.0 && theta > -1.5 * Math.PI)) {
496
			x = -x;
497
		}
498
		double y = tanTheta * x;
499
		Point2D.Double p = new Point2D.Double(x, y);
500
		return p;
501
	}
502
503
	public static void main(String[] args) {
504
		(new SimpleSwingExample()).start();
505
	}
506
507
	/**
508
	 * A JPanel that provides entity and relationship rendering
509
	 * Instead of letting Swing paint all the JPanels for us, we will just do our own painting here
510
	 */
511
	private class MainPanel extends JPanel {
512
513
		private static final long serialVersionUID = 1;
514
515
		protected void paintChildren(Graphics g) {
516
			if (g instanceof Graphics2D && RENDER_HIGH_QUALITY) {
517
				((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
518
				((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
519
				((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
520
				((Graphics2D) g).setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
521
			}
522
523
			// paint the nodes
524
			for (Iterator iter = entities.iterator(); iter.hasNext();) {
525
				paintEntity((SimpleNode) iter.next(), g);
526
			}
527
528
			// paint the relationships 
529
			for (Iterator iter = relationships.iterator(); iter.hasNext();) {
530
				paintRelationship((LayoutRelationship) iter.next(), g);
531
			}
532
		}
533
534
		private void paintEntity(SimpleNode entity, Graphics g) {
535
			boolean isSelected = selectedEntity != null && selectedEntity.equals(entity);
536
			g.setColor(isSelected ? NODE_SELECTED_COLOR : NODE_NORMAL_COLOR);
537
			if (currentNodeShape.equals("rectangle")) {
538
				g.fillRect((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight());
539
			} else { // default 
540
				g.fillOval((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight());
541
			}
542
			g.setColor(isSelected ? BORDER_SELECTED_COLOR : BORDER_NORMAL_COLOR);
543
			String name = entity.toString();
544
			Rectangle2D nameBounds = g.getFontMetrics().getStringBounds(name, g);
545
			g.drawString(name, (int) (entity.getX() + entity.getWidth() / 2.0 - nameBounds.getWidth() / 2.0), (int) (entity.getY() + entity.getHeight() / 2.0 + nameBounds.getHeight() / 2.0));//- nameBounds.getHeight() - nameBounds.getY()));
546
			if (g instanceof Graphics2D) {
547
				((Graphics2D) g).setStroke(isSelected ? BORDER_SELECTED_STROKE : BORDER_NORMAL_STROKE);
548
			}
549
			if (currentNodeShape.equals("rectangle")) {
550
				g.drawRect((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight());
551
			} else { // default
552
				g.drawOval((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight());
553
			}
554
		}
555
556
		private void paintRelationship(LayoutRelationship rel, Graphics g) {
557
558
			SimpleNode src = (SimpleNode) rel.getSourceInLayout();
559
			SimpleNode dest = (SimpleNode) rel.getDestinationInLayout();
560
561
			// Add bend points if required
562
			if (((SimpleRelationship) rel).getBendPoints() != null && ((SimpleRelationship) rel).getBendPoints().length > 0) {
563
				drawBendPoints(rel, g);
564
			} else {
565
				double srcX = src.getX() + src.getWidth() / 2.0;
566
				double srcY = src.getY() + src.getHeight() / 2.0;
567
				double destX = dest.getX() + dest.getWidth() / 2.0;
568
				double destY = dest.getY() + dest.getHeight() / 2.0;
569
				double dx = getLength(srcX, destX);
570
				double dy = getLength(srcY, destY);
571
				double theta = Math.atan2(dy, dx);
572
				drawRelationship(src, dest, theta, srcX, srcY, destX, destY, g);
573
574
				// draw an arrow in the middle of the line
575
				drawArrow(theta, srcX, srcY, dx, dy, g);
576
			}
577
		}
578
579
		/**
580
		 * Draw a line from the edge of the src node to the edge of the destination node 
581
		 */
582
		private void drawRelationship(SimpleNode src, SimpleNode dest, double theta, double srcX, double srcY, double destX, double destY, Graphics g) {
583
584
			double reverseTheta = theta > 0.0d ? theta - Math.PI : theta + Math.PI;
585
586
			Point2D.Double srcIntersectionP = getEllipseIntersectionPoint(theta, src.getWidth(), src.getHeight());
587
588
			Point2D.Double destIntersectionP = getEllipseIntersectionPoint(reverseTheta, dest.getWidth(), dest.getHeight());
589
590
			drawRelationship(srcX + srcIntersectionP.getX(), srcY + srcIntersectionP.getY(), destX + destIntersectionP.getX(), destY + destIntersectionP.getY(), g);
591
		}
592
593
		/**
594
		 * Draw a line from specified source to specified destination
595
		 */
596
		private void drawRelationship(double srcX, double srcY, double destX, double destY, Graphics g) {
597
			g.setColor(RELATIONSHIP_NORMAL_COLOR);
598
			g.drawLine((int) srcX, (int) srcY, (int) destX, (int) destY);
599
		}
600
601
		private void drawArrow(double theta, double srcX, double srcY, double dx, double dy, Graphics g) {
602
			AffineTransform tx = new AffineTransform();
603
			double arrX = srcX + (dx) / 2.0;
604
			double arrY = srcY + (dy) / 2.0;
605
			tx.translate(arrX, arrY);
606
			tx.rotate(theta);
607
			Shape arrowTx = tx.createTransformedShape(ARROW_SHAPE);
608
			if (g instanceof Graphics2D) {
609
				g.setColor(ARROW_HEAD_FILL_COLOR);
610
				((Graphics2D) g).fill(arrowTx);
611
				((Graphics2D) g).setStroke(ARROW_BORDER_STROKE);
612
				g.setColor(ARROW_HEAD_BORDER_COLOR);
613
				((Graphics2D) g).draw(arrowTx);
614
			}
615
		}
616
617
		/**
618
		 * Get the length of a line ensuring it is not too small to render
619
		 * @param start
620
		 * @param end
621
		 * @return
622
		 */
623
		private double getLength(double start, double end) {
624
			double length = end - start;
625
			// make sure dx is not zero or too small
626
			if (length < 0.01 && length > -0.01) {
627
				if (length > 0) {
628
					length = 0.01;
629
				} else if (length < 0) {
630
					length = -0.01;
631
				}
632
			}
633
			return length;
634
		}
635
636
		/**
637
		 * Draw a line from specified source to specified destination
638
		 */
639
		private void drawCurvedRelationship(double srcX, double srcY, double control1X, double control1Y, double control2X, double control2Y, double destX, double destY, Graphics g) {
640
			GeneralPath shape = new GeneralPath();
641
			shape.moveTo((float) srcX, (float) srcY);
642
			shape.curveTo((float) control1X, (float) control1Y, (float) control2X, (float) control2Y, (float) destX, (float) destY);
643
			g.setColor(RELATIONSHIP_NORMAL_COLOR);
644
			((Graphics2D) g).draw(shape);
645
		}
646
647
		/**
648
		 * Draws a set of lines between bendpoints, returning the last bendpoint
649
		 * drawn. Note that this assumes the first and last bendpoints are actually
650
		 * the source node and destination node centre points. 
651
		 * @param relationship
652
		 * @param bendNodes
653
		 * @param bendEdges
654
		 * @return the last bendpoint entity or null if there are no bendpoints
655
		 */
656
		private void drawBendPoints(LayoutRelationship rel, Graphics g) {
657
			final String DUMMY_TITLE = "dummy";
658
			LayoutBendPoint bp;
659
660
			SimpleNode startEntity = (SimpleNode) rel.getSourceInLayout();
661
			SimpleNode destEntity = (SimpleNode) rel.getDestinationInLayout();
662
			double srcX = startEntity.getX();
663
			double srcY = startEntity.getY();
664
665
			// Transform the bendpoints to this coordinate system
666
			LayoutBendPoint[] bendPoints = ((SimpleRelationship) rel).getBendPoints();
667
668
			srcX = bendPoints[1].getX();
669
			srcY = bendPoints[1].getY();
670
			int bpNum = 2;
671
			while (bpNum < bendPoints.length - 1) { // ignore first and last bendpoints (src and dest)
672
				int currentBpNum = bpNum;
673
				bp = bendPoints[bpNum];
674
				if (bp.getIsControlPoint()) {
675
					if (bendPoints[bpNum + 1].getIsControlPoint()) {
676
						destEntity = new SimpleNode(DUMMY_TITLE, bendPoints[bpNum + 2].getX(), bendPoints[bpNum + 2].getY(), 0.01, 0.01);
677
						drawCurvedRelationship(srcX, srcY, bp.getX(), bp.getY(), bendPoints[bpNum + 1].getX(), bendPoints[bpNum + 1].getY(), bendPoints[bpNum + 2].getX(), bendPoints[bpNum + 2].getY(), g);
678
						bpNum += 4;
679
					} else {
680
						destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01);
681
					}
682
				} else {
683
					drawRelationship(srcX, srcY, bp.getX(), bp.getY(), g);
684
					bpNum++;
685
					destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01);
686
				}
687
				startEntity = destEntity;
688
				if (currentBpNum == bendPoints.length - 2) { // last point
689
					// draw an arrow in the middle of the line
690
					double dx = getLength(srcX, destEntity.getX());
691
					double dy = getLength(srcY, destEntity.getY());
692
					double theta = Math.atan2(dy, dx);
693
					drawArrow(theta, srcX, srcY, dx, dy, g);
694
				} else {
695
					srcX = startEntity.getX();
696
					srcY = startEntity.getY();
697
				}
698
			}
699
700
		}
701
	}
702
}
(-)src/org/eclipse/zest/layouts/interfaces/ConnectionLayout.java (+48 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
13
public interface ConnectionLayout {
14
15
	public NodeLayout getSource();
16
17
	public NodeLayout getTarget();
18
19
	/**
20
	 * 
21
	 * @return weight assigned to this connection
22
	 */
23
	public double getWeight();
24
25
	/**
26
	 * Checks if this connection is directed. For undirected connections, source
27
	 * and target nodes should be considered just adjacent nodes without
28
	 * dividing to source/target.
29
	 * 
30
	 * @return true if this connection is directed
31
	 */
32
	public boolean isDirected();
33
34
	/**
35
	 * Changes the visibility state of this connection.
36
	 * 
37
	 * @param visible
38
	 *            true if this connection should be visible, false otherwise
39
	 */
40
	public void setVisible(boolean visible);
41
42
	/**
43
	 * Checks the visibility state of this connection.
44
	 * 
45
	 * @return true if this connection is visible, false otherwise
46
	 */
47
	public boolean isVisible();
48
}
(-)src/org/eclipse/zest/layouts/interfaces/ContextListener.java (+74 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
import org.eclipse.zest.layouts.LayoutAlgorithm;
13
14
public interface ContextListener {
15
	public class Stub implements ContextListener {
16
17
		public boolean boundsChanged(LayoutContext context) {
18
			return false;
19
		}
20
21
		public void backgroundEnableChanged(LayoutContext context) {
22
			// do nothing
23
		}
24
25
		public boolean pruningEnablementChanged(LayoutContext context) {
26
			return false;
27
		}
28
29
	}
30
31
	/**
32
	 * This method is called whenever the bounds available in a layout context
33
	 * change.
34
	 * 
35
	 * If true is returned, it means that the receiving listener has intercepted
36
	 * this event. Intercepted events will not be passed to the rest of the
37
	 * listeners. If the event is not intercepted by any listener,
38
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
39
	 * context's main algorithm.
40
	 * 
41
	 * @param context
42
	 *            the layout context that fired the event
43
	 * @return true if no further operations after this event are required
44
	 */
45
	public boolean boundsChanged(LayoutContext context);
46
47
	/**
48
	 * This method is called whenever graph pruning is enabled or disabled in a
49
	 * layout context.
50
	 * 
51
	 * If true is returned, it means that the receiving listener has intercepted
52
	 * this event. Intercepted events will not be passed to the rest of the
53
	 * listeners. If the event is not intercepted by any listener,
54
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
55
	 * context's main algorithm.
56
	 * 
57
	 * @param context
58
	 *            the layout context that fired the event
59
	 * @return true if no further operations after this event are required
60
	 */
61
	public boolean pruningEnablementChanged(LayoutContext context);
62
63
	/**
64
	 * This method is called whenever background layout is enabled or disabled
65
	 * in a layout context. If the receiving listener is related to a layout
66
	 * algorithm that performs layout in reaction to events, it should turn
67
	 * automatic flush of changes on or off. Also, eventual additional threads
68
	 * responsible for layout should be stopped or started accordingly.
69
	 * 
70
	 * @param context
71
	 *            the layout context that fired the event
72
	 */
73
	public void backgroundEnableChanged(LayoutContext context);
74
}
(-)src/org/eclipse/zest/layouts/interfaces/EntityLayout.java (+106 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
13
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint;
14
15
/**
16
 * A common interface for entities that are displayed on a graph, that is
17
 * {@link NodeLayout nodes} and {@link SubgraphLayout subgraphs}.
18
 */
19
public interface EntityLayout {
20
21
	/**
22
	 * Returns a point laying in the center of this entity. Any subsequent
23
	 * changes to the returned point won't affect this node.
24
	 * 
25
	 * @return position of the center of this node
26
	 */
27
	public DisplayIndependentPoint getLocation();
28
29
	/**
30
	 * Sets the position of this entity. The node will be moved so that it's
31
	 * center is located in the given point.
32
	 * 
33
	 * @param x
34
	 *            the x-position
35
	 * @param y
36
	 *            the y-position
37
	 */
38
	public void setLocation(double x, double y);
39
40
	public DisplayIndependentDimension getSize();
41
42
	public void setSize(double width, double height);
43
44
	/**
45
	 * Returns aspect ratio that is preferred for this entity. Can be 0 if this
46
	 * node can't be resized anyway or it doesn't care about about its ratio.
47
	 * 
48
	 * @return aspect ratio (width / height)
49
	 */
50
	public double getPreferredAspectRatio();
51
52
	public boolean isResizable();
53
54
	public boolean isMovable();
55
56
	/**
57
	 * Returns all entities that are direct successors of this entity. Successor
58
	 * entities of an unpruned node N are:
59
	 * <ul>
60
	 * <li>all unpruned successor nodes of node N</li>
61
	 * <li>all subgraphs that are <code>GraphEntities</code> and contain at
62
	 * least one successor node of node N</li>
63
	 * </ul>
64
	 * Successor entities of a subgraph S that is a <code>GraphEntity</code>
65
	 * are:
66
	 * <ul>
67
	 * <li>all unpruned nodes that are successor of at least one node from
68
	 * subgraph S</li>
69
	 * <li>all subgraphs that are <code>GraphEntities</code> and contain at
70
	 * least one node that is a successor of at least one node from subgraph S</li>
71
	 * </ul>
72
	 * For subgraphs that are not <code>GraphEntities</code> an empty array will
73
	 * be returned.</br>Entities connected with this node by a bidirectional
74
	 * connection are considered both successors and predecessors. Any
75
	 * subsequent changes to the returned array do not affect this node.
76
	 * 
77
	 * @return array of successors of this node
78
	 */
79
	public EntityLayout[] getSuccessingEntities();
80
81
	/**
82
	 * Returns all entities that are direct predecessors of this entity.
83
	 * Predecessor entities of an unpruned node A are:
84
	 * <ul>
85
	 * <li>all unpruned predecessor nodes of node N</li>
86
	 * <li>all subgraphs that are <code>GraphEntities</code> and contain at
87
	 * least one predecessor node of node N</li>
88
	 * </ul>
89
	 * Successor entities of a subgraph S that is a <code>GraphEntity</code>
90
	 * are:
91
	 * <ul>
92
	 * <li>all unpruned nodes that are predecessor of at least one node from
93
	 * subgraph S</li>
94
	 * <li>all subgraphs that are <code>GraphEntities</code> and contain at
95
	 * least one node that is a predecessor of at least one node from subgraph S
96
	 * </li>
97
	 * </ul>
98
	 * For subgraphs that are not <code>GraphEntities</code> an empty array will
99
	 * be returned.</br>Entities connected with this node by a bidirectional
100
	 * connection are considered both successors and predecessors. Any
101
	 * subsequent changes to the returned array do not affect this node.
102
	 * 
103
	 * @return array of predecessors of this node
104
	 */
105
	public EntityLayout[] getPredecessingEntities();
106
}
(-)src/org/eclipse/zest/layouts/interfaces/ExpandCollapseManager.java (+63 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
/**
13
 * A manager that controls expanding and collapsing nodes in a Graph.
14
 */
15
public interface ExpandCollapseManager {
16
17
	/**
18
	 * Initializes the expansion state of all nodes in given layout context. The
19
	 * receiver can initialize its internal state related to the layout context
20
	 * and add its listeners if necessary.
21
	 * 
22
	 * @param context
23
	 *            the context to initialize
24
	 */
25
	public void initExpansion(LayoutContext context);
26
27
	/**
28
	 * Changes the expanded state of given node. It prunes/unprunes nodes and
29
	 * hides/shows connections in the graph according to its policy. If
30
	 * requested operation cannot be currently performed on the node, it does
31
	 * nothing.
32
	 * 
33
	 * @param context
34
	 *            context in which to perform the operation
35
	 * @param node
36
	 *            node to expand or collapse
37
	 * @param expanded
38
	 *            true to expand, false to collapse
39
	 */
40
	public void setExpanded(LayoutContext context, NodeLayout node, boolean expanded);
41
42
	/**
43
	 * Checks if given node can be expanded.
44
	 * 
45
	 * @param context
46
	 *            context containing the node
47
	 * @param node
48
	 *            node to check
49
	 * @return
50
	 */
51
	public boolean canExpand(LayoutContext context, NodeLayout node);
52
53
	/**
54
	 * Checks if given node can be collapsed.
55
	 * 
56
	 * @param context
57
	 *            context containing the node
58
	 * @param node
59
	 *            node to check
60
	 * @return
61
	 */
62
	public boolean canCollapse(LayoutContext context, NodeLayout node);
63
}
(-)src/org/eclipse/zest/layouts/interfaces/GraphStructureListener.java (+119 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
import org.eclipse.zest.layouts.LayoutAlgorithm;
13
14
public interface GraphStructureListener {
15
16
	public class Stub implements GraphStructureListener {
17
18
		public boolean nodeAdded(LayoutContext context, NodeLayout node) {
19
			return false;
20
		}
21
22
		public boolean nodeRemoved(LayoutContext context, NodeLayout node) {
23
			return false;
24
		}
25
26
		public boolean connectionAdded(LayoutContext context, ConnectionLayout connection) {
27
			return false;
28
		}
29
30
		public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection) {
31
			return false;
32
		}
33
	}
34
35
	/**
36
	 * This method is called whenever a node is added to a context. No separate
37
	 * events will be fired for eventual connections adjacent to the added node.
38
	 * 
39
	 * If true is returned, it means that the receiving listener has intercepted
40
	 * this event. Intercepted events will not be passed to the rest of the
41
	 * listeners. If the event is not intercepted by any listener,
42
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
43
	 * context's main algorithm.
44
	 * 
45
	 * @param context
46
	 *            the layout context that fired the event
47
	 * @param node
48
	 *            the added node
49
	 * @return true if no further operations after this event are required
50
	 */
51
	public boolean nodeAdded(LayoutContext context, NodeLayout node);
52
53
	/**
54
	 * This method is called whenever a node is removed from a context. No
55
	 * separate events will be fired for eventual connections adjacent to the
56
	 * removed node.
57
	 * 
58
	 * If true is returned, it means that the receiving listener has intercepted
59
	 * this event. Intercepted events will not be passed to the rest of the
60
	 * listeners. If the event is not intercepted by any listener,
61
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
62
	 * context's main algorithm.
63
	 * 
64
	 * @param context
65
	 *            the context that fired the event
66
	 * @param node
67
	 *            the removed node
68
	 * @return true if no further operations after this event are required
69
	 */
70
	public boolean nodeRemoved(LayoutContext context, NodeLayout node);
71
72
	/**
73
	 * This method is called whenever a connection is added to a context. It can
74
	 * be assumed that both source and target nodes of the added connection
75
	 * already exist in the context.
76
	 * 
77
	 * This method will be called only if both nodes connected by added
78
	 * connection lay directly in the node container owned by the notifying
79
	 * layout context.
80
	 * 
81
	 * If true is returned, it means that the receiving listener has intercepted
82
	 * this event. Intercepted events will not be passed to the rest of the
83
	 * listeners. If the event is not intercepted by any listener,
84
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
85
	 * context's main algorithm.
86
	 * 
87
	 * @param context
88
	 *            the context that fired the event
89
	 * @param connection
90
	 *            the added connection
91
	 * @return true if no further operations after this event are required
92
	 */
93
	public boolean connectionAdded(LayoutContext context, ConnectionLayout connection);
94
95
	/**
96
	 * This method is called whenever a connection is removed from a context. It
97
	 * can be assumed that both source and target nodes of the removed
98
	 * connection still exist in the context and will not be removed along with
99
	 * it.
100
	 * 
101
	 * This method will be called only if both nodes connected by removed
102
	 * connection lay directly in the node container owned by the notifying
103
	 * layout context.
104
	 * 
105
	 * If true is returned, it means that the receiving listener has intercepted
106
	 * this event. Intercepted events will not be passed to the rest of the
107
	 * listeners. If the event is not intercepted by any listener,
108
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
109
	 * context's main algorithm.
110
	 * 
111
	 * @param context
112
	 *            the context that fired the event
113
	 * @param connection
114
	 *            the added connection
115
	 * @return true if no further operations after this event are required
116
	 */
117
	public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection);
118
119
}
(-)src/org/eclipse/zest/layouts/interfaces/LayoutContext.java (+229 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
import org.eclipse.zest.layouts.LayoutAlgorithm;
13
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
14
15
/**
16
 * Objects implementing LayoutContext interface are used for exchanging of
17
 * information between layout algorithms and graphical objects displaying
18
 * graphs.
19
 */
20
public interface LayoutContext {
21
22
	/**
23
	 * Returns all the nodes that should be laid out. Replacing elements in the
24
	 * returned array does not affect this context.
25
	 * 
26
	 * @return array of nodes to lay out
27
	 */
28
	public NodeLayout[] getNodes();
29
30
	/**
31
	 * Returns all the connections between nodes that should be laid out.
32
	 * Replacing elements in the returned array does not affect this context.
33
	 * 
34
	 * @return array of connections between nodes
35
	 */
36
	public ConnectionLayout[] getConnections();
37
38
	/**
39
	 * Returns all entities that are currently placed on the graph, that is
40
	 * subgraphs and unpruned nodes. Replacing elements in the returned array
41
	 * does not affect this context.
42
	 * 
43
	 * @return array of entities to layout
44
	 */
45
	public EntityLayout[] getEntities();
46
47
	/**
48
	 * Returns all the connections between given source and target entities. If
49
	 * given entity is a subgraph, connections adjacent to each of its nodes
50
	 * will be included in the result. All the undirected nodes connecting the
51
	 * two nodes will be also included in the result. Replacing elements in the
52
	 * returned array does not affect this context.
53
	 * 
54
	 * @param layoutEntity1
55
	 * @param layoutEntity2
56
	 * @return
57
	 */
58
	public ConnectionLayout[] getConnections(EntityLayout layoutEntity1, EntityLayout layoutEntity2);
59
60
	/**
61
	 * 
62
	 * @return bounds in which the graph elements can be placed
63
	 */
64
	public DisplayIndependentRectangle getBounds();
65
66
	/**
67
	 * 
68
	 * @return true if a layout algorithm is allowed to place graph elements
69
	 *         outside of suggested bounds
70
	 */
71
	public boolean isBoundsExpandable();
72
73
	/**
74
	 * Returns all the subgraphs this context's nodes were pruned to. Replacing
75
	 * elements in the returned array does not affect this context.
76
	 * 
77
	 * @return array of subgraphs (may be empty)
78
	 */
79
	public SubgraphLayout[] getSubgraphs();
80
81
	/**
82
	 * Creates a subgraph containing given nodes and adds it to this context. If
83
	 * given nodes already belong to another subgraphs, they are removed from
84
	 * them prior to adding to the new subgraph.
85
	 * 
86
	 * @param nodes
87
	 *            nodes to add to the new subgraph
88
	 */
89
	public SubgraphLayout createSubgraph(NodeLayout[] nodes);
90
91
	/**
92
	 * 
93
	 * @return true if this layout context allows pruning nodes into subgraphs
94
	 */
95
	public boolean isPruningEnabled();
96
97
	/**
98
	 * Checks if this layout context allows layout algorithms to work
99
	 * continuously in background and change the layout with time or in reaction
100
	 * to some events. If background changes are not allowed, a layout algorithm
101
	 * can make changes in layout context only when
102
	 * {@link LayoutAlgorithm#applyLayout(boolean)} is called (otherwise a
103
	 * runtime exception will be thrown).
104
	 * 
105
	 * @return true if background layout changes are enabled
106
	 */
107
	public boolean isBackgroundLayoutEnabled();
108
109
	/**
110
	 * Sets the main layout algorithm for this context. Main algorithm will be
111
	 * used to relayout graph items using {@link LayoutAlgorithm#applyLayout()}
112
	 * after every event that is not intercepted by any listener.
113
	 * 
114
	 * @param algorithm
115
	 */
116
	public void setMainLayoutAlgorithm(LayoutAlgorithm algorithm);
117
118
	/**
119
	 * 
120
	 * @return the main algorithm of this context (see
121
	 *         {@link #setMainLayoutAlgorithm(LayoutAlgorithm)} for details)
122
	 */
123
	public LayoutAlgorithm getMainLayoutAlgorithm();
124
125
	/**
126
	 * Sets the expand/collapse manager for this context. The manger will be
127
	 * used to handle expansion related methods called on the owner of this
128
	 * context.
129
	 * 
130
	 * @param expandCollapseManager
131
	 */
132
	public void setExpandCollapseManager(ExpandCollapseManager expandCollapseManager);
133
134
	/**
135
	 * 
136
	 * @return current expand/collapse manager (can be null, which means that
137
	 *         pruning is not enabled).
138
	 */
139
	public ExpandCollapseManager getExpandCollapseManager();
140
141
	/**
142
	 * Adds a listener to the context that will be notified about changes in
143
	 * this context's layout, that is movement and resizing of nodes /
144
	 * subgraphs. The notifications will not include changes made with API
145
	 * included in layout related interfaces, so that layout algorithms won't be
146
	 * notified about changes they invoke. Only internal changes of the system
147
	 * will fire events.
148
	 * 
149
	 * @param listener
150
	 *            listener to add
151
	 */
152
	public void addLayoutListener(LayoutListener listener);
153
154
	/**
155
	 * Removes a layout listener from this context.
156
	 * 
157
	 * @param listener
158
	 *            listener to remove
159
	 */
160
	public void removeLayoutListener(LayoutListener listener);
161
162
	/**
163
	 * Adds a listener to the context that will be notified about changes in
164
	 * graph structure, that is addition and removal of nodes and connections.
165
	 * The notifications will not include changes made with API included in
166
	 * layout related interfaces, so that layout algorithms won't be notified
167
	 * about changes they invoke. Only internal changes of the system will fire
168
	 * events.
169
	 * 
170
	 * @param listener
171
	 *            listener to add
172
	 */
173
	public void addGraphStructureListener(GraphStructureListener listener);
174
175
	/**
176
	 * Removes a graph structure listener from this context.
177
	 * 
178
	 * @param listener
179
	 *            listener to remove
180
	 */
181
	public void removeGraphStructureListener(GraphStructureListener listener);
182
183
	/**
184
	 * Adds a listener to the context that will be notified about changes
185
	 * related to its configuration.
186
	 * 
187
	 * @param listener
188
	 *            listener to add
189
	 */
190
	public void addContextListener(ContextListener listener);
191
192
	/**
193
	 * Removes a context listener from this context.
194
	 * 
195
	 * @param listener
196
	 *            listener to remove
197
	 */
198
	public void removeContextListener(ContextListener listener);
199
200
	/**
201
	 * Adds a listener to the context that will be notified about changes in
202
	 * graph pruning, that is hiding and showing of nodes. The notifications
203
	 * will not include changes made with API included in layout related
204
	 * interfaces, so that layout algorithms won't be notified about changes
205
	 * they invoke. Only internal changes of the system will fire events.
206
	 * 
207
	 * @param listener
208
	 *            listener to add
209
	 */
210
	public void addPruningListener(PruningListener listener);
211
212
	/**
213
	 * Removes a pruning structure listener from this context.
214
	 * 
215
	 * @param listener
216
	 *            listener to remove
217
	 */
218
	public void removePruningListener(PruningListener listener);
219
220
	/**
221
	 * Causes all the changes made to elements in this context to affect the
222
	 * display.
223
	 * 
224
	 * @param animationHint
225
	 *            a hint for display mechanism indicating whether changes are
226
	 *            major and should be animated (if true) or not.
227
	 */
228
	public void flushChanges(boolean animationHint);
229
}
(-)src/org/eclipse/zest/layouts/interfaces/LayoutListener.java (+98 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
import org.eclipse.zest.layouts.LayoutAlgorithm;
13
14
/**
15
 * 
16
 * @author irbull
17
 * 
18
 * @noextend This interface is not intended to be extended by clients.
19
 * @noimplement This interface is not intended to be implemented by clients.
20
 */
21
public interface LayoutListener {
22
23
	/**
24
	 * This method is called whenever location of a particular node is changed
25
	 * within observed context. This usually implicates change of position (the
26
	 * center of the node) and the receiver should be aware of it (no additional
27
	 * {@link #nodeMoved(LayoutContext, NodeLayout)} event will be fired). If
28
	 * true is returned, it means that the receiving listener has intercepted
29
	 * this event. Intercepted events will not be passed to the rest of the
30
	 * listeners. If the event is not intercepted by any listener,
31
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
32
	 * context's main algorithm.LayoutListener
33
	 * 
34
	 * @param context
35
	 *            the layout context that fired the event
36
	 * @param node
37
	 *            the node that has moved
38
	 * @return true if no further operations after this event are required
39
	 */
40
	public boolean nodeMoved(LayoutContext context, NodeLayout node);
41
42
	/**
43
	 * This method is called whenever size of a particular node is changed
44
	 * within observed context. This usually implicates change of position (the
45
	 * center of the node) and the receiver should be aware of it (no additional
46
	 * {@link #nodeMoved(LayoutContext, NodeLayout)} event will be fired).
47
	 * 
48
	 * If true is returned, it means that the receiving listener has intercepted
49
	 * this event. Intercepted events will not be passed to the rest of the
50
	 * listeners. If the event is not intercepted by any listener,
51
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
52
	 * context's main algorithm.
53
	 * 
54
	 * @param context
55
	 *            the layout context that fired the event
56
	 * @param node
57
	 *            the node that was resized
58
	 * @return true if no further operations after this event are required
59
	 */
60
	public boolean nodeResized(LayoutContext context, NodeLayout node);
61
62
	/**
63
	 * This method is called whenever location of a particular subgraph is
64
	 * changed within observed context. If true is returned, it means that the
65
	 * receiving listener has intercepted this event. Intercepted events will
66
	 * not be passed to the rest of the listeners. If the event is not
67
	 * intercepted by any listener, {@link LayoutAlgorithm#applyLayout()
68
	 * applyLayout()} will be called on the context's main algorithm.
69
	 * 
70
	 * @param context
71
	 *            the layout context that fired the event
72
	 * @param subgraph
73
	 *            the subgraph that has moved
74
	 * @return true if no further operations after this event are required
75
	 */
76
	public boolean subgraphMoved(LayoutContext context, SubgraphLayout subgraph);
77
78
	/**
79
	 * This method is called whenever size of a particular subgraph is changed
80
	 * within observed context. This usually implicates change of position (the
81
	 * center of the node) and the receiver should be aware of it (no additional
82
	 * {@link #nodeMoved(LayoutContext, NodeLayout)} event will be fired).
83
	 * 
84
	 * If true is returned, it means that the receiving listener has intercepted
85
	 * this event. Intercepted events will not be passed to the rest of the
86
	 * listeners. If the event is not intercepted by any listener,
87
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
88
	 * context's main algorithm.
89
	 * 
90
	 * @param context
91
	 *            the layout context that fired the event
92
	 * @param subgraph
93
	 *            the subgraph that was resized
94
	 * @return true if no further operations after this event are required
95
	 */
96
	public boolean subgraphResized(LayoutContext context, SubgraphLayout subgraph);
97
98
}
(-)src/org/eclipse/zest/layouts/interfaces/NodeLayout.java (+91 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
13
public interface NodeLayout extends EntityLayout {
14
15
	public boolean isPrunable();
16
17
	public boolean isPruned();
18
19
	/**
20
	 * 
21
	 * @return a subgraph this node belongs to or null if this node is not
22
	 *         pruned
23
	 */
24
	public SubgraphLayout getSubgraph();
25
26
	/**
27
	 * 
28
	 * @param subgraph
29
	 *            a subgraph this node should belong to or null if this node
30
	 *            should not be pruned
31
	 */
32
	public void prune(SubgraphLayout subgraph);
33
34
	/**
35
	 * Returns all nodes that are direct successors of this node. Nodes
36
	 * connected with this node by a bidirectional connection are considered
37
	 * both successors and predecessors. Any subsequent changes to the returned
38
	 * array do not affect this node.
39
	 * 
40
	 * @return array of successors of this node
41
	 */
42
	public NodeLayout[] getSuccessingNodes();
43
44
	/**
45
	 * Returns all nodes that are direct predecessors of this node. Nodes
46
	 * connected with this node by a bidirectional connection are considered
47
	 * both successors and predecessors. Any subsequent changes to the returned
48
	 * array do not affect this node.
49
	 * 
50
	 * @return array of predecessors of this node
51
	 */
52
	public NodeLayout[] getPredecessingNodes();
53
54
	/**
55
	 * Returns all connections that have this node as a target. All connections
56
	 * that are bidirectional and are adjacent to this node will be also
57
	 * included in the result. Any subsequent changes to the returned array do
58
	 * not affect this node.
59
	 * 
60
	 * @return array of connections entering this node
61
	 */
62
	public ConnectionLayout[] getIncomingConnections();
63
64
	/**
65
	 * Returns all connections that have this node as a source. All connections
66
	 * that are bidirectional and are adjacent to this node will be also
67
	 * included in the result. Any subsequent changes to the returned array do
68
	 * not affect this node.
69
	 * 
70
	 * @return array of connections leaving this node
71
	 */
72
	public ConnectionLayout[] getOutgoingConnections();
73
74
	/**
75
	 * Sets the minimized state of this Node. Node that is minimized resizes its
76
	 * figure to (0, 0). When it is unminimized, it resizes back to previous
77
	 * dimension. The node's size property is not affected by minimized state,
78
	 * so an it can be minimized even if it's not resizable.
79
	 * 
80
	 * @param minimized
81
	 *            new minimized state
82
	 */
83
	public void setMinimized(boolean minimized);
84
85
	/**
86
	 * @see #setMinimized(boolean)
87
	 * 
88
	 * @return true if this entity is minimized
89
	 */
90
	public boolean isMinimized();
91
}
(-)src/org/eclipse/zest/layouts/interfaces/PruningListener.java (+51 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
import org.eclipse.zest.layouts.LayoutAlgorithm;
13
14
public interface PruningListener {
15
16
	/**
17
	 * This method is called when some nodes are pruned in a layout context.
18
	 * 
19
	 * If true is returned, it means that the receiving listener has intercepted
20
	 * this event. Intercepted events will not be passed to the rest of the
21
	 * listeners. If the event is not intercepted by any listener,
22
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
23
	 * context's main algorithm.
24
	 * 
25
	 * @param context
26
	 *            the layout context that fired the event
27
	 * @param subgraph
28
	 *            subgraphs that have been created or had nodes added
29
	 * @return true if no further operations after this event are required
30
	 */
31
	public boolean nodesPruned(LayoutContext context, SubgraphLayout[] subgraph);
32
33
	/**
34
	 * This method is called when some nodes are unpruned in a layout context,
35
	 * that is they are no longer part of a subgraph.
36
	 * 
37
	 * If true is returned, it means that the receiving listener has intercepted
38
	 * this event. Intercepted events will not be passed to the rest of the
39
	 * listeners. If the event is not intercepted by any listener,
40
	 * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the
41
	 * context's main algorithm.
42
	 * 
43
	 * @param context
44
	 *            the layout context that fired the event
45
	 * @param nodes
46
	 *            nodes that have been unpruned
47
	 * @return true if no further operations after this event are required
48
	 */
49
	public boolean nodesUnpruned(LayoutContext context, NodeLayout[] nodes);
50
51
}
(-)src/org/eclipse/zest/layouts/interfaces/SubgraphLayout.java (+99 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009-2010 Mateusz Matela and others. All rights reserved. This
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
6
 * 
7
 * Contributors: Mateusz Matela - initial API and implementation
8
 *               Ian Bull
9
 ******************************************************************************/
10
package org.eclipse.zest.layouts.interfaces;
11
12
13
/**
14
 * An interface for subgraphs in layout. A subgraph is a set of pruned nodes
15
 * that will be displayed as one element. A subgraph must contain at least one
16
 * node (empty subgraphs will be removed from its context). Every node can
17
 * belong to at most one subgraph.
18
 */
19
public interface SubgraphLayout extends EntityLayout {
20
21
	/**
22
	 * Constant for top-down direction (default).
23
	 */
24
	public final int TOP_DOWN = 1;
25
26
	/**
27
	 * Constant for bottom-up direction.
28
	 */
29
	public final int BOTTOM_UP = 2;
30
31
	/**
32
	 * Constant for direction from left to right.SubgraphLayout
33
	 */
34
	public final int LEFT_RIGHT = 3;
35
36
	/**
37
	 * Constant for direction from right to left.
38
	 */
39
	public final int RIGHT_LEFT = 4;
40
41
	/**
42
	 * Returns all the nodes belonging to this subgraph. Replacing elements in
43
	 * the returned array does not affect this subgraph.
44
	 * 
45
	 * @return array of nodes
46
	 */
47
	public NodeLayout[] getNodes();
48
49
	/**
50
	 * 
51
	 * @return number of nodes pruned into this subgraph
52
	 */
53
	public int countNodes();
54
55
	/**
56
	 * Adds nodes to this subgraph. If given nodes already belong to another
57
	 * subgraph, they are first removed from them.
58
	 * 
59
	 * @param nodes
60
	 *            array of nodes to add
61
	 */
62
	public void addNodes(NodeLayout[] nodes);
63
64
	/**
65
	 * Removes nodes from this subgraph.
66
	 * 
67
	 * @param nodes
68
	 *            array of nodes to remove
69
	 */
70
	public void removeNodes(NodeLayout[] nodes);
71
72
	/**
73
	 * Returns true if this subgraph is visualized as a particular object on the
74
	 * graph. If this method returns false, it means that this subgraph will not
75
	 * be visible so all methods related to location, size and direction should
76
	 * be ignored.
77
	 * 
78
	 * @return whether or not this subgraph is a graph entity that should be
79
	 *         laid out.
80
	 */
81
	public boolean isGraphEntity();
82
	
83
	/**
84
	 * @return true if this subgraph is visualized differently depending on
85
	 *         direction
86
	 */
87
	public boolean isDirectionDependant();
88
89
	/**
90
	 * Sets the direction of this subgraph (does nothing in case of subgraphs
91
	 * that don't depend on direction)
92
	 * 
93
	 * @param direction
94
	 *            one of constants: {@link #TOP_DOWN}, {@link #BOTTOM_UP},
95
	 *            {@link #LEFT_RIGHT}, {@link #RIGHT_LEFT}
96
	 */
97
	public void setDirection(int direction);
98
99
}
(-)src/org/eclipse/zest/layouts/progress/ProgressEvent.java (-47 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.progress;
12
13
/**
14
 * When an algorithm wants to notify everyone it has completely part of its task, it
15
 * throws a ProgressEvent.  The progress is a number (currentProgress) representing the
16
 * current steps completed out of the total number of steps (totalProgress)
17
 * 
18
 * @author Casey Best
19
 */
20
public class ProgressEvent {
21
	int stepsCompleted;
22
	int totalSteps;
23
	
24
	/**
25
	 * Creates a progress event.
26
	 * @param stepsCompleted The current progress out of the total
27
	 * @param totalNumberOfSteps The number used to indicate when the algorithm will finish
28
	 */
29
	public ProgressEvent (int stepsCompleted, int totalNumberOfSteps) {
30
		this.stepsCompleted = stepsCompleted;
31
		this.totalSteps = totalNumberOfSteps;
32
	}
33
	
34
	/**
35
	 * Returns the number of steps already completed.
36
	 */
37
	public int getStepsCompleted() {
38
		return stepsCompleted;
39
	}
40
	
41
	/**
42
	 * Returns the total number of steps to complete.
43
	 */
44
	public int getTotalNumberOfSteps() {
45
		return totalSteps;
46
	}
47
}
(-)src/org/eclipse/zest/layouts/progress/ProgressListener.java (-38 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     The Chisel Group, University of Victoria
10
 *******************************************************************************/
11
package org.eclipse.zest.layouts.progress;
12
13
14
/**
15
 * Listens for ProgressEvents which are thrown by layout algorithms at frequent intervals.
16
 * 
17
 * @author Ian Bull
18
 * @author Casey Best
19
 */
20
public interface ProgressListener {
21
22
	/**
23
	 * 
24
	 * @param e
25
	 */
26
	public void progressStarted( ProgressEvent e );
27
	
28
	/**
29
	 * Called when the progress of a layout changes
30
	 */
31
	public void progressUpdated (ProgressEvent e);
32
33
	/**
34
	 * 
35
	 * @param e
36
	 */
37
	public void progressEnded( ProgressEvent e );
38
}

Return to bug 277534