Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » Graphiti » Nesting Shapes etc...
Nesting Shapes etc... [message #989310] Wed, 05 December 2012 15:42 Go to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi,

I'm trying to build an non-EMF-based editor with Graphiti.

I finished the Eclipse Help Tutorial and looked through some other code snippets, but unfortunately I can't find information about the following:
(If there is a tutorial or code or forum topic, please post the link)

I'm trying to build a kind of UML editor.

It's pretty similar to this one, shown in the Graphiti introduction:
http://help.eclipse.org/juno/topic/org.eclipse.graphiti.doc/resources/docu/gfw/visio/class-editor.gif

At the moment, I'm trying to build the classes.
Therefore, I added CreateFeatures and AddFeatures for Classes and Attributes.

I took a while to figure out, how to add attributes to classes (could not find a tutorial for this one as well)

At the moment my code looks like this:

AddClassObjectFeature:
	@Override
	public PictogramElement add(IAddContext context) {
		ClassObject addedClass = (ClassObject) context.getNewObject();
		Diagram targetDiagram = (Diagram) context.getTargetContainer();

		IPeCreateService peCreateService = Graphiti.getPeCreateService();
		IGaService gaService = Graphiti.getGaService();

		/***********/
		/** CLASS **/
		/***********/
		ContainerShape classContainer = peCreateService.createContainerShape(
				targetDiagram, true);

		// check whether the context has a size (e.g. from a create feature)
		// otherwise define a default size for the shape
		final int width = context.getWidth() <= 0 ? 100 : context.getWidth();
		final int height = context.getHeight() <= 0 ? 200 : context.getHeight();

		// create and set graphics algorithm
		Rectangle roundedRectangle = gaService.createRectangle(classContainer);
		roundedRectangle.setForeground(manageColor(CLASSOBJECT_FOREGROUND));
		roundedRectangle.setBackground(manageColor(CLASSOBJECT_BACKGROUND));
		roundedRectangle.setLineWidth(1);
		gaService.setLocationAndSize(roundedRectangle, context.getX(),
				context.getY(), width, height);

		// create link and wire it
		link(classContainer, addedClass);

		/**********/
		/** LINE **/
		/**********/
		// create shape for line
		Shape shape = peCreateService.createShape(classContainer, false);

		// create and set graphics algorithm
		Polyline polyline = gaService.createPolyline(shape, new int[] { 0,
				LINE_HEIGHT, width, 20 });
		polyline.setForeground(manageColor(CLASSOBJECT_FOREGROUND));
		polyline.setLineWidth(2);

		/**********************************/
		/** CLASS NAME (shape with text) **/
		/**********************************/
		// create shape for text
		Shape classNameShape = peCreateService.createShape(classContainer,
				false);

		// create and set text graphics algorithm
		Text className = gaService.createText(classNameShape,
				addedClass.getName());
		className.setForeground(manageColor(CLASSOBJECT_TEXT_FOREGROUND));
		className.setHorizontalAlignment(Orientation.ALIGNMENT_CENTER);
		// vertical alignment has as default value "center"
		className.setFont(gaService
				.manageDefaultFont(getDiagram(), false, true));
		gaService.setLocationAndSize(className, 0, 0, width,
				DEFAULT_HEIGHT_TEXT);

		// create link and wire it
		link(classNameShape, addedClass);

		/*****************/
		/** ATTRIBUTES **/
		/*****************/
                // just for testing
		Attribute attribute = new Attribute("TestA1");

		ContainerShape attributeContainer = peCreateService
				.createContainerShape(classContainer, true);

		// create and set graphics algorithm
		Rectangle attribRectangle = gaService
				.createInvisibleRectangle(attributeContainer);

		attribRectangle.setTransparency(0.0);
		attribRectangle.setForeground(manageColor(CLASSOBJECT_FOREGROUND));
		attribRectangle.setBackground(manageColor(CLASSOBJECT_BACKGROUND));
		attribRectangle.setLineWidth(1);
		gaService.setLocationAndSize(attribRectangle, 0, LINE_HEIGHT, width,
				height);

		// Create an add context that describes where to add the Attribute in
		// the class
		AddContext addAttribContext = new AddContext(new AreaContext(),
				attribute);
		addAttribContext.setWidth(width);

		addAttribContext.setTargetContainer(attributeContainer);

		// Trigger the Graphiti framework to find a feature that is capable of
		// adding a Attribute
		addGraphicalRepresentation(addAttribContext, attribute);

		// call the layout feature
		layoutPictogramElement(classContainer);

		return classContainer;
	}


AddAttributeFeature:
	@Override
	public PictogramElement add(IAddContext context) {
		// Get information from context
		Attribute attrib = (Attribute) context.getNewObject();
		ContainerShape containerShape = context.getTargetContainer();

		IPeService peCreateService = Graphiti.getPeService();
		IGaService gaService = Graphiti.getGaService();

		// create shape for text
		Shape attribNameShape = peCreateService.createShape(containerShape,
				false);

		// create and set text graphics algorithm
		Text attribName = gaService.createText(attribNameShape,
				attrib.getName());
		attribName.setForeground(manageColor(CLASSOBJECT_TEXT_FOREGROUND));
		attribName.setHorizontalAlignment(Orientation.ALIGNMENT_LEFT);
		attribName.setVerticalAlignment(Orientation.ALIGNMENT_TOP);
		attribName.setFont(gaService.manageDefaultFont(getDiagram(), false,
				true));
		gaService.setLocationAndSize(attribName, 0, 0, context.getWidth(), 20);

		// create link and wire it
		link(attribNameShape, attrib);

		return attribNameShape;
	}


First of all, I'm absolutely not sure if this is the right way to do it.
(Of course, the attributes for classes won't be hard-coded later)

Second, I've encountered some problems:

1) The Rectangle used as GA for the Attribute-Container, is visible and can be moved and resized indepedent of the ClassContainer. This is not really the way I want it to work.

2) Do I really have to compute the exact coordinates for every Text-Shape representing an attribute? Is there anything like the e.g. the ToolbarLayout in GEF available in Graphiti?

