Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: acb692ae519140fa6b7c33b560ec17295555c8a2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
/*******************************************************************************
 * Copyright (c) 2013, 2014 Ericsson.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Marc Dumais (Ericsson) - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.visualizer.ui.canvas;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;

/**
 * Graphic object that can be used as a container for child objects. Each object
 * is sized and positioned in virtual units, and positioned relative to the parent
 * object's position. Setting the real (pixel) bounds of an object recursively sets
 * the bounds of any contained child objects, taking into account its virtual
 * bounds, compared to its parent.
 * @since 1.1
 */
public class VirtualBoundsGraphicObject extends GraphicObject {

	// --- members ---

	/**
	 * Holds the virtual position and size of graphical object.
	 * Position is relative to parent object
	 */
	protected Rectangle m_virtualBounds = new Rectangle(0, 0, 0, 0);

	/** List of children objects contained in this one */
	protected ArrayList<VirtualBoundsGraphicObject> m_childrenObjects = new ArrayList<VirtualBoundsGraphicObject>();

	/** Map of contained objects and their identifying labels. for quick look-up */
	protected HashMap<String, VirtualBoundsGraphicObject> m_childrenObjectsMap = new HashMap<String, VirtualBoundsGraphicObject>();

	/** Whether the container's boundaries should be drawn */
	protected boolean m_drawContainerBounds = true;

	/** Is the object selectable? */
	protected boolean m_selectable = false;

	/** Color to use when this object is Selected */
	protected Color m_selectedColor = null;

	/** Value for the margin in pixels */
	protected int m_childMargin = 0;

	/** Default for the margin in pixels */
	protected static final int MARGIN_PIXELS_DEFAULT = 1;

	// --- constructors/destructors ---

	/** Constructor */
	public VirtualBoundsGraphicObject() {
		// default is not selectable
		this(false, MARGIN_PIXELS_DEFAULT);
	}

	/** Alternate constructor */
	public VirtualBoundsGraphicObject(boolean selectable, int childMargin) {
		m_selectable = selectable;
		setDrawContainerBounds(true);
		m_childMargin = childMargin;
	}

	/** Dispose method - recursively dispose this object and children objects */
	@Override
	public void dispose() {
		super.dispose();
		if (m_childrenObjects != null) {
			for (VirtualBoundsGraphicObject o : m_childrenObjects) {
				o.dispose();
			}
			m_childrenObjects.clear();
			m_childrenObjects = null;
		}
		if (m_childrenObjectsMap != null) {
			m_childrenObjectsMap.clear();
			m_childrenObjectsMap = null;
		}
	}

	// --- Object methods ---

	/** Returns string representation. */
	@Override
	public String toString() {
		return String.format("Class: %s, Real bounds: %s, Virtual bounds: %s" + ", Draw container bounds: %s ",
				this.getClass().getSimpleName(), this.getBounds().toString(), m_virtualBounds.toString(),
				m_drawContainerBounds);
	}

	// --- accessors ---

	/**
	 * Sets whether the container's boundary should be drawn / filled-in.
	 * If false, indicates child objects are displayed without showing
	 * the parent container.
	 */
	public void setDrawContainerBounds(boolean draw) {
		m_drawContainerBounds = draw;
	}

	/** Sets whether the object is selectable */
	public void setSelectable(boolean sel) {
		m_selectable = sel;
	}

	/** Returns whether the object is selectable */
	public boolean isSelectable() {
		return m_selectable;
	}

	/** Set the color used to highlight a selection */
	public void setSelectedColor(Color color) {
		m_selectedColor = color;
	}

	/** Get the color used to highlight a selection */
	public Color getSelectedColor() {
		return m_selectedColor;
	}

	/** Set the margin to be inserted between a parent and a child object,
	 * in pixels */
	public void setChildMargin(int margin) {
		m_childMargin = margin;
	}

	// --- methods ---

	/**
	 * Sets the absolute bounds (in pixels) of this container object. If it has
	 * children objects, recursively set their absolute bounds.
	 * Overridden to delegate to setBounds(int,int,int,int) in this class,
	 * rather than the base class.
	 */
	@Override
	public void setBounds(Rectangle bounds) {
		setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
	}

