## Creating Customized Widget

### 1. Introduction

BOY is able to load customized widgets using Eclipse Plugin mechanism, so user can implements their own widgets by creating a new Eclipse plugin without touching BOY code. If you do think your widget can benefit other people, you can send your widget code to chenx1@ornl.gov so we can integrate it into BOY as a native BOY widget. This chapter will give you the concept of BOY widget and a step-by-step example. Before start, you should have some basic knowledge in Java and Eclipse plugin. The BOY widget figure is drawn by Draw2d, so you may also need to learn the basics of Draw2d.

The code of this example is availabe in BOY Examples->Miscellaneous->CustomizedWidget.

### 2. Concepts

All BOY widgets should rely on the MVC (Model-View-Controller) pattern to separate responsibilities among their single components.

- Model

  The model of a widget defines the set of properties, stores the according values. The model contains all data that gets persisted.

- View

  The view is a figure which is visible to the user.

- Controller

  The controller is called `Editpart`. Editparts are the link between the model and the view. It is responsible for the behavior of the widget when properties value changed.

### 3. A Step-by-Step Example

This example will create a simple bar graph widget which can display a numeric PV value.

#### 3.1 Pre-requirements

The widget plugin will need to have BOY plugin (org.csstudio.opibuilder) as dependecy, so you need to have BOY plugin and its dependent CSS plugins installed in your Eclipse environment. You can simply copy BOY plugin and its dependencies from your CSS plugin folder to your Eclipse `dropin` folder. If you are a CSS developer, you just need to import these plugins into your workspace from your Mercurial repository.

#### 3.2 Creating a new plugin

Create a new plugin project with all the default settings. Add plugin org.csstudio.opibuilder as dependency.

#### 3.3 Creating the widget model

The class `org.csstudio.opibuilder.model.AbstractWidgetModel` is the base class of all BOY widget models. All the common widget properties are defined in `AbstractWidgetModel`:

- Name
- X, Y, Width, Height
- Foreground Color, Backgournd Color
- Enable, Visible
- Border Style, Border Color, Border Width
- Scripts, Rules and Actions

`AbstractWidgetModel` defines two abstract methods you have to implement within your custom model.The `getTypeID()` method should return a unique ID which is used by the BOY framework to identify your model class. The `configureProperties()` method is called by the BOY framework during the instantiation of your model class. You can use it to equip your own widget model with custom properties. To do so, you need to call `addProperty(..)` to add a new property to the model’s property set. The signature of the addProperty(..) method requests a property ID that must be unique within the widget model and an instance of `org.csstudio.opibuilder.properties.AbstractWidgetProperty` that defines the property type. The supported property types are described at here. You can also create your own property type by extending `org.csstudio.opibuilder.properties.AbstractWidgetProperty`.

Note: There is a convention between property id and property name, which means the property id should be lower case of property name and the space in property name should be replaced with underscore. For example, the property id of `PV Name` is `pv_name`. With this convention, user can easily guess the property id in script.

The `class org.csstudio.opibuilder.model.AbstractPVWidgetModel` is the base class of all widgets that have the capability of connecting PVs. It provides additional common widget properties:

- PV Name
- PV Value (This property is an inner property which will not be shown in property sheet).
- BackColor Alarm Sensitive
- ForeColor Alarm Sensitive
- Border Alarm Sensitive

All Container Widgets must subclass `org.csstudio.opibuilder.model.AbstractContainerModel`.

