Login | Register
My pages Projects Community openCollabNet

argouml
Wiki: Diff for "<<Subsystem>> Property panels"

Edit this page | Links to this page | Page information | Attachments | Refresh page

 

Differences between revisions 5 and 8 (spanning 4 versions)

Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
 Located:: {{{org.argouml.core.propertypanels.module}}} in Eclipse project argouml-core-umlpropertypanels (location of the new implementation).
Line 5: Line 6:
=== Public API ===
The {{{PropPanel}}}s for the diagrams are in {{{org.argouml.uml.diagram.ui}}} and the property panels for UML objects are in {{{org.argouml.uml.ui.UML}}} path.
== Definition ==
There are a few different views for this. The main view of the objects contains all fields in the object including references to other objects.
Line 8: Line 9:
=== Receiving target change and modification events ===
Proppanels are created when their UML object becomes the target for the first time.
The Documentation and Tagged Values tabs contain fewer fields and is the same for all kinds of objects.
Line 11: Line 11:
Whenever the target changes to another type of UML object, another proppanel is
activated, which redraws itself according the target modelelement.
== The old and the new ==
There is a new implementation (since 0.30) for the main view.
Line 14: Line 14:
Whenever the target changes to another instance of the same kind of
modelelement, the (current) proppanel updates its rendering based on this new target.

Whenever the model changes for the current target, without this invoking a
target change, the proppanel updates.

Whenever the model changes for another modelelement than the current target,
usually nothing should happen - especially not for proppanels that are not the
current one - since they do not know which will be their next target anyhow.
Unless e.g. the name of the namespace of the current class changes, since this
namespace is shown on the current proppanel. In such case, the proppanel of a class is
listening to modelchange events from more than only the class itself.


=== Adding the property panel ===

Property Panels for UML model elements are found as class '''PropPanel''XXX''.java''', where '''''XXX''''' is the UML meta-class. They are in sub-packages of {{{org.argouml.uml.ui}}} corresponding to the UML package which contains the {{{''XXX''}}} metaclass in the UML specification.

So for our example we create a new class {{{PropPanelExtend}}} in package {{{org.argouml.uml.ui.behavior.use_cases}}}.

Any associated classes that do not fall into the UML classification are provided in {{{org.argouml.uml.ui}}}.

Typically the constructor for the new proppanel class invokes the parent constructor, and then builds the fields required on the property tab. The parent constructor may need an icon. If you need a new icon, a call to {{{lookupIcon()}}} should be made (note that this is a utility method of the parent {{{PropPanel}}} class). For our example we had to add '''Extend.gif'''.

You will need to make an icon, in '''.gif''' format, 16 X 16 pixels, with the transparent background color set to white. Place this file in the {{{org.argouml.Images}}} directory (it must be named like '''Name.gif'''). This icon will automatically be used in the toolbar and in the Navigation pane.

Finally the property panel must be added to the list of property panels in the {{{run()}}} method of the TabProps class, with a new call of {{{panels.put()}}}. If you don't do this, navigation listeners won't know about it!

The content of the property panel is created as a grid with columns (1 column if there are only a few fields, 2 or 3 if there are more). Each row of each column contains a caption (i.e. label) and its corresponding field.

A caption and its field may be added with one of a small number of utility methods which shield you from the layout stuff: {{{addField()}}} and {{{addSeperator()}}}.

A button may be added to the toolbar with the utility method {{{addButton()}}}.

Every field is built from Java Swing components. However these are extended by ArgoUML to help in the provision of action methods for fields in the property tab. Several fields involve lists, and these require in addition list models to compute the members of the list.

