Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

GMF Propsheet Customization

Note, the tutorial is completely obsolete for GMF 2.1 stream. You should better avoid getting used to the way it generates property sheets.

How-to customize property sheet for your gmf editor.

In org.eclipse.gmf.gmfgraph.editor we have customized the property sheet supplied by this editor by customizing gmf.genmodel templates to work with our joint propsheet.ecore metamodel. This actually provided a framework to customize your property sheets. So customizing templates together with providing a joint metamodel for the gmfgen is actually a good example of customizing gmf for making frameworks for it!

But now let’s walk through all the steps needed for using our resulted propsheet framework.

We assume that at the moment you have your specific metamodel, described in .ecore, and have already generated your DSL editor, provided by GMF. And now you want to customize the property sheet for your editor with our propsheet framework.

So how does it work?

We can generate most of the code needed, if you reuse customized templates from org.eclipse.gmf.gmfgraph.editor. More precisely, you need only PropertySection.xpt (along with Utils.ext). If you have a look inside, you will notice, that all the template code is set up to use PropSheet metamodel, and its custom code is target metamodel independent. You will also notice, that for all of the general functionality this class org.eclipse.gmf.graphdef.editor.sheet.AbstractCustomSectionParent (it is located in “src-extra” source folder of the project). You can have a glance at it to see the graphical services available. I have mentioned, that this template produces “most” of the code, so actually we still need some NOT generated parts in complicated cases of operating metamodel, so one of the 2 tabs used in our gmfgraph.editor contains such parts (see org.eclipse.gmf.graphdef.editor.sheet.GeneratedLayoutPropertySection).

The main feature of the template engine, which have made it possible to have a framework, is xpand automatic searching for referenced objects within the same resource set. In our graphical description of propsheet widgets we only reference gmf Custom Property Tabs, and gmfgen itself knows nothing of our extension.

Let’s give it a try.

Small tutorial. Part 0. Setup.

  • Using Help -> Software Updates -> Find and Install... -> Search for new features to install -> Europa Discovery Site, Finish. Find Eclipse CVS Client and install it to the current dev platform (restart required).
  • In CVS Repositories view add new location with Host=dev.eclipse.org and path=/cvsroot/modeling. Then you can use pserver connection type with anonymous user and blank password.

Small tutorial. Part 0. Preconfigure.

  • Locate Dates group under the /cvsroot/modeling repository location in the CVS Repositories view and choose New -> Date Tag.... Add, for example, September, 08, 2007 (it is the date of 2.0.1 release) tag (other dates prior to February, 22, 2008, will do as well). Under the date you have just set expand org.eclipse.gmf/plugins/ and select 2 ones: org.eclipse.gmf.graphdef.editor and org.eclipse.gmf.codegen, check them out (the latter is needed to avoid template compile errors).
  • [Trick!] : In order to avoid complains from editor without generating unnecessary code for the propsheet model, you should slightly finetune graphdef.gmfgen first:

Find graphdef.gmfgen in models folder of org.eclipse.gmf.graphdef.editor plugin, and Open With -> Text Editor. You will see the following xml header (line numbers are added):


0.<?xml version="1.0" encoding="UTF-8"?>
1.<xmi:XMI xmi:version="2.0"
2.    xmlns:xmi="http://www.omg.org/XMI"
3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4.    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel"
5.    xmlns:gmfgen="http://www.eclipse.org/gmf/2006/GenModel"
6.    xmlns:propsheet="http://www.eclipse.org/gmf/2007/GmfGraph/PropSheet">
7.  <gmfgen:GenEditorGenerator
8.      packageNamePrefix="org.eclipse.gmf.graphdef.editor"........

Now you should add one magic line between the 5th and 6th line, so that resulting file looked like:


0.<?xml version="1.0" encoding="UTF-8"?>
1.<xmi:XMI xmi:version="2.0"
2.    xmlns:xmi="http://www.omg.org/XMI"
3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4.    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel"
5.    xmlns:gmfgen="http://www.eclipse.org/gmf/2006/GenModel"
->    xsi:schemaLocation="http://www.eclipse.org/gmf/2007/GmfGraph/PropSheet propsheet.ecore"
6.    xmlns:propsheet="http://www.eclipse.org/gmf/2007/GmfGraph/PropSheet">
7.  <gmfgen:GenEditorGenerator
8.      packageNamePrefix="org.eclipse.gmf.graphdef.editor"........