Our simple bar graph widget will need to connect PV, so it needs to subclass `AbstractPVWidgetModel`. We will add two double properties to this widget: `Min` and `Max`.

    public class SimpleBarGraphModel extends AbstractPVWidgetModel{

    	/** Lower limit of the widget. */
    	public static final String PROP_MIN = "max"; //$NON-NLS-1$

    	/** Higher limit of the widget. */
    	public static final String PROP_MAX = "min"; //$NON-NLS-1$

    	public final String ID = "org.csstudio.opibuilder.widgetExample.SimpleBarGraph"; //$NON-NLS-1$

    	/**
    	 * Initialize the properties when the widget is first created.
    	 */
    	public SimpleBarGraphModel() {
    		setForegroundColor(new RGB(255, 0, 0));
    		setBackgroundColor(new RGB(0,0,255));
    		setSize(50, 100);
    	}

    	@Override
    	protected void configureProperties() {
    		addProperty(new DoubleProperty(PROP_MIN, "Min", WidgetPropertyCategory.Behavior, 0));
    		addProperty(new DoubleProperty(PROP_MAX, "Max", WidgetPropertyCategory.Behavior, 100));
    	}

    	@Override
    	public String getTypeID() {
    		return ID;
    	}

    	/**
    	 * @return the lower limit
    	 */
    	public double getMin(){
    		return getCastedPropertyValue(PROP_MIN);
    	}

    	/**
    	 * @return the higher limit
    	 */
    	public double getMax(){
    		return getCastedPropertyValue(PROP_MAX);
    	}
    }

#### 3.4 Creating the view

The view is responsible for the graphical representation of the widget. All widget view must implement the interface of `org.eclipse.draw2d.IFigure`. The class `org.eclipse.draw2d.Figure` has implemented most of the functions, most of BOY widgets figure extended this class.In most cases, the widget figure should be drawn in the function of `paintClientArea()`. The client area is the area that is inside the border. So paint in this area will not affect the border area. BOY will automatically draw borders for all widgets based on the border setting, so you do not need to consider the border in most cases.

    public class SimpleBarGraphFigure extends Figure {

    	private double min =0;
    	private double max = 100;
    	private double value = 50;

    	@Override
    	protected void paintClientArea(Graphics graphics) {
    		super.paintClientArea(graphics);
    		//fill background rectangle
    		graphics.setBackgroundColor(getBackgroundColor());
    		graphics.fillRectangle(getClientArea());

    		//fill foreground rectangle which show the value's position
    		graphics.setBackgroundColor(getForegroundColor());
    		//coerce drawing value in range
    		double coercedValue = value;
    		if(value < min)
    			coercedValue = min;
    		else if (value > max)
    			coercedValue = max;
    		int valueLength = (int) ((coercedValue-min)*getClientArea().height/(max-min));
    		graphics.fillRectangle(getClientArea().x,
    				getClientArea().y + getClientArea().height -valueLength,
    				getClientArea().width, valueLength);
    	}

    	public void setMin(double min) {
    		this.min = min;
    		repaint();
    	}

    	public void setMax(double max) {
    		this.max = max;
    		repaint();
    	}

    	public void setValue(double value) {
    		this.value = value;
    		repaint();
    	}

    	public double getValue() {
    		return value;
    	}
    }

#### 3.5 Creating the controller (editpart)

BOY widget controller basically are special GEF edit parts. You don't need in-depth knowledge about GEF for most regular widgets. BOY provides a comfortable framework for that. If you want to have special behaviors on your widgets, for example, changing the behavior when you are clicking or draging the widget, you may need to have further knowledge about GEF.