3) What will happen, when I (later on) delete a single attribute from the container? How can I update the positions of the other ones at the best?
I guess by using a LayoutFeature?


Is there any tutorial showing the differences between the shapes and how to use them? Which GA are necessary to do different things...and stuff like this? To better understand the Shape structure?

Thanks in advance,
Tim

[Updated on: Thu, 06 December 2012 05:36]

Report message to a moderator

Re: Nesting Shapes etc... [message #989400 is a reply to message #989310] Thu, 06 December 2012 05:32 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Tim E. wrote on Wed, 05 December 2012 10:42

1) The Rectangle used as GA for the Attribute-Container, is visible and can be moved and resized indepedent of the ClassContainer. This is not really the way I want it to work.


Okay, so I just set the rectangle to "inactive" and I'm done Smile

I don't really know, why I didn't do it before Smile

[Updated on: Thu, 06 December 2012 05:33]

Report message to a moderator

Re: Nesting Shapes etc... [message #989430 is a reply to message #989310] Thu, 06 December 2012 08:21 Go to previous messageGo to next message
Michael Wenz is currently offline Michael Wenz
Messages: 1618
Registered: July 2009
Location: Walldorf, Germany
Senior Member
Tim,

unfortunatly there is so far no example that really shows how to build such
an shape with nested details. There's an open request for that, though:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=364452

What we have newly in the current head version is an example with nested
details, but that is using the new IdPattern, so I'm not sure if that helps.
Here's the link in case it does:
http://git.eclipse.org/c/gmp/org.eclipse.gmp.graphiti.git/tree/examples/org.eclipse.graphiti.examples.filesystem/src/org/eclipse/graphiti/examples/filesystem/patterns/FolderPattern.java

To your open questions: yes, you will need to set the coordinates of all
child shapes relative to the outer container. The easiest way to do that is
in the layout feature that might be triggered from e.g. the delete or remove
feature that removes children. Maybe teh above mentioned example gives you
an idea how to do that.

Michael
Re: Nesting Shapes etc... [message #989432 is a reply to message #989430] Thu, 06 December 2012 08:27 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi Michael,

thanks for your help.

Did you have the time to look through the code I posted?
Is this, in general, the intended way to code nestes shapes?

Edit: The code of the quoted example of a UML ClassDiagram is not available by any chance?

To your answer:

Is there any further information about IdPattern?
I'm probably going to use Patterns, so I might as well have a look at IdPatterns..?!

Cheers,
Tim

[Updated on: Thu, 06 December 2012 08:28]

Report message to a moderator

Re: Nesting Shapes etc... [message #989512 is a reply to message #989310] Thu, 06 December 2012 14:46 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi again,

so I had a look at the FileSystemExample and it is actually really helpful. Thanks.

But in the meantime some more questions raised:

1: Will the IdPattern be part of the next release?

2: I've got an implementation of a classdiagram class. At the moment with the following structure:

- container : rectangle
-- shape : polyline
-- shape : text (for classname)
-- container : rectangle (for attributes)
--- shapes : text (for each attribute)

At the moment I'm struggling to update everything correctly.
I'm trying to update the attribute container by deleting all shapes and creating new ones for every attribute.

Is there only one possible to "empty" a container? i.e. deleting all current shapes?
Namely:
	Shape[] toDelete = oldAttributeCon.getChildren().toArray(
		       new Shape[oldAttributeCon.getChildren().size()]);
	for (Shape child : toDelete) {
		EcoreUtil.delete(child, true);
	}	

Meaning that I have to use EcoreUtil.delete to delete shapes?

Or is there any other possibility?

[Updated on: Thu, 06 December 2012 14:59]

Report message to a moderator

Re: Nesting Shapes etc... [message #989702 is a reply to message #989432] Fri, 07 December 2012 11:10 Go to previous messageGo to next message
Michael Wenz is currently offline Michael Wenz
Messages: 1618
Registered: July 2009
Location: Walldorf, Germany
Senior Member
Hi Tim,

I browsed through the coding and it looks good at first glance. If that
works for you I think it's a good way to solve that. In principle there's
not the one-and-only way to implement such things in Graphiti. There's
always options you might follow, depending on the concrete scenario...

Currently, as the IdPattern is still very new, there is not that much info
available on it. There's a short section in the Eclipse help system (in the
section under patterns) and the example I already mentioned. If some piece
of information you are looking for is missing, just let us know.

Michael
Re: Nesting Shapes etc... [message #989704 is a reply to message #989702] Fri, 07 December 2012 11:17 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi Michael,

thanks for your help again.

I'm building a prototype editor at the moment. After this I'll have a look at patterns, because I probably want to use them later...

I like the idea of having all necessary information in one class and be able to use inheritance...

If some information is missing, I'll post again. Thanks.

Cheers,
Tim
Re: Nesting Shapes etc... [message #989706 is a reply to message #989512] Fri, 07 December 2012 11:16 Go to previous messageGo to next message
Michael Wenz is currently offline Michael Wenz
Messages: 1618
Registered: July 2009
Location: Walldorf, Germany
Senior Member
Tim,

IdPattern will be part of our 0.10.0 release for Eclipse Kepler (next June).
Note that as it is very new and marked as experimental, there might still be
some incompatible changes to the APIs there.

Only emptying the collection of child shapes will not be sufficient: without
actually deleting them they will still exist (but not belong to any EMF
hierarchy any more) and EMF will try to persist them on the next save; that
will fail because those shapes will not belong to any resource.

Michael
Re: Nesting Shapes etc... [message #989707 is a reply to message #989706] Fri, 07 December 2012 11:22 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
At the moment my code does the following:

-- find the container (containing all "attribute"-shapes)
-- delete the whole container
-- create a new container
-- add a shape for every attribute in the class

It seems to work fine, even though I'm still not sure if this is the best way of doing it Smile
Re: Nesting Shapes etc... [message #989710 is a reply to message #989707] Fri, 07 December 2012 11:26 Go to previous messageGo to next message
Michael Wenz is currently offline Michael Wenz
Messages: 1618
Registered: July 2009
Location: Walldorf, Germany
Senior Member
That surely involves some overhead, but is probably the easiest way to
handle it. For very large diagrams you might run into performance issues,
but that might not become relevant for your case.

Michael
Re: Nesting Shapes etc... [message #989712 is a reply to message #989710] Fri, 07 December 2012 11:34 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
To be honest, I don't really know how big the diagrams will be, but I don't want to run into those issues.

What would be a better way with regard to performance?

A alternative solution I was thinking of was:

-- find container
-- compare number of shapes with number of attributes
-- compare all shape-texts with attribute names
-- if not equal, change the text accordingly
-- if #shapes was less than #attributes: add shapes with text of attributes
-- if #shapes was more than #attributes: delete remaining shapes

In my opinion the overhead was comparably high....
Re: Nesting Shapes etc... [message #989731 is a reply to message #989712] Fri, 07 December 2012 13:06 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
I'm sorry for bothering you again, but I ran into something and just want to ask if this is intended.

It's about my Class implementation again:
Class
- name
- attributes
- methods (not implemented yet)

To ease the access of the different containers and shapes, I just created my own ClassContainerShape by extending ContainerShapeImpl.

Now I get the warning:

Discouraged access: The type ContainerShapeImpl is not accessible due to restriction on required library G:\...\eclipse-juno-graphiti\plugins\org.eclipse.graphiti.mm_0.10.0.v20121130-1412.jar

So it is not intended, that developers build their own Shape implementations by extended the current ones?

Cheers,
Tim

Edit: delete personal information Smile

PS: If I should open extra topic in this forum for not-really-topic-related questions like this, just let me know, and I'll do it.

[Updated on: Thu, 13 December 2012 15:50]

Report message to a moderator

Re: Nesting Shapes etc... [message #990038 is a reply to message #989712] Mon, 10 December 2012 16:06 Go to previous messageGo to next message
Michael Wenz is currently offline Michael Wenz
Messages: 1618
Registered: July 2009
Location: Walldorf, Germany
Senior Member
Yes, that would be roughly the algorithm. And yes, you will have to weight
that overhead with the overhead of redrawing unchanged shapes. Highly
depends on the scenario...

Michael
Re: Nesting Shapes etc... [message #990040 is a reply to message #989731] Mon, 10 December 2012 16:09 Go to previous messageGo to next message
Michael Wenz is currently offline Michael Wenz
Messages: 1618
Registered: July 2009
Location: Walldorf, Germany
Senior Member
Tim,

yes, it is not intended to subclass the Graphiti Shapes. Not sure what
exactly you are using it for, but in case it is only for easier identifying
them, we recommend to use properties to tag them or the linking mechanism.

Michael
Re: Nesting Shapes etc... [message #990058 is a reply to message #990040] Mon, 10 December 2012 16:45 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi Michael,

first of all, thanks for your help, again.

The purpose of my extension is the following:

My Class representation implementation is the following:

- container : rectangle
-- shape : polyline
-- shape : text (for classname)
-- container : rectangle (for attributes)
--- shapes : text (for each attribute)
-- container : rectangle (for methods)
--- shapes : text (for each method)

Now the my idea was to implement my own Shape to make access easier.

I use methods like:

ContainerShape getFirstContainerShape()
ContainerShape setFirstContainerShape()
public Shape getNameShape()
public String getName()
public void setName(String name)
...etc...


This makes life a lot easier (at least in my opinion), so that I don't
have to navigate trough all the Container and children everytime, when
writing the different Features (like update, layout, add).

I just had a quick look at the Properties, but the documentation
is more like
* If the meaning of the ... isn't clear,
* there really should be more of a description here...


which makes it kind of difficult to understand how to use them properly Smile

Cheers,
Tim

[Updated on: Mon, 10 December 2012 16:52]

Report message to a moderator

Re: Nesting Shapes etc... [message #990281 is a reply to message #990058] Tue, 11 December 2012 17:20 Go to previous messageGo to next message
Aljoscha Hark is currently offline Aljoscha Hark
Messages: 24
Registered: March 2012
Junior Member
Hello Tim,

I avoided the ContainerShapeImpl-Inheritance "issue" by creating a somehow "helper" class for each neccessary BO, which delegates to the right PE/GA.

To be concrete: class ClassObjectUtil could deliver methods to get to a specific PE or GA. These delegations would have only be maintained inside the *AddFeature and the *Util class.

E.g.: text GA of a class:

// ClassAddFeature:
Text nameText = Graphiti.getGaService().createText(pe);

// ClassUpdateFeature:
Text nameText = ClassUtil.getText(pe);

// ClassUtil.getText(PictogramElement pe)
return pe.getGraphicAlgorithm().getGAChildren().get(0);

<edit>
Quote:
- container : rectangle
-- shape : polyline
-- shape : text (for classname)
-- container : rectangle (for attributes)
--- shapes : text (for each attribute)
-- container : rectangle (for methods)
--- shapes : text (for each method)


in this case "nameText" would be "((ContainerShape)pe).getChildren().get(1).getGA()" in the util class.
</edit>


Hope it helps,
Aljoscha

[Updated on: Tue, 11 December 2012 17:25]

Report message to a moderator

Re: Nesting Shapes etc... [message #990282 is a reply to message #990281] Tue, 11 December 2012 17:23 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi,

thanks for your help. This is the alternative solution I already programmed in order to avoid extending the Shape-implementations.

Still the "properties"-solution sounds interesting, but I don't really see how they can help Smile
Re: Nesting Shapes etc... [message #990285 is a reply to message #990282] Tue, 11 December 2012 17:33 Go to previous messageGo to next message
Aljoscha Hark is currently offline Aljoscha Hark
Messages: 24
Registered: March 2012
Junior Member
Hi,

regarding the "properties"-solution: I tried it some time ago...

The advantage: you are "independent" of your shape/GA structure (when properly implemented) - as long as all PEs/GAs exist on a proper context.

Otherwise you will could get into strange issues when you change the hierarchy - e.g. possibly you rely on some of the structure anyway and update your parent in some situation: Although you got the correct "Rectangle" after a structural change in feature A- other features COUlD possibly misbehave because the parent/child structure has changed.

So: as Michael said - it depends on the application Smile

Greets,
Aljoscha
Re: Nesting Shapes etc... [message #990288 is a reply to message #990285] Tue, 11 December 2012 17:44 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi,

thanks for your hints. At the moment I don't really know how to start on the property-alternative. I'll have a closer look at it this week.

Cheers,
Tim
Re: Nesting Shapes etc... [message #990335 is a reply to message #990288] Tue, 11 December 2012 23:07 Go to previous messageGo to next message
Hallvard Traetteberg is currently offline Hallvard Traetteberg
Messages: 601
Registered: July 2009
Location: Trondheim, Norway
Senior Member
On 11.12.12 09.44, Tim E. wrote:
> Hi,
>
> thanks for your hints. At the moment I don't really know how to start on
> the property-alternative. I'll have a closer look at it this week.

Something like:

interface PEProperty<PE> {
PE get(Shape context);
}

abstract class PEPropertyImpl<PE> {
private Class<PE> peClass;
protected PEPropertyImpl(Class<PE> peClass) {
this.peClass = peClass;
}
public PE get(Shape context) {
PictoralElement pe = <<search for a contained PE of type peClass>>
return (PE) pe;
}
}

In the feature class:
public static IPEProperty<Text> MY_LABEL_PROPERTY = new
PropertyImpl<AbstractText>(Text.class);

Then use myLabelProperty to get the label: AbstractText myLabel =
MY_LABEL_PROPERTY.get(parentShape);

Hallvard
Re: Nesting Shapes etc... [message #990378 is a reply to message #990335] Wed, 12 December 2012 08:47 Go to previous messageGo to next message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Thanks a lot! This was really helpful!
Re: Nesting Shapes etc... [message #990691 is a reply to message #990281] Thu, 13 December 2012 19:59 Go to previous message
Tim E. is currently offline Tim E.
Messages: 56
Registered: November 2012
Member
Hi,

I finally found the time today, to improve my code.
I tried to implement more parts of ClassUtil idea.

But I ran into a strange problem, I don't understand.

The basic idea is, as Aljoscha described, a util class for navigating through the
(Container)-Shapes of a created PE.

This util class is used in the Layout- and UpdateFeature.

Unfortunately, updateNeeded is called on a object, that has not finished its creation.
To understand what I mean here some code:

Structure of ClassObject (my business object)
- container : rectangle
-- shape : polyline
-- shape : text (for classname)
-- container : rectangle (for attributes)
--- shapes : text (for each attribute)


ClassObjectUtil:
public ClassObjectUtil(ContainerShape classContainer) {
		this.classContainer = classContainer;

		// set all other shapes we know
		for (Shape child : classContainer.getChildren()) {
			if (child instanceof ContainerShape) {
				attributeContainer = (ContainerShape) child;
			} else if (child instanceof Shape) {
				// could be name or line
				GraphicsAlgorithm ga = child.getGraphicsAlgorithm();
				if (ga instanceof Text) {
					classNameShape = child;
				} else if (ga instanceof Polyline) {
					line = (Polyline) ga;
				}
			}
		}
	}

	public String getClassName() {
		return ((Text) classNameShape.getGraphicsAlgorithm()).getValue();
	}

...
...
...


According to my AddFeature, every created ClassObject has an according structure.
But at some point updateNeeded is called on a ContainerShape with a ClassObject as businessObject that does not have "finished" its creation?! At least the Shape (GA: Text) for the ClassObject name is "null" and the attribute ContainerShape is also "null". In fact, there aren't any children.

So of course the program ends up with a NPE when the updateNeeded method calls getClassName(...) of the ClassObjectUtil class.

Just for the record: canUpdate is not called at all up to this point, but I'm not sure, if this should be the case.

Do I miss something? Did I do something wrong?

Thanks in advance,
Tim

PS: A possible work around is to check in updateNeeded if the passed PE has children and if not, to return falseReason(), but this seems odd to me.

[Updated on: Thu, 13 December 2012 20:07]

Report message to a moderator

Previous Topic:Automatic refresh of the diagram editor at runtime
Next Topic:Scrollbar
Goto Forum:
  


Current Time: Fri Oct 24 13:37:21 GMT 2014

Powered by FUDForum. Page generated in 0.02717 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software