Now you are ready to proceed with the tutorial. Select the graphdef.gmfgen you have just changed and Open With -> GMFGen Model Editor to start. Nevertheless, this approach has some limitations. For instance, it won't allow you to chose EObject references for its instance. That is why the recommended way is still complete codegeneration of propsheet model. See the hint right below.

[Hint] If you do not like to see modification markers, there is an alternate way of what we have just done to avoid errors from gmfgen editor. All we provide is a simple scratch of model. To be able to use it, create and edit elements from it, you need to generate EMF model and editor for your developer platform. To do this, open the propsheet.genmodel, that should exist in the models folder of org.eclipse.gmf.graphdef.editor plugin, and, using Generate Model Code main element context action, generate the org.eclipse.gmf.graphdef.propsheet plugin. After this project is generated, you can Export it as Deployable plug-ins and fragments into your current platform (or import it from the development workbench, if you are currently using target one for work). And do not forget to restart Eclipse workbench afterwards.

Small tutorial. Part 1. Introduction.

As one of the most simple, but useful examples, let’s provide users with a way to specify names for figures at last. This functionality was lost when we have switched to customized properties presentation. Since there could be many reasons for Which custom tab should we choose to meaningfully add this text field, let’s simply make a new one. As usual, open the gmf generation model from models/graphdef.gmfgen, and add one more Custom Property Tab child for Property Sheet node (these nodes are part of the common gmfgen description, so you can find them under Gen Editor Generator customization root, as always).

NewCustomPropertyTab.png

I have given it “genLabel” id and chosen “Name” for its label. I have also changed the implementation class name a little (just to make it prettier), and now it is going to be “GeneratedNamePropertySection”.

Now we need to decide, which objects does it make sense to set name for – and this actually means tiding our property with the metamodel, specifying the blackbox from which the value should be initially taken, and where it should be saved afterwards. Actually, with the normal development flow, you always think of this first, because the metamodel already exists and its properties are already known, and now you are just thinking of how to show them in widgets. So, looking to our domain gmfgraph.ecore metamodel we can notice, that every org.eclipse.gmf.gmfgraph.Identity object can have a name in metamodel, so that tells us of what filter should we add to show our tab: we want the tab to be shown for Identities. OK, let’s add New Child to our Custom Property Tab named Name of type Custom filter, and make it point to the org.eclipse.jface.viewers.IFilter class which would check selection’s type. There are filters for other custom tabs already implemented within org.eclipse.gmf.graphdef.editor.part.PropertySectionFilters (being the custom implementation class, it could be found in src-extra folder as well), and so all we need is making some new static nested class with similar code. Coping ShapeFilter and replacing Shape with Identity works fine for me, so let’s do it.


public static class IdentityFilter implements IFilter {
	public boolean select(Object toTest) {
		Object transformed = PropertySectionFilters.transformSelection(toTest);
		return transformed instanceof Identity;
	}
}


Therefore, we can now set PropertySectionFilters$IdentityFilter as the Implementation class of that added custom filter for our tab. That’s it with gmfgen customization itself.

Now let’s go into our propsheet framework. If you have checked the latest graphdef.gmfgen, you should find a second root node, Property Section Container. This is our model to let you describe custom widgets to fill your referenced tab and access features from your domain metamodel.

Note : Initially your own gmfgen model will have no Property Section Container second root. To add it, you can use our tool for “Adding extension models”, available from the context menu of gmfgen. It should create that second root for you, when you locate propsheet.ecore and select PropertySectionContainer to instantiate. Note the common confusion that can happen here if you use the tool from GMF versions prior to the end of February, 20, 2008. Your GMFGen Editor could report errors after this action been made. (Nevertheless, if you open your .gmfgen with text editor, you should see new <propsheet:PropertySectionContainer> element at the end.) Errors arise because the editor knows nothing of how to show and how to edit instances of the metamodel being linked. It indicates that your workbench instance could not find model for nsUri 'http://www.eclipse.org/gmf/2007/GmfGraph/PropSheet'. You can overcome it the same way we have just made it for the graphdef in the [Trick!], by adding xsi:schemaLocation attribute with this URI followed by relative path to propsheet.ecore model file to the root XMI element. Or you can generate code for propsheet.genmodel, which, in turn, exposes its URI with the help of platform extension. Generated plugin with code and extension in this case should be installed into the platform you are working in (you can make sure of it by observing it in the Plug-in Registry view).