	/**
	 * Sets the absolute bounds (in pixels) of this container object. If it has
	 * children objects, recursively set their absolute bounds.
	 */
	@Override
	public void setBounds(int x, int y, int w, int h) {
		super.setBounds(x, y, w, h);
		for (VirtualBoundsGraphicObject o : m_childrenObjects) {
			o.setBounds(virtualToRealBounds(o.getVirtualBounds()));
		}
	}

	/**
	 * Set bounds of current object relative to its container.
	 * The x and y coordinate are relative to the parent container.
	 * The unit is arbitrary integer but have to be consistent between
	 * parent and children. Width and height must be greater than zero.
	 */
	public void setVirtualBounds(int[] bounds) {
		m_virtualBounds.x = bounds[0];
		m_virtualBounds.y = bounds[1];
		m_virtualBounds.width = bounds[2];
		m_virtualBounds.height = bounds[3];
		checkVirtualBounds();
	}

	/**
	 * Set bounds of current object relative to its container.
	 * The x and y coordinate are relative to the parent container.
	 * The unit is arbitrary integer but have to be consistent between
	 * parent and children. Width and height must be greater than zero.
	 */
	public void setVirtualBounds(Rectangle bounds) {
		m_virtualBounds = bounds;
		checkVirtualBounds();
	}

	/**
	 * Set bounds of current object relative to its container.
	 * The x and y coordinate are relative to the parent container.
	 * The unit is arbitrary integer but have to be consistent between
	 * parent and children. Width and height must be greater than zero.
	 */
	public void setVirtualBounds(int x, int y, int width, int height) {
		m_virtualBounds.x = x;
		m_virtualBounds.y = y;
		m_virtualBounds.width = width;
		m_virtualBounds.height = height;
		checkVirtualBounds();
	}

	/** Get the relative bounds of current object, relative to its parent container */
	public Rectangle getVirtualBounds() {
		return m_virtualBounds;
	}

	/** Performs a sanity check of the virtual bounds of this object */
	private void checkVirtualBounds() {
		if (m_virtualBounds.x < 0) {
			throw new IllegalArgumentException("Illegal x: " + m_virtualBounds.x);
		}
		if (m_virtualBounds.y < 0) {
			throw new IllegalArgumentException("Illegal y: " + m_virtualBounds.y);
		}
		if (m_virtualBounds.width <= 0) {
			throw new IllegalArgumentException("Illegal width: " + m_virtualBounds.width);
		}
		if (m_virtualBounds.height <= 0) {
			throw new IllegalArgumentException("Illegal height: " + m_virtualBounds.height);
		}
	}

	/**
	 * Returns the computed absolute (canvas) bounds of passed child object,
	 * considering its virtual bounds compared to its parent's (current object)
	 */
	public Rectangle virtualToRealBounds(Rectangle childsVirtualBounds) {
		float ox = 0.0f;
		float oy = 0.0f;
		float ow = 0.0f;
		float oh = 0.0f;

		ox = (float) this.getBounds().x
				+ childsVirtualBounds.x * ((float) this.getBounds().width / (this.getVirtualBounds().width));
		oy = (float) this.getBounds().y
				+ childsVirtualBounds.y * ((float) this.getBounds().height / this.getVirtualBounds().height);
		ow = ((float) childsVirtualBounds.width / this.getVirtualBounds().width) * this.getBounds().width;
		oh = ((float) childsVirtualBounds.height / this.getVirtualBounds().height) * this.getBounds().height;

		// add margin
		ox += m_childMargin;
		oy += m_childMargin;
		ow -= 2 * m_childMargin;
		oh -= 2 * m_childMargin;

		// make sure computed width and height are positive
		ow = (ow > 0) ? ow : 0.0f;
		oh = (oh > 0) ? oh : 0.0f;
		return new Rectangle(Math.round(ox), Math.round(oy), Math.round(ow), Math.round(oh));
	}

	/** Add children graphical object in this container. Provided label can be used to later retrieve object */
	public VirtualBoundsGraphicObject addChildObject(String label, VirtualBoundsGraphicObject obj) {
		m_childrenObjects.add(obj);
		m_childrenObjectsMap.put(label, obj);
		return obj;
	}

