How to write a Rhino ItemView
Table of Contents
This document is a reference for SGI software engineers who
will be writing ItemViews for Rhino applications. An ItemView in the
Rhino Architecture is a UI Component that displays all relevant information
about a particular Item. The ItemView is the user's main source of information
about the attributes of an Item, which include both static and dynamic information.
Shown below is a picture of an ItemView, with the different
sections labeled.
Icon |
Shown in the upper left corner of the
ItemView. Typically, the Icon represents the type of Item being
viewed, and additionally the state of the Item |
Fields |
Shown in the upper right corner of the ItemView. This section is
divided into two columns, the left for the name of the field, and the
right for the value of the field. The fields section is designed to
show information about the Item that can be represented by fairly
short Strings |
Additional Info section |
This section occupies the center of the ItemView. It is an optional section.
This section is designed to show information about the Item that can't
easily be represented as a single line of text. Examples include ItemTables,
graphs, or additional icons. Any Java component can be shown
here. If there are no components to show in this section, then the
ItemView will not show the Additional Info section. |
TaskShelf section |
This section occupies the bottom of the ItemView. It shows a TaskShelf
containing Tasks that can operate on the displayed Item in the Item's current state |
Before beginning to create an ItemView for a particular Category, it
is necessary to understand the names and terms that the Rhino infrastructure
uses in relation to Categories. See the The Names of Categories on the Client and on
the Server documentation for more information.
While in the early stages of writing Categories, it may be desirable
to show an ItemView that shows all of the Attributes of an Item. The ItemView
supports this idea by means of a "no-code" ItemView. This version of an
ItemView is not designed for use in a shipping Rhino application, but can
be of great assistance while investigating the Rhino Infrastructure or
for giving preliminary demos. No code or resource files need to be written
to use the "no-code" ItemView - it can be launched as soon the server side
Categories have been written and the Rhino infrastructure has been installed
on the client. To turn the ItemView into a shippable ItemView, it is necessary
to provide resources that describe the way that the Attributes of the
Item are to be displayed. The rest of this document will describe how
to accomplish this. To launch a "no-code" ItemView, follow the
instructions in the section titled How to launch
ItemViews. An example of the "no-code" ItemView for the
RhinoExampleCategory is shown to the right.
Before writing any code or resource files for the ItemView, begin by
analyzing the information that needs to be displayed. Divide the
information into two groups: information that will go in the
Fields section, and information that will go in the Additional
Information section. While there are no absolute rules about what
kind of information goes where, here are some suggestions on how to
divide the information:
- The Fields section is best suited for displaying short text
strings. The Additional Information section has the ability to
display larger components.
- It is suggested that information that defines the
identity of the Item be in the Fields section where it will be
easy to find. Secondary information, such as information about
relationships between the Item and other Items, can be in the
Additional Information section and interested users can take
the time to locate it.
- It may be desireable to place information that is static or changes
infrequently in the Fields section, and put dynamic information
that changes often during the normal operation of the system in
the Additional Information section.
- Don't put too many pieces of information in the Fields section.
More than about 7 lines will make the ItemView hard to read.
Try to split the information into smaller sets that are logically and
semantically grouped. Put the most
important sets of information in the Fields section and put the
rest in the Additional Information section.
- In each section, order the information by importance, with
the most important information at the top.
- In some cases, it makes sense to break these guidelines to
group common pieces of information or to give the ItemView an
appealing layout that's easy to understand.
All of the information in a RhinoExampleCategory Item probably
belongs in the Fields section, but imagine that the Printer type of Item also had
a list of print jobs. Since the list could be quite long, using a comma
separated list would not be a practical solution. In this case, it
might work to use a JList to implement a scrollable list to display all of the
print jobs. This component would be displayed in the Additional Information
section).
There are several types of properties that control the look of the
ItemView. The properties (in the order that they are described) are:
- The field properties - Define names of the fields.
- The basedOn properties - Tell the
ItemView which Attributes correspond to particular fields.
- The label properties - Provide the labels the ItemView will
use for the fields.
- The method properties - Specify the
manner in which the ItemView will use the Item's Attributes
to fill in the field.
The pieces of the Item's information that are displayed in the Fields section are
completely controlled by a resource file. The most fundamental resources
are those that give names to the fields that will be displayed. These
names identify the fields so that other resources can refer to
particular fields. The resources follow the form
<Category name>.ItemView.field<n>,
where <Category name> is the name of the Category (see the FIELDS
documentation for more info), and <n> represents integers starting at
0 that represent order in which the fields should be displayed. For
example, the resource file that controls the RhinoExampleCategory
contains the following lines (the letters in the first column are for
reference purposes only):
A: com.sgi.rhexamp.category.rhexampRhinoExampleCategory.ItemView.field0=name
B: com.sgi.rhexamp.category.rhexampRhinoExampleCategory.ItemView.field1=type
C: com.sgi.rhexamp.category.rhexampRhinoExampleCategory.ItemView.field2=mode
Because the first part of each line is identical, it is common to use
macros to shorten the lines of the resource file and to make the file
easier to read. An example of the same resources using macros is
shown below.
A: RHINO_EXAMPLE_CATEGORY=com.sgi.rhexamp.category.rhexampRhinoExampleCategory
B: IVprefix=${RHINO_EXAMPLE_CATEGORY}.ItemView
C:
D: ${IVprefix}.field0=name
E: ${IVprefix}.field1=type
F: ${IVprefix}.field2=mode
The three "field" resources (D - F) define the names of the fields and the
order in which the fields will be displayed in the ItemView. In this example,
the names of the fields correspond exactly with the names of the Attributes
in the Item that will be displayed in the field. By naming the fields
in this manner, the ItemView can use default behavior and
automatically associate the correct Attribute with the field. It is
also possible to give the fields names that are not the same as the
names of Attributes. In that case, it may be necessary to use the
"basedOn" property (defined below) to tell the ItemView which
Attribute is associated with a field.
Running an ItemView with the 5 lines described above in the
resource file will result in an ItemView that is shown on the right.
Notice that the order of the fields is "name", "type", and then
"node", which is as specified in the resource file. ItemView has used
a default label for each of the fields. Information about how to
customize the label is described below. The ItemView is using the
default "toString" method (methods are described below). This is the
simplest method, and uses the results of calling Java's toString
method on the value of the Attribute.
In the example resource file shown above, the names of the fields were
defined to be the same as the Item's Attributes that they
represented. This allowed the ItemView to automatically show the
value of the Attribute in the field.
It is sometimes desirable to use different names for the fields than
the Attributes that they represent. This can make the resource file
more readable or can be required because there may not be a one to one
correspondence between the Attributes in the Item and the fields that
are displayed.
If a field is given a name that does not correspond to the name of
an Attribute, the "basedOn" property is
used to tell the ItemView which Attribute the field represents. The
"basedOn" resources are defined
as: <Category name>.ItemView.basedOn.<field>,
where <Category name> is the name of the Category, and <field> is the name
of a field. (See the BASED_ON
documentation for more info).
The renderer method (as described
below) does not require that the field be associated with a particular
Attribute. When using this method, it is not necessary to specify
the "basedOn" property even if the name of the field does not
correspond to an Attribute. All the other methods, including the default
"toString" method, require that the field be associated with a
particular Attribute of the Item.
For example, suppose that the "name" Attribute should be displayed
twice, once at the beginning of the list, and once at the end.
A resource file as follows would do just that:
A: RHINO_EXAMPLE_CATEGORY=com.sgi.rhexamp.category.rhexampRhinoExampleCategory
B: IVprefix=${RHINO_EXAMPLE_CATEGORY}.ItemView
C:
D: ${IVprefix}.field0=Name1
E: ${IVprefix}.field1=type
F: ${IVprefix}.field2=node
G: ${IVprefix}.field3=Name2
H:
I: ${IVprefix}.basedOn.Name1=name
J: ${IVprefix}.basedOn.Name2=name
This would result is the name being shown twice, as is seen to the right:
The next step is to define the strings that will be used as the labels
for the fields. The "label" resources are defined
as: <Category name>.ItemView.label.<field>.label. (See the LABEL
documentation for more info). Optionally, another resource can be
specified that gives the name of a glossary entry that will be
displayed if the user clicks on the label. This resource is defined
as: <Category name>.ItemView.label.<field>.glossary,
and if this resource is defined, then the label will appear blue.
For example, define labels for the example ItemView, the following properties
would be added to the resource file:
A: RHINO_EXAMPLE_CATEGORY=com.sgi.rhexamp.category.rhexampRhinoExampleCategory
B: IVprefix=${RHINO_EXAMPLE_CATEGORY}.ItemView
C:
D: ${IVprefix}.field0=name
E: ${IVprefix}.field1=type
F: ${IVprefix}.field2=mode
G:
H: ${IVprefix}.label.name.label=Name:
I: ${IVprefix}.label.type.label=Type:
J: ${IVprefix}.label.type.glossary=glossary.Type
K: ${IVprefix}.label.mode.label=Access:
L:
M: glossary.Type = The type of the Item
Displaying the ItemView now shows that the desired labels are
displayed. Notice that the "type" label is displayed as a link, and
the picture shows the glossary window that results when the user
clicks on the link.
The next step is to choose what method the ItemView should use to display
the field. (In this usage, "method" does not refer to a Java
method, but rather to the typical English definition of the word)
The "method" resource controls this, and is defined as:
<Category name>.ItemView.method.<field> (see the METHOD
documentation for more info). Four methods are available:
-
The toString method is the default method, and is what the ItemView
implicitly uses to display the field if no method is specified in
the properties file. The toString method calls Java's
toString method on the value of the
Attribute that is associated with the field (either by the "basedOn"
property or the name of the field if no "basedOn" property is set). If
this method is used, no additional resources are
needed. For example, to make explicit
the fact that the "name" field should use the toString method, include
the following in the resource file:
${IVprefix}.method.name=toString
-
The lookup method uses the value of the Attribute associated with the
field (either by the "basedOn" property or the name of the field if no
"basedOn" property is set) as a key to lookup a string in
a table of values. This is good for cases when the value of the Attributes
comes from a limited set of possible values, and there is a
mapping from the Attribute's value to some more easily understandable
string. This method
is also good when there is a need to localize the text that gets
displayed in the field. If the "lookup" type is used, additional
"lookup" resources (defined as
<Category name>.ItemView.lookup.<field>.<Attribute's value>)
should also be provided for each of the possible values of the Attribute. For example,
to specify that the "type" field should use the lookup method, and should
display the type in Spanish instead of English, include the following
in the resource file:
A: ${IVprefix}.method.type=lookup
B:
C: ${IVprefix}.lookup.type.Printer=Impresora
D: ${IVprefix}.lookup.type.Clock=Reloj
E: ${IVprefix}.lookup.type.NetscapeExecutable=Netscape
In this case, the type of the Item will be displayed in it's Spanish equivalent:
-
The richText method will display the string value of the Attribute
just as the toString method does, but will display it as a link that launches
an ItemView. This is generally used to show the relationship between an
Item in one Category and an Item in another Category. The example used in this document
has one Category, but consider the case where
each of the Items in the RhinoExample category had an Attribute in it that
specifies the server on which the Item was running. Assume also that there
is a second Category, "rhexampServerCategory" with server Items. Consider
that the RhinoExample Item has an Attribute with the name "server" that is the name
of the server that the RhinoExample is running on, and another Attribute
"server_selector" which is the selector of the server in the "rhexampServerCategory"
category. (In many cases, the name of the server would be the same as
the selector of the server. In that case, substitute "server" for
"server_selector" in the following example.) To show a link to the
appropriate server from the RhinoExample ItemView, the following would
be added to the Resource File:
A: ${IVprefix}.field3=server
B: ${IVprefix}.label.server=Server:
C:
D: ${IVprefix}.method.server=richText
E: ${IVprefix}.selector.server=server_selector
F: ${IVprefix}.category.server=rhexampServerCategory
-
It is sometimes the case that none of the three ways presented so far are
adequate to display the state of the Item. Such cases can result when:
- there is a need to synthesize two or more Attributes into a single value for display
- Java code is needed to decode the Attribute (or Attributes) into a
user-readable value
- A special component is needed to display text
- It is desired to show a label with color
- The user would want launch something other than an ItemView
- any thing else not permitted with the three predefined methods
In any of these cases, the renderer method should be used. This method
provides a chance to write a small piece of Java code that will control
the display of the field. In the case of the RhinoExample Category, the
renderer converts the numeric "mode" Attribute into text that
is displayed to the user. For example, the mode "33060" is displayed
as "Read Only". See the RhinoExampleCategoryRenderers file for this example.
Depending on the way that the server-side Category is written, there
may be cases where a particular Attribute is missing from an Item. For
example, consider that the Item can optionally contain the "type"
Attribute. If the Item contains that Attribute, then the ItemView
should display the name using the lookup method as described
above. Otherwise, the ItemView should display some other string, such
as "(Unknown)". For this situation, you can use the "missing" resource (defined as
<Category name>.ItemView.missing.<field>).
The "missing" resource allows you to specify a string that will be
displayed if an Attribute is missing from an Item. The "missing"
resource can be used with the toString, lookup, or
richText methods.
For example, to use the string "(Desconocido)" (Spanish for
"Unknown") if the "type" Attribute is missing from the Item, add the
following resource:
A: ${IVprefix}.method.type=lookup
B:
C: ${IVprefix}.lookup.type.Printer=Impresora
D: ${IVprefix}.lookup.type.Clock=Reloj
E: ${IVprefix}.lookup.type.NetscapeExecutable=Netscape
F: ${IVprefix}.missing.type=(Desconocido)
ItemViews use an instance of the ItemViewFieldRenderer
interface to render fields that use the renderer
method. There is only one ItemViewFieldRenderer
per ItemView, so it must be able to handle all of the fields in the ItemView
that are using the renderer method. A class should be written that implements
the ItemViewFieldRenderer interface, and placed in the product's "category"
package. (The file can actually be placed anywhere, but the "category"
package is one logical place). Tell the ItemView how to find the
class by naming it in the
property file with the "fieldRenderer" property, which is defined as <Category name>.ItemView.fieldRenderer (see the FIELD_RENDERER
documentation for more info). For example, the RhinoExampleCategory
(whose full name is
com.sgi.rhexamp.category.rhexampRhinoExampleCategory) has a
class com.sgi.rhexamp.category.rhexampRhinoExampleCategoryRenderers
that implements the ItemViewFieldRenderer interface, and so the following
line is included in the Category's resource file:
A: ${IVprefix}.fieldRenderer=${RHINO_EXAMPLE_CATEGORY}Renderers
If a field uses the renderer method, but no
ItemViewFieldRenderer is defined with the "fieldRenderer" property,
then the ItemView will attempt to load a class with the name
<Category Name>FieldRenderer. For example, for the
RhinoExampleCategory, it would attempt to load the class
com.sgi.rhexamp.category.rhexampRhinoExampleCategoryFieldRenderer.
If the "fieldRenderer" resource is not specified and the
<Category Name>FieldRenderer class is not found, then
ItemView will throw an assertion.
The ItemViewFieldRenderer has five methods that must be implemented. See
the documentation for ItemViewFieldRenderer about the specifics of each
method.
-
public void initializeFieldRenderer(ItemViewContext ivc, ItemViewController
controller);
-
public Component getComponentForField(String field);
-
public void renderFields(Item item);
-
public void renderFieldsAgain(Item item);
-
public void renderFieldsBlank();
The sequence that the methods will be called in is as follows:
-
initializeFieldRenderer
-
getComponentForField (once for each field using the renderer)
-
renderFields
-
renderFieldsAgain (zero or more times)
-
renderFieldsBlank
-
repeat from step 3 (only if ItemView is used to display another Item)
The initializeFieldRenderer method is responsible for initializing the renderer.
The ItemView calls the getComponentForField method once for each field
that is using the ItemViewFieldRenderer. The ItemView passes in the name
of the field. and the renderer passes back the component that the ItemView
should use to display the field. When the ItemView obtains an Item, it
passes the Item to renderFields. At this time, the renderer should
use the Item to fill in the information that the components are displaying.
If the Item changes, the ItemView calls renderFieldsAgain, passing
the Item. The renderer should update the components to show the current
state. If the Item is deleted, or the ItemView is disposed, the ItemView
will call renderFieldsBlank. The renderer should remove any state
information from the component, and prepare itself to be garbage collected.
In the case that the Item reappears, or the ItemView is set to display
another Item, the sequence repeats from step 3 (renderFields).
The additional info area of the ItemView is completely at the discretion
of the programmer, and Java code must be written to display anything. ItemViews
use an instance of ItemViewAdditionalInfoRenderer to render the additional
info section. Write a class that implements the ItemViewAdditionalInfoRenderer
interface, and place it in the product's "category" package. (The
file can actually be placed anywhere, but the "category" package is
one logical place). Tell the
ItemView how to find the class by naming it in the property file with
the "additionalInfoRenderer" property, which is defined as <Category name>.ItemView.additionalInfoRenderer (see the
ADDITIONAL_INFO_RENDERER
documentation for more info). For example, the
RhinoExampleCategory (whose full name is
com.sgi.rhexamp.category.rhexampRhinoExampleCategory)
has a class com.sgi.rhexamp.category.rhexampRhinoExampleCategoryRenderers
that implements the ItemViewAdditionalInfoRenderer interface, and so the
following line is included in the Category's resource file:
A: ${IVprefix}.additionalInfoRenderer=${RHINO_EXAMPLE_CATEGORY}Renderers
If no ItemViewAdditionalInfoRenderer is defined with the "additionalInfoRenderer" property,
then the ItemView will attempt to load a class with the name
<Category Name>AdditionalInfoRenderer. For example, for the
rhinoExampleCategory, it would attempt to load the class
com.sgi.rhexamp.category.rhexampRhinoExampleCategoryAdditionalInfoRenderer.
If the "addtionalInfoRenderer" resource is not specified and the
<Category Name>AdditionalInfoRenderer class is not found, then
ItemView will not display anything in the "Additional Information" section.
The API for the ItemViewAdditionalInfoRenderer is almost identical to that
of the ItemViewFieldRenderer. In this case, there are four methods that
must be implemented:
-
public void initializeAdditionalInfoRenderer(LabelComponentPanel panel,
ItemViewContext ivc, ItemViewController controller);
-
public void renderInfo(Item item);
-
public void renderInfoAgain(Item item);
-
public void renderInfoBlank();
The sequence that the methods will be called in is as follows:
-
initializeAdditionalInfoRenderer
-
renderInfo
-
renderInfoAgain (zero or more times)
-
renderInfoBlank
-
repeat from step 2 (only if ItemView is used to display another Item)
The initializeAdditionalInfoRenderer method is responsible for
initializing the renderer and preparing it for use. The ItemView passes
the method a LabelComponentPanel that the renderer should add its components
to. When the ItemView receives an Item, it passes the
renderInfo method the Item, and the renderer should update
the components on the panel as it
wishes. If the Item changes its state then the ItemView will call renderInfoAgain
and the renderer should update all of the components to show the current
state. If the Item is deleted or the ItemView is disposed, then the ItemView
will call renderInfoBlank, and in response the renderer should update the
components to not show any state and prepare to be garbage collected. In
the case that the Item reappears, or the ItemView is set to display another
Item, the sequence repeats from step 2 (renderInfo).
The ItemView does not directly control the Icon that is displayed. The
Icon is generated by the ResourceBasedIconRenderer. See the
tutorial on using
ResourceBasedIconRenderer for details on how to control the Icon.
The ItemView does not directly control the title that is used. The Title
is generated by the ResourceBasedNameRenderer.
See the tutorial on using
ResourceBasedNameRenderer for details on how to control the title.
The ItemView does not directly control the Tasks shown in the
TaskShelf. The Tasks are generated by the TaskRegistry.
To view an ItemView from the command line, type:
%> java com.sgi.sysadm.manager.RunItemView <package qualified Category name> <Item selector>
For example, to launch an ItemView of the Item "foo" in Category
"BarCategory", where the ItemView's resource file is in
com/sgi/myProduct/category (relative to classpath),
type:
%> java com.sgi.sysadm.manager.RunItemView
com.sgi.myProduct.category.BarCategory foo
To launch a "no-code" ItemView, pass the Category selector instead
of the fully qualified name:
%> java com.sgi.sysadm.manager.RunItemView BarCategory foo
A "no-code" ItemView will also be displayed if no resources
corresponding to the Category are found.
To programmatically launch an ItemView, use one of two methods:
To launch an ItemView in a new frame (called an ItemViewFrame),
use the launchItemViewFrame
method in ItemViewFrame. The launchItemViewFrame method
takes a ItemViewLaunchRequestEvent,
which encapsulates all the information about which ItemView to launch. For example:
1: ItemViewFrame.launchItemViewFrame(
2: new ItemViewLaunchRequestEvent(this,
3: "com.sgi.myProduct.category.BarCategory",
4: "foo"),
5: new UIContext());
To embed an ItemView in another component, create an ItemView with the
createItemView
method of ItemView, set Item to display with the setItem
method, then call getPanel on ItemView to get a panel that contains
the ItemView. For example:
1: ItemView iv = ItemView.createItemView(_hostContext,
2: "com.sgi.myProduct.category.BarCategory");
3: iv.setItem("foo");
4: _panel.add(iv.getPanel());
A: # Set up some macros to use in this resource file. See the
B: # ResourceStack documentation for more about macros.
C: RHINO_EXAMPLE_CATEGORY=com.sgi.rhexamp.category.rhexampRhinoExampleCategory
D: IVprefix=${RHINO_EXAMPLE_CATEGORY}.ItemView
E: ITprefix=${RHINO_EXAMPLE_CATEGORY}.ItemTable
F:
G:
H: # Set the width and height of the ItemView. See the PANEL_WIDTH and
I: # PANEL_HEIGHT documentation for more information.
J: ItemViewPanel.width=333
K: ItemViewPanel.height=260
L:
M: # Set up the three fields. Call the fields "name", "type", and "mode".
N: ${IVprefix}.field0=name
O: ${IVprefix}.field1=type
P: ${IVprefix}.field2=mode
Q:
R: # Tell the ItemView which Attributes of the Item to use to show the
S: # appropriate field. It is not necessary to set the "basedOn" resource
T: # for a field that is using the "renderer" method, which is why there is
U: # no "${IVprefix}.basedOn.mode" resource. In this case, the next two
V: # lines are unnecessary, because the name of the Attribute is the same
W: # as the field. They are included here to make the resource file easier
X: # to understand and for illustration purposes.
Y: ${IVprefix}.basedOn.name=name
Z: ${IVprefix}.basedOn.type=type
AA:
AB: # Sets the labels to be used for the three fields.
AC: ${IVprefix}.label.name.label=Name:
AD: ${IVprefix}.label.type.label=Type:
AE: ${IVprefix}.label.mode.label=Access:
AF:
AG: # Sets the method that the ItemView will use to display the
AH: # three fields.
AI: ${IVprefix}.method.name=toString
AJ: ${IVprefix}.method.type=lookup
AK: ${IVprefix}.method.mode=renderer
AL:
AM: # Resources necessary because the "type" field is using the
AN: # "lookup" method. See the description of the lookup method for more information.
AO: ${IVprefix}.lookup.type.Printer=Printer
AP: ${IVprefix}.lookup.type.Clock=Clock
AQ: ${IVprefix}.lookup.type.NetscapeExecutable=Netscape
AR:
AS: # Tells the ItemView what classes to use as the
AT: # ItemViewFieldRenderer and the ItemViewAdditionalInfoRenderer. In this
AU: # case, both renderers are in the same class, but this is not
AV: # necessarily the case.
AW: ${IVprefix}.fieldRenderer=${RHINO_EXAMPLE_CATEGORY}Renderers
AX: ${IVprefix}.additionalInfoRenderer=${RHINO_EXAMPLE_CATEGORY}Renderers
AY:
AZ: # Resources specific to the AdditionalInfoRenderer. The
BA: # AdditionalInfoRenderer has access the the ResourceStack, so this file
BB: # is a good place to put resources that control the
BC: # ItemViewAdditionalInfoRenderer or ItemViewFieldRenderer. The names of
BD: # the resources are specific to the code that is written in the
BE: # renderers.
BF: ItemView.AdditionalInfo.marginLeft=0
BG: ItemView.AdditionalInfo.marginTop=0
BH: ItemView.AdditionalInfo.marginBottom=0
BI: ItemView.AdditionalInfo.marginRight=0
BJ: ItemView.AdditionalInfo.layoutType=vertical
BK: ItemView.AdditionalInfo.label=Additional Information:
BL: ItemView.AdditionalInfo.text=This area is available for displaying \
BM: additional, item-specific Components to represent this item. In this \
BN: example, we're just displaying text in a RichTextComponent, but you can \
BO: put any Component you want in here.