The shapes in JSynoptic are based on the Java2D API. See the Java tutorial for information about Java2D.
The JSynoptic shapes thus implement the Shape interface from Java2D. Additionally, they all extend the simtool.shape AbstractShape. Unless there is a very special reason not to use it, we recommend to to also extend the AbstractShape. Indeed, this class makes the bridge between the Java2D API and JSynoptic listener events.
Another way to use JSynoptic shapes is to write directly in the Graphics2D object provided. See the AbstractShape draw method. Then, one can display images, draw pixel by pixel and display using double-buffering, etc. Any shape can be done this way.
Abstract shapes also have an anchor (the _ox and _oy fields), and offset from this anchor (_x and _y fields) which allows to align them on the grid. Also, width and height (_w and _h) information is available. If you fill in correctly these fields, there is no need to overload the bounding methods.
Abstract shapes are cloneable. Please make sure to overload the cloneShape method if you need deep copying. Cloning is used in copy/paste operations, so make sure it works.
Examples of shapes are included in the builtin and the JFreeChart plugins. You can also use the Abstract1DShape and Abstract2DShape in the builtin plugin if your shape makes use of respectively 1 or 2 dynamic colors (contour and filling, for example).
Index of contents
Any shape in JSynoptic making use of data sources for its parameters should register as a listener on this data source. See the presentation of the data sources API for more information.
If your shape listens on data sources, which is often the case, don't forget to register an end-notification listener. It is much more efficient to just take notice when a data source event occurs, and process all them at the end, than to do some heavy processing each time. This is especially true for several data sources coming from the same collection. Your shape will be much more efficient if it waits till all events generated from a single external reason arrived, than if it blindly updates itself at each individual event. For example, if new data arrives on the network for a data source collection, and your shape listens on several data sources, you will find it more efficient to wait till all data sources send their “index range changed”, “value range changed” or “data info changed”, and update only once to take in account all these.
Whenever the shape changes (whether in reaction to a data source change or for any other reason), it should notify its own listeners. Please use the notifyChange(...) methods in AbstractShape to do that. The change notification can be restricted to a rectangle, if only this rectangle area changed. The system will then ask the shape to redraw itself. Please don't try to redraw the shape directly, as this will probably hang the Java virtual machine (see next paragraph).
In fact, refresh events are not immediately processed. This is because the shapes usually receive data change notifications from independent threads (timer, socket, etc...) and the Java virtual machine can only process graphic changes in the graphic thread. Thus, we recommend you not to access any part of the JSynoptic GUI and not to process any operation on a Graphics2D object during the listener callbacks. This isn't a JSynoptic specific limitation, but a rather general Java rule. You have been warned, don't complain later on!
Also, to avoid hogging the CPU, all notifications are condensed into a single notification event by the AbstractShape at a maximum frequency. By default, this frequency is limited to 10 refreshes per second, which is more than enough for monitoring live data (and if you want to play a movie, well, JSynoptic could probably do it but this may not be the best choice...). See the source of AbstractShape for more information (and a ready-to-use hack on this subject...).
Index of contents
It is generally a good idea to include a few contextual action for your shapes. If only, for example, to configure it with a dedicated dialog.
To do so, the shape has to implement the ContextualActionProvider interface. The description is the same as for the sources, so please refer to that section. Additionally, more contexts are used for the shapes than for the sources.
Here is a description of each context. The other contextual arguments to the getAction method are the mouse position (x,y), and the object selected in the source tree on the left panel.
- EDITOR_CONTEXT. This is the standard context, when the user is right-clicking on the shape in the synoptic editor window
- SOURCELIST_CONTEXT. This context is used when the user right-clicks in the source tree in the left panel, and a shape is selected in the editor window. In this case, the shape actions are also appended to the popup, with this context. This is quite useful for the user : for example, it is possible to add several Y variables to a standard plot without leaving the source tree.
- MOUSE_OVER_CONTEXT.This context is used when the mouse is moved over the shape. The shape isn't necessarily selected.
- MOUSE_OUT_CONTEXT.This context is used when the mouse was over the shape and leaves it. The shape isn't necessarily selected.
- MOUSE_PRESSED_CONTEXT.This context is used when the mouse was over the shape and leaves it. The shape isn't necessarily selected.
- SHAPELIST_CONTEXTThis context is used when the user has selected the shape list tab of the left panel, and she right-clicks in the editor to create a new shape. If a shape is selected, then its actions for this context are appended to the popup menu.
Please refer to the shapes in the builtin and JFreeChart plugin for practical implementation examples.
Index of contents
Don't forget to make your shape Linkable. The implementation cost of doing so is very low, all that is needed is to implement the Linkable interface from the jsynoptic.base package. See the shapes in the builtin and JfreeChart plugins for examples.
The shape itself should be serializable, or synoptics using that shape won't be saved. Do not serialize data sources. Rather, make them transient and in the read/write Object methods, use the DataSourcePool helper methods (readDataSource and writeDataSource).
The doAction method is called from within the Java event thread. Thus, all parts of the JSynoptic GUI are safely accessible. On the other hand, don't do anything long in this thread, or the GUI will be blocked. Rather, use another thread for long actions, possibly with the LongAction helper class in the jsynoptic.ui package.
Index of contents