Purpose - to provide a form view of the diagrams and objects in the model.
The contents of the model are modifiable from the panel.
5.4.1. Adding the property panel
Property Panels for UML model elements are found as class
PropPanelXXX.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 theUMLComboBoxNavigator 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.
5.4.1.1. 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.
5.4.1.1.1. 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 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.
![[Warning]](images/warning.png) | Warning |
|---|
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.
![[Warning]](images/warning.png) | Warning |
|---|
NOTE: The following was written regarding NSUML.
It may be generally true for the Model subsystem, but
this hasn't been verified.
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)
![[Warning]](images/warning.png) | Warning |
|---|
| This describes the old event interface.
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)
![[Warning]](images/warning.png) | Warning |
|---|
| This describes the old event interface.
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)
,
public void propertySet(final MElementEvent p1)
![[Warning]](images/warning.png) | Warning |
|---|
| This describes the old event interface.
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!
5.4.1.2. 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.
5.4.1.3. 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.
5.4.1.4. 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.
5.4.1.5. 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.
5.4.1.6. 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:
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
UMLTextFields:
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.