Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » BPMN 2.0 Modeler » Dynamically update the layout of a customTaskElement
Dynamically update the layout of a customTaskElement [message #1733703] Tue, 31 May 2016 07:26 Go to next message
Ralph Soika is currently offline Ralph SoikaFriend
Messages: 192
Registered: July 2009
Senior Member
Hi Bob,

I still did not success to customize the layout of a custom Task or Event element dynamically. I saw that the discussion raised up again but we did not have an example in our wiki yet.

What I want solve is that the color or the icon of a custom task element changes in dependency of a property from the related business object dynamically.

In case of the addFeature method this is quite straightforward and we explained this also in our wiki.

@Override
public IAddFeature getAddFeature(IFeatureProvider fp) {
	return new AddTaskFeature(fp) {

		@SuppressWarnings("unused")
		@Override
		protected void decorateShape(IAddContext context,
				ContainerShape containerShape, Task businessObject) {
			super.decorateShape(context, containerShape,
					businessObject);
			IGaService gaService = Graphiti.getGaService();
			// set background color
			setFillColor(containerShape);
		}
	};
}



But to update the layout during a property change I guess the getUpdateFeature() method need to be implemented. But this seems to be much more complex as the getAddFeature() method. For my understanding an implementation should look something like this:

@Override
public IUpdateFeature getUpdateFeature(IFeatureProvider fp) {
	MultiUpdateFeature multiUpdate = new MultiUpdateFeature(fp);
	multiUpdate.addFeature(new  AbstractBpmn2UpdateFeature(fp) {
		
		@Override
		public boolean update(IUpdateContext context) {
			// set background color
			DIUtils.updateDIShape(context.getPictogramElement());
			setFillColor((ContainerShape)context.getPictogramElement());
			// what to return here???
			return false;
		}

		@Override
		public IReason updateNeeded(IUpdateContext context) {
			IReason reason = super.updateNeeded(context);
			if (reason.toBoolean())
				return reason;

			// how to send the correct Reson object??

			// if (property changed...) {
			//	return Reason.createTrueReason("evalute property changed");
			// }
			return Reason.createFalseReason("");
		}

		@Override
		public boolean canUpdate(IUpdateContext arg0) {
			// what do to here?
			return true;
		}
	});
	
	return multiUpdate;
}



But what ever I try I got a 'red dashed line' indicating that Graphiti is not happy with my update.
I fear that the problem begins in the construction of the MultiUpdateFeature class. I am not sure what class I should take to extend from.

Can you please give me some example code? In return, I will update our wiki Smile
Re: Dynamically update the layout of a customTaskElement [message #1733758 is a reply to message #1733703] Tue, 31 May 2016 13:09 Go to previous messageGo to next message
Ralph Soika is currently offline Ralph SoikaFriend
Messages: 192
Registered: July 2009
Senior Member
I figured out, that the important part is to store the current layout state of the pictogramm. This is because the method updateNeeded() will be triggered several times.
Each time we need to check if our layout was already updated. This can be done by storing a kind of 'session' state in the pictorgrammElement during the update() method by calling the helper method setPropertyValue:

FeatureSupport.setPropertyValue(containerShape, "evaluate.property", propertyValue);


This 'session state' need to be evaluted in the method updateNeeded() to signal when the update was performed.

Simplified the code should look like this:

@Override
 public IUpdateFeature getUpdateFeature(IFeatureProvider fp) {
		MultiUpdateFeature multiUpdate = (MultiUpdateFeature) super.getUpdateFeature(fp);
		multiUpdate.addFeature(new  AbstractUpdateBaseElementFeature<Task>(fp) {
			
			@Override
			public boolean update(IUpdateContext context) {		
				// update layout
				// .... to some graphic stuff... 
				
				// finally update pictorgrammElement state (IMPORTANT!)
				FeatureSupport.setPropertyValue((ContainerShape)context.getPictogramElement(), "evaluate.property", myState);
				return true;
			}
		

			@Override
			public IReason updateNeeded(IUpdateContext context) {
				IReason reason = super.updateNeeded(context);
				if (reason.toBoolean())
					return reason;
				
				
				// test state stored in the pictorgramElement...
				PictogramElement pe = context.getPictogramElement();
				String myState = FeatureSupport.getPropertyValue(pe, "evaluate.property");
				
				//....
				
				// compate pictorgram state with current BusinessObject
				BaseElement ta = (BaseElement) getBusinessObjectForPictogramElement(pe);
				// ...
				
				// update is needed if the property has changed....
				if (Boolean.parseBoolean(myState) != newState)
					return Reason.createTrueReason("evaluate.property changed");
				return Reason.createFalseReason("");
				}
			});
		
		return multiUpdate;
	}


Then the update method will complete correctly and the red dotted line is no longer shown.
Re: Dynamically update the layout of a customTaskElement [message #1733851 is a reply to message #1733758] Wed, 01 June 2016 13:24 Go to previous messageGo to next message
Robert Brodt is currently offline Robert BrodtFriend
Messages: 811
Registered: August 2010
Location: Colorado Springs, CO
Senior Member

Hi Ralph,

Yes, this is exactly right. The reason Graphiti is showing that red dotted line border is because your UpdateFeature.updateNeeded() is still returning true even after Graphiti has called UpdateFeature.update(). After the update call, your updateNeeded should be false.
Re: Dynamically update the layout of a customTaskElement [message #1734011 is a reply to message #1733851] Fri, 03 June 2016 01:27 Go to previous messageGo to next message
Delacyr Ferreira is currently offline Delacyr FerreiraFriend
Messages: 20
Registered: February 2016
Junior Member
I don't know what I'm doing wrong. My elements are still getting red dashed border. Can you see what's happening?

http://i.imgur.com/wgaa633.png

public abstract class AbstractActivityFeatureContainer extends BaseElementFeatureContainer {

	@Override
	public IUpdateFeature getUpdateFeature(IFeatureProvider fp) {
		UpdateActivityCompensateMarkerFeature compensateMarkerUpdateFeature = new UpdateActivityCompensateMarkerFeature(fp);
		UpdateActivityLoopAndMultiInstanceMarkerFeature loopAndMultiInstanceUpdateFeature = new UpdateActivityLoopAndMultiInstanceMarkerFeature(fp);
		MultiUpdateFeature multiUpdate = new MultiUpdateFeature(fp);
	multiUpdate.addUpdateFeature(compensateMarkerUpdateFeature);
	multiUpdate.addUpdateFeature(loopAndMultiInstanceUpdateFeature);
		AbstractUpdateBaseElementFeature nameUpdateFeature = new AbstractUpdateBaseElementFeature(fp) {
			
			@Override
			public boolean canUpdate(IUpdateContext context) {
				Object bo = getBusinessObjectForPictogramElement(context.getPictogramElement());
				return bo != null && bo instanceof BaseElement && canApplyTo((BaseElement) bo);
			}
			
			@Override
			public boolean update(IUpdateContext context) {		
				// update layout
				// .... to some graphic stuff... 
				
				ContainerShape containerShape = (ContainerShape)context.getPictogramElement();
				Shape shape = containerShape.getChildren().get(0);
				
				Activity variant = (Activity)getBusinessObjectForPictogramElement(context.getPictogramElement());
				BaseElement baseElement = BusinessObjectUtil.getFirstBaseElement(containerShape);
				if (variant!=null && variant.isCheck()) {
					ShapeStyle ss = new ShapeStyle();
					ss.setDefaultColors(IColorConstant.LIGHT_GREEN);
					StyleUtil.applyStyle(shape.getGraphicsAlgorithm(), baseElement, ss);
				}
				
				String myState = null;
				// finally update pictorgrammElement state (IMPORTANT!)
				FeatureSupport.setPropertyValue((ContainerShape)context.getPictogramElement(), "evaluate.property", myState);
				return true;
			}
		

			@Override
			public IReason updateNeeded(IUpdateContext context) {
				IReason reason = super.updateNeeded(context);
				if (reason.toBoolean())
					return reason;
				
				// test state stored in the pictorgramElement...
				PictogramElement pe = context.getPictogramElement();
				String myState = FeatureSupport.getPropertyValue(pe, "evaluate.property");
				if (myState==null || myState.isEmpty())
					myState = "false";
				
				// compare the pictogram state with current BusinessObject
 				Activity variant = (Activity) getBusinessObjectForPictogramElement(pe);
 				Boolean newState;
                               //if was selected to be colored or not
 				if (!variant.isCheck())
 					newState = false;
 				else
 					newState = true;

 				// update is needed if the property has changed....
 				if (Boolean.parseBoolean(myState) != newState){
 					// indicate a reason to update the shape
 					return Reason.createTrueReason("evaluate.property changed");
 				}
 				
 				// otherwise no reason to update the element now.
 				return Reason.createFalseReason("");
			}
			
		};
		multiUpdate.addUpdateFeature(nameUpdateFeature);
		return multiUpdate;
	}
}
Re: Dynamically update the layout of a customTaskElement [message #1734094 is a reply to message #1734011] Fri, 03 June 2016 17:07 Go to previous messageGo to next message
Ralph Soika is currently offline Ralph SoikaFriend
Messages: 192
Registered: July 2009
Senior Member
Yes I am still puzzled how implement this in the right way.
In my case the problem is that I did not know how to force the editor to refresh my custom Task element. I guess you have the same problem.

I have implemented an adapter class for notifyChanged events when the properties of my business object have changed. In this situation I call

getDiagramEditor().getDiagramBehavior().refresh();


This call will trigger the updateNeeded() method of my AbstractUpdateBaseElementFeature implementation. I recognize the property change there and return a Reason.createTrueReason("evaluate.property changed");

But the problem is, that the update() method itself will never be called. The only thing I got is the red dashed border. When I move the element inside the diagram the layout is refreshed the right way.

@Bob: can you give a hint how to force the DiagramEditor to update a specific customShape?

===
Ralph
Re: Dynamically update the layout of a customTaskElement [message #1734107 is a reply to message #1734094] Fri, 03 June 2016 21:39 Go to previous messageGo to next message
Ralph Soika is currently offline Ralph SoikaFriend
Messages: 192
Registered: July 2009
Senior Member
Finally it looks that I found the solution for the problem in my case:
The trick seems to be calling the update() method for the corresponding iUpdateFeature.

In my scenario I implemented a notifyChange listener to react on a change of a specific property inside my model.
Inside the method notifyChanged() I was able to figure out the corresponding iUpdateFeature with the help of the getSelectedPictogramElements() method of the DiagramEditor.

So my code (which is in my case part of a DefaultPropertySection) looks now like this :

// get corresponding EObject object from my BaseElement and add an adapter...
myModelValue.eAdapters().add(new AdapterImpl() {
	public void notifyChanged(Notification notification) {
		int type = notification.getEventType();

		if (type == Notification.SET) {
			Object newValue = notification.getNewValue();
			if (newValue != null) {
				System.out.println("notify type=SET   value=" + newValue);

				PictogramElement pe = null;
				PictogramElement[] pictogramElementList = getDiagramEditor().getSelectedPictogramElements();
				if (pictogramElementList != null && pictogramElementList.length > 0) {
					pe = pictogramElementList[0];
					UpdateContext updateContext = new UpdateContext(pe);
					IUpdateFeature iUpdateFeature = getDiagramEditor().getDiagramTypeProvider()
							.getFeatureProvider().getUpdateFeature(updateContext);
					if (iUpdateFeature.canUpdate(updateContext))
						iUpdateFeature.update(updateContext);
				}

			}
		}
	}
});


Now the updateNeeded() method and the update method() of my AbstractUpdateBaseElementFeature implementation are triggered exactly in the expected way.
Re: Dynamically update the layout of a customTaskElement [message #1734131 is a reply to message #1734107] Sat, 04 June 2016 15:51 Go to previous messageGo to next message
Delacyr Ferreira is currently offline Delacyr FerreiraFriend
Messages: 20
Registered: February 2016
Junior Member
So, if you close the diagram and open again, you wouldn't lose the new color shape?

Update: Answering the question above. I added some graphic stuff at org.eclipse.bpmn2.modeler.ui.features.activity.task.TaskFeatureContainer.getAddFeature(...).new AddTaskFeature() {...}.decorateShape(IAddContext, ContainerShape, Task) to solve the problem.

[Updated on: Sat, 04 June 2016 16:27]

Report message to a moderator

Re: Dynamically update the layout of a customTaskElement [message #1735016 is a reply to message #1734131] Tue, 14 June 2016 16:37 Go to previous messageGo to next message
Robert Brodt is currently offline Robert BrodtFriend
Messages: 811
Registered: August 2010
Location: Colorado Springs, CO
Senior Member

Ralph, you shouldn't need to add your own adapter to the model object since the updateNeeded() and update() methods are called by way of a resourceSetChanged() handler in Graphiti's internal DomanModelChangeListener. This is true for any model change since your model should already have been added to the resource set. I don't understand why your model changes are not triggering this chain of events...
Re: Dynamically update the layout of a customTaskElement [message #1735399 is a reply to message #1735016] Sat, 18 June 2016 11:13 Go to previous messageGo to next message
Ralph Soika is currently offline Ralph SoikaFriend
Messages: 192
Registered: July 2009
Senior Member
Hi Bob,

thanks for your answer.
Ok this sounds like I did something completely wrong in my custom PropertySections. I usually override the createBindings method.
When I bind an Attribute which is simply a new custom attribute assigned to my custom task object I can use the standard method call: this.bindAttribute()

But in case I want to bind one of my extinsionElement properties this results in the unexpected behavior where no updateNeeded() method is triggered. See the following example:

@Override
public void createBindings(EObject be) {
	setTitle("Workflow");

	// ProcessID (send an updateNeeded event)
	this.bindAttribute(attributesComposite, be, "processid");

	// Summary (did not send an updateNeeded event!)
	Value itemValue = ImixsBPMNPlugin.getItemValueByName((BaseElement) be,
			"txtworkflowsummary", null, "");
	TextObjectEditor valueEditor = new TextObjectEditor(this, itemValue,
			ImixsBPMNPlugin.IMIXS_ITEMVALUE);
	valueEditor.createControl(attributesComposite, "Summary");
}


The xml result looks like this :

 <bpmn2:task id="Task_1" imixs:processid="1111" name="Task 1">
      <bpmn2:extensionElements>
        <imixs:item name="txtworkflowsummary" type="xs:string">
          <imixs:value><![CDATA[som data]]></imixs:value>
        </imixs:item>
      </bpmn2:extensionElements>


Everything looks fine and I can change the values by typing in the TextObjectEditor. And this will indicate the 'Dirty' Flag in the eclipse editor tab. And the value is stored correctyl when I save the model, and displayed correctly when I open it.
So it did not look to bad so far. But the updateNeeded method is not called during typing. This is a different behavior when I am typing the value of an attribute which is not part of the extensionElements (like imixs:processid in my example) .

The tutorial seems a little bit outdated in the meantime. Because the method ModelDecorator.addExtensionAttributeValue(myTask, feature, taskConfig, true); is deprecated.
https://wiki.eclipse.org/BPMN2-Modeler/DeveloperTutorials/CustomPropertyTabs

In my own code I use the InsertionAdapter intead to add a extensionAttribute and a custom property:

extensionAttribute = Bpmn2Factory.eINSTANCE
		.createExtensionAttributeValue();
// insert the extension into the base element
InsertionAdapter
		.add(be, Bpmn2Package.eINSTANCE
				.getBaseElement_ExtensionValues(),
				extensionAttribute);

// we need to execute to avoid the generation of empty
// extensionElements
InsertionAdapter.executeIfNeeded(extensionAttribute);

// insert the item into the extension
InsertionAdapter.add(extensionAttribute,
		ImixsBPMNPlugin.IMIXS_ITEM_FEATURE, item);


Maybe this is the source of the wrong behavior?


===
Ralph
Re: Dynamically update the layout of a customTaskElement [message #1735407 is a reply to message #1735399] Sat, 18 June 2016 14:40 Go to previous messageGo to next message
Ralph Soika is currently offline Ralph SoikaFriend
Messages: 192
Registered: July 2009
Senior Member
Hi Bob,

I debugged deeper and deeper.... and figured out the following different behavior:

An Editor created with the default bindAttribute() call,

this.bindAttribute(attributesComposite, be, "processid");


triggers at the end a method

Bpmn2DiagramTypeProvider.BPMNNotificationService.updatePictogramElements(PictogramElement[] dirtyPes)


When I manually created a TextEditor with the following code:

TextObjectEditor valueEditor = new TextObjectEditor(this, itemValue,
			ImixsBPMNPlugin.IMIXS_ITEMVALUE);
	valueEditor.createControl(attributesComposite, "Summary");


this mechanism is not triggered. But all the other behavior seems to be identically called.
I can't figure out what the BPMNNotificationService is used for.

Could it be possible that in my case I need to register such a service manually for my extension custom properties?

===
Ralph
Re: Dynamically update the layout of a customTaskElement [message #1735933 is a reply to message #1735407] Thu, 23 June 2016 17:30 Go to previous message
Robert Brodt is currently offline Robert BrodtFriend
Messages: 811
Registered: August 2010
Location: Colorado Springs, CO
Senior Member

Hi Ralph,

I don't know if you've figured this out yet, and I'm not really sure what the problem could be with update() but here's a bit of info that might help:

All DetailComposites register as ResourceSetListeners (see ListAndDetailCompositeBase#setBusinessObject()). If a model change happens somewhere (either on the canvas, or in a different tab or widget or view), the DetailComposite passes the notification on to all of its children, which are subclasses of ObjectEditors (see ListAndDetailCompositeBase#resourceSetChanged()) These are responsible for checking if the change affects them and redrawing themselves. For example, if you add a new Message type to the model, the combo boxes that are used to select a Message are updated to include the new Message. This is also where the label decorators (ERROR or WARNING icons next to the label of an ObjectEditor) are updated (see ObjectEditor#updateLabelDecorator()).

Is it possible that somewhere along the line, your ObjectEditor isn't updating itself in response to a model change?

HTH,
Bob
Previous Topic:How to place a second icon into a CustomShapeFeatureContainer
Next Topic:Using BPMN2 Model in maven
Goto Forum:
  


Current Time: Fri Apr 26 05:09:29 GMT 2024

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

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

Back to the top