The fields that you might add to a property panel include:
 * Simple editable text. For example the Name field. Supported through the {{{UMLTextField2}}} class.
 * A drop down box (aka combobox) of options that can be selected. Supported by the {{{UMLComboBox2}}} class. Used e.g. for the type of a parameter.
 * A check box. This one does not use a seperate model class, thanks to the simplicity of the represented boolean value. Supported by the {{{UMLCheckBox2}}} class. Used e.g. for the concurrency checkbox on a composite state.
 * A radio button. These always come in a group. Supported by the {{{UMLRadioButtonPanel}}} class. Used e.g. for selecting the visibility on the properties panel of a class.
 * A list. Used e.g. for the Generalizations field on the proppanel of a class. The non-editable list is supported by the {{{UMLList2}}} class and its child {{{UMLLinkedList}}}. The latter also exists in the form of {{{UMLMutableLinkedList}}}, which allows adding, creation and deleting elements by popup menu. Used e.g. for the subvertex list for a composite state.
 . The list model is usually provided by a sub-class of {{{UMLModelElementListModel2}}}. There is a variant {{{UMLModelElementOrderedListModel2}}} intended for ordered links, which adds a few items to the pop-up menu, allowing sorting. This latter model is used e.g. for attributes of a class.
 * A drop down box of options that can be selected. This one exists in several versions, each having different possibilities. The most simple version is the UMLComboBox2.
 . The {{{UMLEditableComboBox}}} allows editing the selected item.
 . The {{{UMLSearchableComboBox}}} allows editing the selected item. See e.g. the Operation combobox on the callevent properties panel.
 . Then there is a variant with a seperate button for navigation to the property panel for the currently selected item. This is supported by the {{{UMLComboBoxNavigator}}} class. Used e.g. for the stereotype field.
 * An editable multiline text area. Supported by the {{{UMLTextArea2}}} class. Used e.g. for the text field of a UML Comment.

Examples of these fields in more detail follow below.

==== Adding a simple list field ====

For example we need to add a field to the use case property panel for the extends relationships that derive from this use case.

This field consists of a label and a scrollable pane ({{{JScrollPane}}}) containing the list ({{{JList}}}), which may be empty, or contain extend relationships from this use case.

Rather than a straight {{{JList}}}, we use its child, {{{UMLLinkedList}}}, which adds several features to the standard {{{JList}}} specifically for ArgoUML's properties panels.

The constructor for {{{UMLLinkedList}}} requires two arguments, a list model and a flag to indicate whether to show an icon.

The list model should be a subclass of {{{UMLModelElementListModel2}}}, a subclass of the Swing {{{DefaultListModel}}} which implements {{{AbstractListModel}}}. The {{{UMLModelElementListModel2}}} implements two interfaces: one that listens to target changes, and one that listens to UML model changes.

===== The list model =====

In our example we create {{{UMLUseCaseExtendListModel}}}. Its constructor takes no arguments. However, we need to provide the parent class with a Model subsystem event name by invoking the constructor of the parent class, with the event name as parameter.

A string naming a ["<<Subsystem>> Model"] subsystem event that should force a refresh of the list model. A null value will cause all events to trigger a refresh. The name of the event is the same as the name of the associated attribute or association end from the UML 1.4 metamodel.

This list model should then be provided with a number of methods. The following are mandatory, since they are declared abstract in the parent.

{{{protected void buildModelList()}}}
 (Re)Builds the list of elements. Called from {{{targetChanged}}} every time the target of the proppanel is changed.

 {{{protected boolean isValidElement(Object/*MBase*/ o)}}}
 Returns true if the given element is valid, i.e. it may be added to the list of elements. This function is called for many UML elements, to determine if it fits in the list. Remark: The indication /*MBase*/ is a remainder from the time that ArgoUML included direct references to the NSUML model all over the code. Now it is a practical reminder of what we are dealing with.

 /!\ CategoryFix: The following description is old and the property panels have undergone some fundamental changes since it was written. It would be good if someone that knows how it works now could write a description on how it works now.

The following are sometimes provided as an override of the parent, although for many uses the default is fine.

{{{public void open(int index)}}}

 Perform the action associated with the “open” pop-up menu on the element at the given index. The default provided in the parent just navigates to that element.

{{{public boolean buildPopup(JPopupMenu popup, int index)}}}

    Build a pop-up menu for the list and return whether it should be displayed. Any actions will be associated with the item at the given index in the list. This is built using UMLListMenuItem, which can record the index, rather than plain JListItem. The default provides open, add, delete, move up and move down, with add disabled if there are already as many elements as the upper bound (if any) for the list, open and delete disabled if there are no elements and move up and move down disabled if they cannot be invoked on the given element. The default implementation always returns true.

The following should be declared as needed to support particular pop-up functions.