So now locate that second Property Section Container root, and add one more Custom Property Section child node. You can set the name for your section, if you like (I have used Generated Name, similar to other tabs). There is one main thing is to be done next. You should specify our Custom Property Tab with “Name” name, created above, as it’s Gen Tab. Thus you make a reference for genmodel entity, which will let our template customization know that the class, generated by that entity, should use our joint propsheet description instead of a plain table one.

NewCustomPropertySection.png

GenTabForNewCustomPropertySection.png

There is still one hack left, so you have to make one more custom tuning, unfortunately. This thing is going to be removed in the nearest future, but at the moment (GMF2.0M6/RC0) you can’t go without it. So, please, open templates/aspects/xpt/propsheet/Utils.ext extension file and add the ID of your Custom Property Tab (as far as I remember, I have used “genLabel” for our “Name” one) to the list of custom tabs IDs in the isGeneratedTab method at line 27. Therefore, the isGeneratedTab should look like this:


boolean isGeneratedTab(gmfgen::GenCustomPropertyTab tab) :
"genLayout" == tab.iD || "genStyles" == tab.iD || "genLabel" == tab.iD
;


Well, that’s all with the difficulties. Now only fun is left. To our “Generated NameCustom Property Section we can add graphical widgets to make it look as interesting as your imagination can make it up. So let’s play with it a little. I have added Group widget with the TextField (Note, not the plain “Text” one!) widget inside. For convenience, I have given them names, you can do this if you like, too. Besides, if you want to provide some friendly label for your users, you should add a Text element, with the help of which you will be able to turn the label completely off, or providing some custom extended label, that couldn’t be pulled out of the domain metamodel. I have added such Text child for my Group added, and specified “Identity Parameters”.

GroupAndTextFieldWidgets.png

Well, for now I think that there is enough graphics, and we can go into taking care of interaction with model. At the moment our widgets know nothing about the source from where they could take initial values or save modified ones. So there is not too much use in the code being generated so long. And it is time to breath some life into them.

We have a Text Field, so it is naturally to suppose that it’s value is provided by some metamodel feature of EString type. To use this feature to be source of values you should simply add String Value Model Element to the widget and locate your feature from the “Feature” list in the property sheet. For our case I have added String Value to the Name Text Field, and chosen “name : EStringFeature for it.

TextFieldStringValueModelElement.png

Secondly, let’s tell templates the concrete metamodel type we are going to operate within this tab. Actually, we have already provided this information with the help of Custom filter for the Custom Property Tab Name, so this is actually a little duplication of information. But the way we have done this in a filter I like less, so it is doubtful which one is to be improved. So to tell the target metatype for the tab we should add Model Mediator to Custom Property Section or any Group element. Why should it be available for any Group element widget, you may wonder. Actually, Model Mediator element is highly used by our customized templates to provide model-operating code. Group widget is logically used in two different senses, as a graphical widget (SWT Group(expandable=false) or FormsUI ExpandableComposite (expandable=true) if it doesn’t contain explicit Text element with Create Label set to false, and plain Composite otherwise), and as well a grouping element for referenced model features. Now we need to specify the metaclass to which the structural feature we have used in String Value Model Element belongs. So we add Model Mediator element and select Identity classifier from the list available for its Cast property.

GroupModelMediatorCast.png

OK. I think that is enough for the first iteration. And it is more interesting to take a look at the result now. Let’s “Generate diagram code” and run the editor. Look! It does work indeed. :)

RuntimeEditorWithName.png

Small tutorial. Part 2. Widgets concepts.

This section describes existing widgets available within our framework to be added to the property sheet.

Layout and LayoutData.

