diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/META-INF/MANIFEST.MF index c7e8b0ddadc..d2d11301404 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.debug.core, org.eclipse.swt, org.eclipse.cdt.launch;bundle-version="6.1.0", + org.eclipse.cdt.visualizer.ui, org.eclipse.cdt.gdb;bundle-version="7.0.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/VisualizerVirtualBoundsGraphicObjectTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/VisualizerVirtualBoundsGraphicObjectTest.java new file mode 100644 index 00000000000..b3e9541766b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/VisualizerVirtualBoundsGraphicObjectTest.java @@ -0,0 +1,136 @@ + +/******************************************************************************* + * Copyright (c) 2014 Ericsson + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Marc Dumais (Ericsson) - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests; + +import org.eclipse.cdt.visualizer.ui.canvas.VirtualBoundsGraphicObject; +import org.eclipse.swt.graphics.Rectangle; +import org.junit.Test; + +public class VisualizerVirtualBoundsGraphicObjectTest { + + + // testcases + + /** + * Test that VirtualBoundsGraphicObject scale correctly. + * To do that create a hierarchy of graphical object using virtual coordinates + * and sizes (bounds). Then set the real, pixel-bounds for the outer-most object. + * Verify that child objects end-up with expected pixel bounds. + * + * Also test retrieval of child objects. + */ + @Test + public void testVirtualBoundsGraphicObjectRelativeSizingAndRetrieval() throws Exception { + + // we create the graphical objects with no margin, so as to make the sizes + // easier to predict / understand, for this test + VirtualBoundsGraphicObject containerA = new VirtualBoundsGraphicObject(true, 0); + VirtualBoundsGraphicObject containerB = new VirtualBoundsGraphicObject(true, 0); + VirtualBoundsGraphicObject containerC = new VirtualBoundsGraphicObject(true, 0); + VirtualBoundsGraphicObject containerD = new VirtualBoundsGraphicObject(true, 0); + VirtualBoundsGraphicObject containerE = new VirtualBoundsGraphicObject(true, 0); + VirtualBoundsGraphicObject containerF = new VirtualBoundsGraphicObject(true, 0); + + // Add B within A + containerA.addChildObject("B", containerB); + + // A - The outer-most container - is a virtual square of size 10x10 + containerA.setVirtualBounds(new Rectangle(0, 0, 10, 10)); + // Relative to A, B starts at (1,1) and is of size 8x8 + containerB.setVirtualBounds(new Rectangle(1, 1, 8, 8)); + + // Add C, D and E within B + containerB.addChildObject("C", containerC); + containerB.addChildObject("D", containerD); + containerB.addChildObject("E", containerE); + + // Relative to B, C starts at (3,2) and is of size 2x2 + containerC.setVirtualBounds(new Rectangle(3, 2, 2, 2)); + // Relative to B, D starts at (3,5) and is of size 2x2 + containerD.setVirtualBounds(new Rectangle(3, 5, 2, 2)); + // Relative to B, E starts at (6,1) and is of size 1x6 + containerE.setVirtualBounds(new Rectangle(6, 1, 1, 6)); + + // Add F within C + containerC.addChildObject("F", containerF); + + // Relative to C, F starts at (1,1) and is of size 1x1 + containerF.setVirtualBounds(new Rectangle(1, 1, 1, 1)); + + // Define the real pixel bounds of the outer-most container - + // the pixel coordinates of all children objects will be + // computed recursively + containerA.setBounds(100, 50, 100, 100); + + // check computed bounds for containerA + org.junit.Assert.assertEquals(100, containerA.getBounds().x); + org.junit.Assert.assertEquals(50, containerA.getBounds().y); + org.junit.Assert.assertEquals(100, containerA.getBounds().width); + org.junit.Assert.assertEquals(100, containerA.getBounds().height); + + // check computed bounds for containerB + org.junit.Assert.assertEquals(110, containerB.getBounds().x); + org.junit.Assert.assertEquals(60, containerB.getBounds().y); + org.junit.Assert.assertEquals(80, containerB.getBounds().width); + org.junit.Assert.assertEquals(80, containerB.getBounds().height); + + // check computed bounds for containerC + org.junit.Assert.assertEquals(140, containerC.getBounds().x); + org.junit.Assert.assertEquals(80, containerC.getBounds().y); + org.junit.Assert.assertEquals(20, containerC.getBounds().width); + org.junit.Assert.assertEquals(20, containerC.getBounds().height); + + // check computed bounds for containerD + org.junit.Assert.assertEquals(140, containerD.getBounds().x); + org.junit.Assert.assertEquals(110, containerD.getBounds().y); + org.junit.Assert.assertEquals(20, containerD.getBounds().width); + org.junit.Assert.assertEquals(20, containerD.getBounds().height); + + // check computed bounds for containerE + org.junit.Assert.assertEquals(170, containerE.getBounds().x); + org.junit.Assert.assertEquals(70, containerE.getBounds().y); + org.junit.Assert.assertEquals(10, containerE.getBounds().width); + org.junit.Assert.assertEquals(60, containerE.getBounds().height); + + // check computed bounds for containerF + org.junit.Assert.assertEquals(150, containerF.getBounds().x); + org.junit.Assert.assertEquals(90, containerF.getBounds().y); + org.junit.Assert.assertEquals(10, containerF.getBounds().width); + org.junit.Assert.assertEquals(10, containerF.getBounds().height); + + + // check recursive object retrieval returns expected number of child objects, for A + org.junit.Assert.assertEquals(5, containerA.getAllObjects(true).size()); + + // check recursive object retrieval returns expected number of child objects, for B + org.junit.Assert.assertEquals(4, containerB.getAllObjects(true).size()); + + // check non-recursive object retrieval returns expected number of (1st level) child objects, for A + org.junit.Assert.assertEquals(1, containerA.getAllObjects(false).size()); + + // check object retrieval returns expected number of "selectable" child objects, for C + org.junit.Assert.assertEquals(1, containerC.getSelectableObjects().size()); + + // check specific children-object retrieval + VirtualBoundsGraphicObject obj = containerA.getObject("D"); + org.junit.Assert.assertEquals(containerD, obj); + + // check another specific object retrieval + obj = containerC.getObject("F"); + org.junit.Assert.assertEquals(containerF, obj); + + // Check the retrieval of a non-existing object + obj = containerC.getObject("blablabla"); + org.junit.Assert.assertEquals(null, obj); + } + +} \ No newline at end of file diff --git a/visualizer/org.eclipse.cdt.visualizer.ui/src/org/eclipse/cdt/visualizer/ui/canvas/VirtualBoundsGraphicObject.java b/visualizer/org.eclipse.cdt.visualizer.ui/src/org/eclipse/cdt/visualizer/ui/canvas/VirtualBoundsGraphicObject.java new file mode 100644 index 00000000000..ba1d1700e6b --- /dev/null +++ b/visualizer/org.eclipse.cdt.visualizer.ui/src/org/eclipse/cdt/visualizer/ui/canvas/VirtualBoundsGraphicObject.java @@ -0,0 +1,419 @@ +/******************************************************************************* + * Copyright (c) 2013, 2014 Ericsson. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * 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 m_childrenObjects = + new ArrayList(); + + /** Map of contained objects and their identifying labels. for quick look-up */ + protected HashMap m_childrenObjectsMap = + new HashMap(); + + /** 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 getChildObjects(Class type, boolean recurse) { + ArrayList objs = new ArrayList(); + + 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 getAllObjects(boolean recurse) { + ArrayList list = new ArrayList(); + 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 getSelectableObjects() { + List list = new ArrayList(); + 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(); + } +}