	/** Returns a list of child objects of a given derived class, optionally recursing through child objects */
	public ArrayList<VirtualBoundsGraphicObject> getChildObjects(Class<?> type, boolean recurse) {
		ArrayList<VirtualBoundsGraphicObject> objs = new ArrayList<VirtualBoundsGraphicObject>();

		for (VirtualBoundsGraphicObject o : this.getAllObjects(recurse)) {
			if (type.isInstance(o)) {
				objs.add(o);
			}
		}
		return objs;
	}

	/** Searches recursively for a child object matching a label.
	 Returns null if object is not found */
	public VirtualBoundsGraphicObject getObject(String label) {
		return getObject(label, true);
	}

	/** Searches for a child object matching a label. Recurse flag
	 controls whether the search is recursive. */
	public VirtualBoundsGraphicObject getObject(String label, boolean recurse) {
		if (m_childrenObjectsMap.containsKey(label)) {
			return m_childrenObjectsMap.get(label);
		} else if (recurse) {
			for (VirtualBoundsGraphicObject o : m_childrenObjects) {
				if (o.getObject(label) != null) {
					return o.getObject(label, true);
				}
			}
		}
		return null;
	}

	/** Gets all objects from this container. Optionally recurse to all sub-objects */
	public ArrayList<VirtualBoundsGraphicObject> getAllObjects(boolean recurse) {
		ArrayList<VirtualBoundsGraphicObject> list = new ArrayList<VirtualBoundsGraphicObject>();
		for (VirtualBoundsGraphicObject o : m_childrenObjects) {
			list.add(o);
			if (recurse) {
				list.addAll(o.getAllObjects(recurse));
			}
		}
		return list;
	}

	/** Returns a list of selectable objects */
	public List<VirtualBoundsGraphicObject> getSelectableObjects() {
		List<VirtualBoundsGraphicObject> list = new ArrayList<VirtualBoundsGraphicObject>();
		for (VirtualBoundsGraphicObject o : m_childrenObjects) {
			if (o.isSelectable()) {
				list.add(o);
			}
			list.addAll(o.getSelectableObjects());
		}
		return list;
	}

	/**
	 * Returns whether an immediate child of current object reports
	 * having decorations to display
	 */
	public boolean hasChildrenWithDecorations() {
		for (VirtualBoundsGraphicObject o : getAllObjects(false)) {
			if (o.hasDecorations()) {
				return true;
			}
		}
		return false;
	}

	// --- paint methods ---

	/**
	 * Invoked to allow element to paint itself on the viewer canvas
	 * Also paints any children objects(s)
	 */
	@Override
	public void paint(GC gc, boolean decorations) {
		// Set GC to reflect object properties, if set.
		Color oldForeground = null;
		Color oldBackground = null;
		if (m_foreground != null) {
			oldForeground = gc.getForeground();
			gc.setForeground(m_foreground);
		}
		if (m_background != null) {
			oldBackground = gc.getBackground();
			gc.setBackground(m_background);
		}

		if (!decorations) {
			// Paint the object.
			if (isVisible()) {
				paintContent(gc);
			}
		} else {
			// Paint decorations
			if (isVisible() && hasDecorations()) {
				paintDecorations(gc);
			}
		}

		// recursively paint children objects
		if (m_childrenObjects != null) {
			for (VirtualBoundsGraphicObject o : m_childrenObjects) {
				o.paint(gc, decorations);
			}
		}

		// Restore old state.
		if (m_foreground != null && oldForeground != null)
			gc.setForeground(oldForeground);
		if (m_background != null && oldBackground != null)
			gc.setBackground(oldBackground);
	}

	/**
	 * Invoked to allow element to paint itself on the viewer canvas.
	 */
	@Override
	public void paintContent(GC gc) {
		if (m_drawContainerBounds) {
			if (isSelected() && m_selectedColor != null) {
				gc.setForeground(m_selectedColor);
			} else {
				gc.setForeground(m_foreground);
			}
			gc.setBackground(m_background);
			gc.fillRectangle(m_bounds);
			super.paintContent(gc);
		}
	}

	/**
	 * Recursively checks if children objects have decorations to draw.
	 * If overridden in a derived type, this behavior should be preserved
	 * to ensure that children's decorations are drawn.
	 */
	@Override
	public boolean hasDecorations() {
		return hasChildrenWithDecorations();
	}
}

Back to the top