At the moment (GMF2.0/M6/RC0) whole propsheet framework offers the only layout, FormLayout, and all widgets location can be controlled with solely one way, by tuning their child Form Layout Constraint.

Tip : To clarify yourself of how it is working you can have a look at the org.eclipse.gmf.graphdef.editor.sheet.AbstractCustomSectionParent.createFormData(Control, Control, Control, Control, boolean, boolean, boolean, boolean) method.

You can tune Form Layout Constraint with 2 major parameters: Anchor Control and Relative Position, so to display some widget to the right of another, you should add Form Layout Constraint child to it, and set that other widget as Anchor Control, with Relative Position = LEFT.

Note : If no Form Layout Constraint specified, default widget position is set to be the topmost and leftmost. So you can always omit Form Layout Constraint for the top left (often the first one) widget in the container for your convenience.

Tip : The easiest way to place your widget at the lower right corner (IV quarter) is adding 2 invisible labels with whitespaced text, the second one below the first one, and specifying that lower label as your widget’s Left Anchor Control.

Group

Group has the largest number of use cases and varieties. Group is the entity for containing other widgets, i.e. grouping them by some qualities.

Purposes for such grouping can be quite different :

  • Layout. Building complex compound layouts of control. Group acts as layout container, so all the widgets inside get a chance to specify their layoutData anchors relative to the contents of the group only.
  • Model specialization. In most cases we specify concrete feature to get and set value for our widgets, but additionally we need some mediator layer. The simplest thing it serves for is specifying metaclass this feature should belong to. So that we could generate in our code something like casting input object to that metaclass and call the feature getter/setter on that metaclass. More sense appears for that layer in little more complicated cases. Consider, for example, the case, when you would like to set or get value for the class instance, that your target instance can return (something like Points, Dimensions or LayoutDatas), but you do not want to have a separate tab for that. In such cases you should add a Group with Model Mediator child and set its Feature property to reference the feature that should return the target metaclass for all widgets inside that group could operate.

Note : There are some cases when modification listeners do not react on just changing instance fields of that contained objects, but only on changing the whole instance. In order not to lose other instance settings, you should implement EObject cloneVars(EObject target) method yourself (see GeneratedLayoutPropertySection.ModelHelper.cloneVars for an example).

The other use case of Model Mediator is making specific parts of your tab appear only for definite subclasses of the target tab one. For example, you want some parameter widget to appear only if your metaclass instance is of Editable subtype. You can get that behavior by adding Group with child Model Mediator Feature, which Cast references Editable subclass and Visible Only For Targets flag turned on, and afterwards adding your feature-modification widget to that Group.

Their visual representation can vary too .

  • SWT Group (if Expandable=false)

GroupWidget.png

  • FormsUI ExpandableComposite (if Expandable=true)

ExpandableWidget.png

  • SWT Composite (if contains Text element with Create Label=false)

Label

LabelWidget.png

Checkbox

CheckboxWidget.png

Radio

RadioWidget.png

Spin

SpinnerWidget.png

Text Field

TextFieldWidget.png

Text Area

TextAreaWidget.png

Do not forget to configure LayoutConstraints accordingly (for instance, making it, or its container Group, rightmost and lowermost)

Time

TimeWidget.png

Date

DateWidget.png

Combo

ComboWidget.png

Small tutorial. Part 3. Model elements concepts.

With the help of References in propsheet you can teach your widgets to interact with model element.

  • Model Mediator Feature. Downcasts input element to the specified type from Cast field, and passes that typed element to contained Feature References. And/Or uses its Feature reference to extract input element for its children from that feature. Of course, it makes sense only if you reference some composite type.
  • Boolean Condition – you can specify the EBoolean-typed feature from your metamodel, and Checkbox or Radio widgets get selected state on TRUE and deselected on FALSE.
  • IntValue – you can specify EInt-typed feature from your metamodel, and Spin widget shows its value and is able to increase or decrease it.
  • String Value - you can specify EString-typed feature from your metamodel, and TextField widget displays that string and sets modified value as String too.
  • Is Feature Of Type Reference – this is some not so trivial reference. It is applicable for Boolean-controlled widgets (Checkboxes and Radios)
  • Is Feature Of Kind Reference



Back to GMF Documentation

Back to the top