{{{public void add(int index)}}}

    Perform the actions associated with the “add” pop-up menu on the element at the given index. There is no default provided, so this must be given if the “add” operation is supported. The {{{addAtUtil()}}} method (see below) may prove helpful.

    In this routine you may create a new Model subsytem entity. The best way to do this is using a buildXXX method from the appropriate factory so that the appropriate initialization gets done, but you can also use a createXXX method and set it up (don't forget e.g namespace etc) yourself. Remember also to change anything that references the newly created entity.
    /!\ NOTE: The following was written regarding NSUML. It may be generally true for the ["<<Subsystem>> Model"] subsystem, but this hasn't been verified. CategoryFix: Verify this.

    NSUML routines generally set up the “other” end of a relationship automatically if you set up one end. If you try to do both (on a NxM relationship) you will probably end up doing it twice. If you do encounter this, the rule of thumb is to explicitly set the ordered end (if you do it the other way round, NSUML will assume you mean the "other" end to be at the end of its ordered list).

{{{public void delete(int index)}}}

    Perform the actions associated with the “delete” pop-up menu on the element at the given index. There is no default provided, so this must be given if the “delete” operation is supported.

{{{public void moveUp(int index)}}}

    Perform the actions associated with the “move up” pop-up menu on the element at the given index. There is no default provided, so this must be given if the “move up” operation is supported.

{{{public void moveDown(int index)}}}

    Perform the actions associated with the “move down” pop-up menu on the element at the given index. There is no default provided, so this must be given if the “move down” operation is supported.

The following normally use the default method, but may be declared to override methods in the parent

{{{public void resetSize()}}}

    Called when an external event may have changed the size of the list. The default just sets a flag, which will ensure {{{recalcModelElementSize}}} (see above) is invoked as needed.

{{{public Object formatElement(MModelElement element)}}}

    Return an object (invariably a String) that represents an element. The default provided in the parent defers this to the container, which in turn defers it to the current profile. This is usually perfectly satisfactory.

{{{public void targetChanged()}}}

    Called when the number of elements in the displayed list (including “none”) may have changed. Default invokes the necessary Swing operations to advise of a change in list size.

{{{public void targetReasserted()}}}

    Called when the navigation history has been changed (and navigation buttons may need changing). Not clear why anything is needed, but default recomputes the list size, and invokes the necessary Swing operations.

{{{public void roleAdded(final MElementEvent event)}}}
    /!\ This describes the old event interface. CategoryFix: It needs to be updated.

    part of the NSUML EventListener interface. Called when an add event happens, i.e. some Model subsystem object has been added. The default provided looks to see if the event is the role name we declared, or we are listening to all events, and if so looks to see if it relates to an element in our list. If so Swing is notified that the element has been added.

{{{public void roleRemoved(final MElementEvent event)}}}
    /!\ This describes the old event interface. CategoryFix: It needs to be updated.

    part of the NSUML EventListener interface. Called when a remove event happens, i.e. some Model subsystem object has been removed. The default provided looks to see if the event is the role name we declared, or we are listening to all events, and if so looks to see if it relates to an element in our list. If so Swing is notified that the element has been removed.

{{{public void recovered(final MElementEvent p1)}}}, {{{public void listRoleItemSet(final MElementEvent p1)}}}, {{{public void removed(final MElementEvent p1)}}}, and
{{{public void propertySet(final MElementEvent p1)}}}
    /!\ This describes the old event interface. CategoryFix: It needs to be updated.

    these are all required as part of the NSUML EventListener interface, which is not well documented. In each case the default implementation recomputes the size, and advises Swing that the entire list has changed. Needs more investigation.
public void navigateTo(MModelElement modelElement)

    a request to navigate to the specified object as part of the NavigationListener interface. The default in the parent just invokes navigateTo() on the container (ultimately PropPanel).

The following utility routines are also provided in the parent. They are not normally overridden.

{{{public int getUpperBound()}}}

    get any upper bound (-1 is used if there is none).

{{{public void setUpperBound(int newBound)}}}

    set the upper bound (-1 is used if there is none).

{{{public final String getProperty()}}}

    returns the Model subsystem event name being monitored (null if all are being monitored).

{{{protected final int getModelElementSize()}}}

    returns the number of elements in the list. Invokes {{{recalcModelElementSize()}}} (see above) if necessary.

{{{final Object getTarget()}}}

    returns the Model subsystem object associated with the container (some child of PropPanel usually) that holds this list model.

{{{final UMLUserInterfaceContainer getContainer()}}}

    returns the the container (some child of PropPanel usually) that holds this list model.

{{{public int getSize()}}}

    returns the size of the list. Including if there are no elements in the model, but the list has a default text when empty.

{{{public Object getElementAt(int index)}}}

    returns the element at the given index in the list.

{{{static protected Collection addAtUtil(Collection oldCollection, MModelElement newItem, int index)}}}

    helps in writing the “add” function. newItem is added at the specified index in the given oldCollection.

{{{static protected java.util.List moveUpUtil(Collection oldCollection, int index)}}}

    helps in writing the “move up” function. Swaps the elements at offsets index and index-1. Not clear why it doesn't return a Collection.

{{{static protected java.util.List moveDownUtil(Collection oldCollection, int index)}}}

    helps in writing the “move down” function. Swaps the elements at offsets index and index-1. Not clear why it doesn't return a Collection.

{{{static protected MModelElement elementAtUtil(Collection collection, int index, Class requiredClass)}}}

    helps in writing the getElementAt(). Finds the element at a specific index. The last argument is ignored!

==== Building the field ====

By convention the background of the list is set to the same as the background of the {{{PropPanel}}} and the foreground to {{{Color.blue}}}.

The list is then added to a {{{JScrollPane}}}. Although ArgoUML has historically not used scrollbars ({{{JScrollPane.VERTICAL_SCROLLBAR_NEVER}}} and {{{JScrollPane.HORIZONTAL_SCROLLBAR_NEVER}}}), it is more helpful to permit at least a vertical scrollbar where needed ({{{JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED}}} and {{{JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED}}}).

Finally the inherited method {{{addCaption()}}} is used to add the label for the field and {{{addField()}}} to add the associated scroll pane.

The second argument of each of these identifies the index of the caption/field pair in the vertical column of the grid for this property panel. The third argument identifies the column index. The final argument is a vertical weighting to expand the field if there is room in the property tab. This is usually set to the same non-zero value for all fields and corresponding captions that can have multiple entries, so they expand equally. If none of the fields should expand, the caption only of the last field in each column should be given a non-zero value.

==== Adding Property Tab Tool-bar Buttons ====

These are added by creating new instances of {{{PropPanelButton}}} (you don't need to assign them to anything - just creating will do). This has six arguments.
 * The container, i.e this property panel (usually just use this).
 * The panel for the buttons. Use {{{buttonPanel}}} which is inherited from {{{PropPanel}}}.
 * The icon. Lots of these are already defined in {{{PropPanel}}}.
 * The advisory text for the button. Use {{{localize(string)}}} to ensure international portability.
 * The name of the method to invoke when this button is used. Some of the standard ones (e.g for navigation) are provided, but you will need to write any specials.
 * The name of the method (if any) to invoke to see if this button should be enabled. Use null if the button should always be enabled.

In our example, the extend property panel has a “add extension point” button, with a method {{{newExtensionPoint}}} that we provide to create a new use case.

==== Support for stereotypes ====

The {{{PropPanel}}} should override the following (note the spelling of the method name).

{{{protected boolean isAcceptibleBaseMetaClass(String baseClass)}}}
 Returns true if the given base class is a class of the target in the PropPanel.

 This is used to determine what stereotypes may be shown for this property panel.

==== Other sorts of fields ====

Another sort of field that may be useful is the {{{ComboBox}}}. This is useful to allow users to select from a pre-defined list of alongside a navigation arrow to go to the selected entry.

For example this is used to provide drop-down lists for the base and extension use cases of an Extend relationship in {{{PropPanelExtend}}}.

The model behind the drop down is created by using {{{UMLComboBoxModel}}}: {{{UMLComboBoxModel(container, predicate, event, getter, setter, allowVoid, baseClass, useModel)}}}.

The container is the {{{PropPanel}}} where we are setting up this ComboBox, the predicate is the name of a public method in that {{{PropPanel}}} that, given a model element, determines if it should be in the drop down, the event is the Model subsystem event name we are looking for (see earlier for the list), getter is the name of a public method in the {{{PropPanel}}} that yields the current entry in the combo Box (of type baseClass), setter (with a single argument of type baseClass) sets that entry, allowVoid if true will allow an empty entry for the box, baseClass is the UML metaclass from which all entries must descend, useModel is true to consider all the elements in the standard profile model for inclusion (so the Java types, standard stereotypes etc.).

For our PropPanelExtend, we provide a predicate routine the call for the “base” field is:

{{{UMLComboBoxModel(this, "isAcceptableUseCase", "base", "getBase", "setBase", true, MUseCase.class, true);}}}

and we define the methods {{{isAcceptableUseCase}}}, {{{getBase}}} and {{{setBase}}} in {{{PropPanelExtend}}}.

==== How UMLTextField works ====

This information is provided by Jaap Branderhorst (September 2002).

{{{UMLTextField}}} implements several kinds of event listeners:
 * {{{MMelementListener}}}
 * {{{DocumentListener}}}
 * {{{FocusListener}}}

Furthermore it is a {{{UMLUserInterfaceComponent}}}.

Since it is an {{{UMLUserInterfaceComponent}}} it must implement {{{targetChanged}}} and {{{targetReasserted}}}. {{{TargetChanged}}} is called every time the {{{UMLTextField}}} is selected. {{{targetReasserted}}} is of no interest for {{{UMLTextField}}}. It plays a role in keeping history but since history is not really implemented at the moment in ArgoUML it is of no interest. {{{targetChanged}}} does two things:
 * It calls the {{{targetChanged}}} method of the {{{UMLTextProperty}}} this {{{UMLTextfield}}} is showing.
 * It calls the update method. The update method is described further on.

Besides {{{UMLUserInterfaceComponent}}} there are several other interfaces of interest. One of them is {{{MMElementListener}}}.

Every time a {{{MModelElement}}} is changed this will fire an {{{MEvent}}} to {{{UMLChangeDispatch}}}. {{{UMLChangeDispatch}}} will dispatch these events to all containers implementing {{{UMLUserInterfaceComponents}}} interested in this event, including {{{UMLTextField}}}. It will also dispatch the event to all children of an interested container implementing {{{UMLUserInterfaceComponent}}}. By this it is only necessary to register a {{{PropPanel}}} which holds an {{{UMLTextField}}} at {{{UMLChangeDispatch}}} to dispatch the event to the {{{UMLTextField}}} too. MMelementListener knows several methods of which only one is of interest to {{{UMLTextField}}}s:
 * {{{propertySet}}}

      Called every time a property in a {{{MModelElement}}} is set. This method calls update too if the {{{UMLTextProperty}}} really is affected.

Furthermore {{{UMLTextField}}} implements {{{DocumentListener}}}. This is very typical for {{{UMLTextField}}}. At the moment it is not possible to change the style of the text in the {{{UMLTextField}}}. Therefore the method {{{changedUpdate}}} does not have a body. This method is only called when a {{{DocumentEvent}}} occurs that changes the style/layout of the text. The methods {{{insertUpdate}}} and {{{removeUpdate}}} are respectively called when a character is added to the document {{{UMLTextField}}} contains or removed. Since both methods are called when there is true user input and when the contents of the document are changed programmatically, the methods distinguish between them. {{{insertUpdate}}} and {{{removeUpdate}}} are both handled via the protected method handleEvent. {{{handleEvent}}} updates the property in {{{UMLTextProperty}}} if it is really changed. If the update comes via user input, it is checked if it is valid input. If it is not, a JOptionPane is shown with a warning and the change is not committed into the model. If it is not via user input, the input is not checked and the property is set. If the property is set, the {{{update}}} method is called.

The implementation of {{{FocusListener}}} makes sure that the checking of user input only happens when focus is lost. Otherwise, it would not be possible to enter 'intermediate' values that are not legal. For instance, say the value class is not legal. Without the implementation of {{{FocusListener}}}, it would not be possible to enter class diagram since handleEvent would pop-up a warning message box.

The method update updates both the actual {{{JTextfield}}} as the diagram as soon as some property is set. The updating of the diagram is done by calling the damage method of the figs that represent the property on the diagram.
----
CategoryFromCookbook
The rest of this page is a description of the ["/Old Implementation"].
Purpose
to provide a form view of the diagrams and objects in the model. The contents of the model are modifiable from the panel.
Located

org.argouml.uml.?

Located

org.argouml.core.propertypanels.module in Eclipse project argouml-core-umlpropertypanels (location of the new implementation).

Layer

<<Layer>> View and Control subsystems

Definition

There are a few different views for this. The main view of the objects contains all fields in the object including references to other objects.

The Documentation and Tagged Values tabs contain fewer fields and is the same for all kinds of objects.

The old and the new

There is a new implementation (since 0.30) for the main view.

The rest of this page is a description of the /Old Implementation.

<<Subsystem>> Property panels (last edited 2010-02-17 06:07:29 -0800 by linus)