All BOY widget controllers must subclass `org.csstudio.opibuilder.editparts.AbstractWidgetEditPart`. For widget that needs to connect to PV, its controller need to subclass `org.csstudio.opibuilder.editparts.AbstractPVWidgetEditPart`.

    public class SimpleBarGraphEditpart extends AbstractPVWidgetEditPart {

    	/**
    	 * Create and initialize figure.
    	 */
    	@Override
    	protected IFigure doCreateFigure() {
    		SimpleBarGraphFigure figure = new SimpleBarGraphFigure();
    		figure.setMin(getWidgetModel().getMin());
    		figure.setMax(getWidgetModel().getMax());
    		return figure;
    	}

    	/**Get the widget model.
    	 * It is recommended that all widget controller should override this method.
    	 *@return the widget model.
    	 */
    	@Override
    	public SimpleBarGraphModel getWidgetModel() {
    		return (SimpleBarGraphModel) super.getWidgetModel();
    	}

    	@Override
    	protected void registerPropertyChangeHandlers() {
    		// The handler when PV value changed.
    		IWidgetPropertyChangeHandler valueHandler = new IWidgetPropertyChangeHandler() {
    			public boolean handleChange(final Object oldValue,
    					final Object newValue,
    					final IFigure figure) {
    				if(newValue == null)
    					return false;
    				 ((SimpleBarGraphFigure) figure).setValue(ValueUtil.getDouble((IValue)newValue));
    				return false;
    			}
    		};
    		setPropertyChangeHandler(AbstractPVWidgetModel.PROP_PVVALUE, valueHandler);

    		//The handler when max property value changed.
    		IWidgetPropertyChangeHandler maxHandler = new IWidgetPropertyChangeHandler() {

    			public boolean handleChange(Object oldValue, Object newValue, IFigure figure) {
    				((SimpleBarGraphFigure) figure).setMax((Double)newValue);
    				return false;
    			}
    		};
    		setPropertyChangeHandler(SimpleBarGraphModel.PROP_MAX, maxHandler);

    		//The handler when min property value changed.
    		IWidgetPropertyChangeHandler minHandler = new IWidgetPropertyChangeHandler() {

    			public boolean handleChange(Object oldValue, Object newValue, IFigure figure) {
    				((SimpleBarGraphFigure) figure).setMin((Double)newValue);
    				return false;
    			}
    		};
    		setPropertyChangeHandler(SimpleBarGraphModel.PROP_MIN, minHandler);

    	}

    	@Override
    	public Object getValue() {
    		return ((SimpleBarGraphFigure)getFigure()).getValue();
    	}

    	@Override
    	public void setValue(Object value) {
    		if(value instanceof Double)
    			((SimpleBarGraphFigure)getFigure()).setValue((Double)value);
    	}
    }

The `doCreateFigure()` method creates figure by instantiating a SimpleBarGraphFigure. It is also reponsible for initializing the figure with the property value in model. The `registerPropertyChangeHandlers()` method is responsible for the behavior of the widget when its property value changed. A property can have more than one handler. All handlers must implement the interface of `IWidgetPropertyChangeHandler`. The `handleChange()`method will handle the property value change in which `oldValue` is the old property value, `newValue` is the new property value, `figure` is the widget figure.

The editpart of a widget is the `widgetController` in BOY javascript. You can add additional methods here for javascript use.

#### 3.6 Register the widget with BOY

So far, we have finished the coding part. Now, we need to register this widget with BOY so that BOY can recognize and list this widget in its widget palette.

Open the `MANIFEST.MF` file in your plugin project. Go to the tab of `extensions`. Add an extension `org.csstudio.opibuilder.widget`. Configure the extension properties as shown in the figure.

- typeId

  The global unique typeId of the widget which is the return value of the method `getTypeID()` in widget model.

- name

  The name of the widget, which will appear in the widget palette.

- category

  The category of the widget. The default categories include Graphics, Monitors, Controls and Others. You can create a new category by giving a new category name, but this is not recommended.

- icon

  The icon of the widget.

- model_class

  The model of the widget.

- editpart_class

  The editpart class of the widget.

- onlineHelpHtml

  The online help html file.

- description

  The description of the widget, which will be displayed as the tooltip.

#### 3.7 Hook widget help with BOY online help

You can hook up the widget's help page with BOY online help from toc anchor `../org.csstudio.opibuilder/toc.xml#thirdPartyWidgets`.

#### 3.8 Integrate the widget into CSS

If you are a CSS developer, now your have finished the plugin project. You can integrate it into CSS by including this plugin into your CSS build.

Here we will discuss how to add this widget to an existing CSS.

Open the `MANIFEST.MF` file and go to the `overview` tab. Click `Export Wizard`. Change the Destination Directory to the any directory you want. Leave other settings as default and click `Finish`. When exporting finished, go to the directory you set and copy the jar file in `plugin` folder to the CSS `dropins` folder. Start CSS, you will see the Simple Bar Graph widget in you CSS BOY palette! You can test it by setting `PV Name` to `sim://noise`, setting `max` to 5, `min` to -5. You will see it already has all the BOY PV widget functions such as PV connection, Border alarm sensitive and so on.

*This project zip file can be found at BOY Examples->Miscellaneous->CustomizedWidgetExample.zip. You can import it into your Eclipse workspace by selecting `File->Import...->Existing Project into workspace`.

#### 3.9 Resources

There are lots of exist resources come with BOY, such as `linear scale`, `round scale`, `AbstractBoolFigure`, `AbstractScaledWidgetFigure` and so on. They are included in the plugins of `org.csstudio.swt.widgets` and `org.csstudio.swt.xygraph`. A draw2d figure can be composed of several other figures, so you can build your figure easily with these figure parts. For example, the Byte Monitor widget is composed of an array of LED figures. It is recommended to check if there are existing figure parts available for your new widget before you start to create your widget figure from scratch.

### 4. Advanced Topics

#### 4.1 Create Popup Menu for the Widget

The selected widgets in edit mode or the widget on which the context menu was actived in run mode is the editpart of the widget. So it is easy to add a popup menu to this widget by adding an `objectContribution`.

The Action Class can simply extend `org.csstudio.opibuilder.actions.AbstractWidgetTargetAction`, which provided method `getSelection()` to get the selected widgets.

    public class WidgetContextMenuExample extends AbstractWidgetTargetAction{

    	public void run(IAction action) {
    		MessageDialog.openInformation(null, getSelectedWidget().getWidgetModel().getName(),
    				"This dialog is opened from the Context menu of SimpleBar Graph.");

    	}

    	/**
    	 * @return the selected widget. In runmode, it is the editpart of the
    	 * widget on which the context menu was activated.
    	 */
    	protected final SimpleBarGraphEditpart getSelectedWidget() {
    		return (SimpleBarGraphEditpart)getSelection().getFirstElement();
    	}
    }

If you want the action can be Undoable/Redoable in editing mode, you need to create your `org.eclipse.gef.commands.Command` and execute the command by calling `execute(Command command)`. Please see `org.csstudio.opibuilder.widgets.actions.AddTabAfterAction.java` for example.

You can also make the popup menu only visible for editing mode or run mode by adding a `objectState` visibility. To make it only visible in editing mode, set name to `executionMode` and value to `EDIT_MODE`. To make it only visible in run mode, set name to `executionMode` and value to `RUN_MODE`. Here is the result of this example:

#### 4.2 Create BOY widget based on native SWT widget

To use SWT widgets in BOY, you need to subclass `org.csstudio.opibuilder.widgets.AbstractSWTWidgetFigure` for the figure. The model and editpart are same as regular BOY widgets. For an SWT widget, there are few drawbacks:

- You cannot change its order. Its order is always on top.
- You cannot hide part of the widget in grouping container.

Please see BOY Combo widget or Web Browser widget as examples.

#### 4.3 Customized Graphical Feedback

If you need some special behavior on the widget in editing mode, for example, keep the size of the widget in fixed ratio when user is resizing it, you can create you own customized graphical feedback by extending the extension point `org.csstudio.opibuilder.graphicalFeedbackFactory`. The class must implement `org.csstudio.opibuilder.feedback.IGraphicalFeedbackFactory`. Two abstract graphical feedback factory are provided in `org.csstudio.opibuilder.widgets`. `org.csstudio.opibuilder.widgets.feedback.AbstractFixRatioSizeFeedbackFactory` will provide the fixed size ratio behavior when user resizes it. `org.csstudio.opibuilder.widgets.feedback.AbstractPolyFeedbackFactory` will provide the behavior like Polyline or Polygon widgets.

Note: It is not recommended to give widget customized graphical feedback or implement your own `IGraphicalFeedbackFactory` if it is not super necessary, because the details are very complicated and need lots of GEF knowledge. If it is not desinged carefully, it is very possible to have bugs introduced.