Vadiraj's Blog

My experiments with Java

Part 3 - XML Multiview + Visual Library

Thursday Mar 08, 2007

Finally I am here with Part 3. The last two parts, Part 1 and Part 2 discussed how to start with the XML multiview and explained the code with reference to the Book example. This part discusses about how to integrate XML multiview and Visual Library by displaying a graph view of Book and chapters on a multiview. We will be editing the Book example code and extending it.

  1. The End result
  2. Introduction
  3. Setting up dependencies
  4. Create new multiview
  5. Create graph
  6. Add sync logic

The End result

If you are just interested the sources for the full series (XML multiview+Visual Library 2.0), download here.

Here is the original Book example design multiview (Not modified in this series):

 

Here is the Visual Library in action on a new multiview: Looks colorful than the earlier screenshot?



Introduction

Visual Library 2.0 is an improved version of the old graph library existed before in Netbeans. Here is the project home page and here is the documentation. It allows to create graphs, in an easy and quick way. You would be surprised to see how easy to create graphs with functions like move, zoom, in-place edit, add connectors and create satellite view. The tutorial written by Geertjan describes the ways in which we can do all these. The current tutorial is based on the Geertjan's tutorial.

Setting up dependencies

Recent Update: As of March 2007, the Visual Library 2.0 was released as a stable API and is available in Netbeans 6.0 update center. 

The Visual Library 2.0 is not yet exposed as a standard module API to which we can add the dependency directly. There might be multiple ways to add "Visual Library API" dependency to the module. In all ways, we need to get the jars first. Again there are multiple ways to get these jars. One is described here, which explains how we can download the sources and build locally the required libraries. Here we can download and build the whole IDE or download only required sources and build the distributions for Visual Library and Utilities API. We can also download the jar files hosted on the home page, from here. If you do not want to download the entire source and build the IDE locally, then get 'org-netbeans-api-visual.jar' and 'org-openide-util.jar' either by downloading the hosted distributions or minimal building as described here. We can include these jar files as a 'Library module'. As the original Book example is a single module, lets create a new 'module suite' and include the Book example and the new Library modules in it.

First, Create a new module suite and name it 'BookExampleModuleSuite'. Then expand the module suite project node in the project view and right click on the 'Modules' node and choose 'Add Existing' and choose the Book Example module, that we already have. Now the earlier Book Example module is part of this new module suite. Let's run the module suite now by choosing Run/Run Main Project from the menu, or if the module suite is not the main project, then you can right click on the module suite project node and choose, Run.

Download the Visual Library 2.0 jar files using one of the above mentioned method. Adding the library module for them is simple. Right click on the 'Modules' node and choose 'Add Library' and choose the org-netbeans-api-visual.jar and click 'Next'. Click 'Next' again and click 'Finish' to complete the process. A new module of the type 'Library module' will be created in the module suite. Repeat the steps for org-openide-util.jar. Now we have the dependencies available for use. We will start using them shortly.

Next we will go back to the multiview!

Create new multiview

Open BookDataObject.java in the editor. Goto getMultiViewDesc() method and add a new line as highlighted below:

protected DesignMultiViewDesc[] getMultiViewDesc() {	
return new DesignMultiViewDesc[]{
new DesignView(this,TYPE_TOOLBAR),
new GraphView(this,TYPE_TOOLBAR)
}

 The new line

new GraphView(this,TYPE_TOOLBAR)

adds a new multiview. Its underlined red in the editor as we need to yet create it. Here is the code for new GraphView mutliview:

private static class GraphView extends DesignMultiViewDesc {
private int type;
GraphView(BookDataObject dObj, int type) {
//super(dObj, "Design"+String.valueOf(type));
super(dObj, "Graph");
this.type=type;
}

public org.netbeans.core.spi.multiview.MultiViewElement createElement() {
BookDataObject dObj = (BookDataObject)getDataObject();
return new GraphToolBarMVElement(dObj);
}

public java.awt.Image getIcon() {
return org.openide.util.Utilities.loadImage(
"org/netbeans/modules/bookmultiview/Datasource.gif"); //NOI18N
}

public String preferredID() {
return "book_multiview_design"+String.valueOf(type);
}
}

 This class is much the same as DesignView, except it creates GraphToolBarMVElement. The line where this multiview element is created is underlined with red wavy line. Let's create GraphToolBarMVElement class now.

/*
* GraphToolBarMVElement.java
*/

package org.netbeans.modules.bookmultiview;

import org.netbeans.modules.bookmultiview.GraphPanelFactory;
import org.netbeans.modules.xml.multiview.ToolBarMultiViewElement;
import org.netbeans.modules.xml.multiview.ui.SectionView;
import org.netbeans.modules.xml.multiview.ui.ToolBarDesignEditor;


public class GraphToolBarMVElement extends ToolBarMultiViewElement{

private ToolBarDesignEditor comp;
private GraphPanelFactory factory;
private BookDataObject dataObject;
private SectionView view;

/** Creates a new instance of GraphToolBarMVElement */
public GraphToolBarMVElement(BookDataObject dataObject) {
super(dataObject);
this.dataObject = dataObject;
}

public SectionView getSectionView() {
return view;
}

}

Right click on the mutliview package and choose New/Java Class. Type the name of the class as :  GraphToolBarMVElement and click Ok. Netbeans creates this new class and opens it in the editor. Make this class to extend ToolBarMultiViewElement. Red wavy line appears again on the class, place the cursor on the class, a hint bulb appears on the left margin. Choose that to import the required classes and implement the abstract methods. There is only one method : getSectionView()  which is abstract and we need to code. Change the constructor to accept BookDataObject  (this the model class which we will be using to create the graph). Define an instance variable to hold this data object. Define another instance variable called view,  which is of the type SectionView. Define two more instance variables of type ToolBarDesignEditor and GraphPanelFactory. Right click on the editor and choose 'Fix imports' to import the necessary classes. Return the 'view' object from getSectionView(). Later we will create a section view and assign it to this instance.

First, we will add code to the constructor to initialize the toolbar editor as follows: The new additions are highlighted.

/** Creates a new instance of GraphToolBarMVElement */
public GraphToolBarMVElement(BookDataObject dataObject) {
super(dataObject);
this.dataObject = dataObject;
comp = new ToolBarDesignEditor();
factory=new GraphPanelFactory(comp,dataObject);
setVisualEditor(comp);

}

There two lines which show errors and those lines are where we have GraphPanelFactory. We will create this new class shortly.

Next we will create the section view. We just need one section and a node to hold up a section panel. So here is the inner class called GraphView.

private class GraphView extends SectionView {
GraphView(BookDataObject dObj) {
super(factory);

Children rootChildren = new Children.Array();
Node root = new AbstractNode(rootChildren);
try {
Book book = dObj.getBook();
Node bookNode = new BookNode(book);

rootChildren.add(new Node[] {bookNode});
addSection(new SectionPanel(this,bookNode,book)); //NOI18N

} catch (java.io.IOException ex) {
System.out.println("ex="+ex);
root.setDisplayName("Invalid Book");
}
setRoot(root);
}
}
private class BookNode extends org.openide.nodes.AbstractNode {
BookNode(Book book) {
super(org.openide.nodes.Children.LEAF);
setDisplayName(book.getTitle());
//setIconBase("org/netbeans/modules/web/" +
"dd/multiview/resources/class"); //NOI18N
}
}

Note that GraphView extends from SectionView.  It passes the reference to panel factory to the super class to enable the super class to invoke a particular Section inner panel to embed in this section view. We create a root node and a Book node and add it to the root's children. (Refer Nodes API for information on Nodes). We add a new section in this view by calling addSection().  Finally we set the root node by calling setRoot(). Note that we are not creating sections and section containers for chapters here as it was so in the original example. We will represent chapters as graph nodes on the graph. The BookNode class is the same class as of BookToolBarMVElement class, you can copy it here or remove the private access specifier for this class in there. Again, right click and choose 'Fix imports' to import the necessary classes.

One last piece is missing to create and initialize the SectionView. This is done in componentShowing() method, which is overridden here. The method is as follows:

    public void componentShowing() {
super.componentShowing();
view=new GraphView(dataObject);
comp.setContentView(view);
try {
view.openPanel(dataObject.getBook());
} catch(java.io.IOException ex){}
ex.printStackTrace();
}

Note how we create the GraphView (which is a section view) and assign it to the toolbar design editor. We open the panel by passing the appropriate model element, which is Book in this case. This key is actually used by the Graph Panel Factory to create a particular section inner panel and return it.

Now we create the GraphPanelFactory. The reason to create new panel factory is to keep the original example still working, so that we can compare. This new panel factory will handle the panel display in the new multiview that we are currently creating. You can copy the existing PanelFactory class and paste that as a new file and rename it to GraphPanelFactory. Here is the class:

 


package org.netbeans.modules.bookmultiview;

import org.netbeans.modules.bookmultiview.bookmodel.Book;
import org.netbeans.modules.bookmultiview.bookmodel.Chapter;
import org.netbeans.modules.xml.multiview.ui.SectionView;
import org.netbeans.modules.xml.multiview.ui.ToolBarDesignEditor;
import org.netbeans.modules.xml.multiview.ui.SectionInnerPanel;

public class GraphPanelFactory implements
org.netbeans.modules.xml.multiview.ui.InnerPanelFactory {
private BookDataObject dObj;
ToolBarDesignEditor editor;

/** Creates a new instance of ServletPanelFactory */
GraphPanelFactory(ToolBarDesignEditor editor, BookDataObject dObj) {
this.dObj=dObj;
this.editor=editor;
}

public SectionInnerPanel createInnerPanel(Object key) {
if (key instanceof Book)
return new BookPanel((SectionView)editor.getContentView(),
dObj, (Book)key);
else
return new ChapterPanel((SectionView)editor.getContentView(),
dObj, (Chapter)key);
}
}

 OK, all set to verify our new additions. We have not yet created the graph view yet, but its worth running once before we start the next section.

Right click on project node and click on Clean and Build Project. Once it completes, then again right click on the project node and choose Run. Now the fun part begins, a new IDE (which is actually Netbeans platform application) opens up and allows us to test our module in that. Here is how it looks:

Now we need open abc.story file (this is the only file that we have, for which the example contains the multiview). For that, lets open the module example here in this new IDE. Choose File/Open Project, locate and select the Book example project that we are working on so far. Its ok :) to open it in this new IDE. Another marvel of Netbeans :)

Once you open the example project, expand the Project node, Source Packages node and finally, org.netbeans.modules node. You see the abc.story file as below:


Double click on abc.story file to open it in multiview. You can see still the original multiview and the new 'Graph' multiview in between the original 'Design' view and 'XML' view :

You can see that only the BookPanel inner panel contents are shown. This is because, when we constructed the multiview, we provided a single node for Book. Notice that although we have defined the GraphPanelFactory to return both BookPanel and ChapterPanel, because of the node structure, only BookPanel will get shown.

Ok, enough waiting now... Lets design graph.

Create graph

Let's copy BookPanel and create a new class called BookGraphPanel.  You can use copy and paste to create this new file quickly. You need to rename BookPanel_1 to BookGraphPanel once you paste the class. Switch to 'Design' view (I am referring to Matisse GUI builder view here, see even thats built on multiview) and remove all GUI components by selecting them and hitting 'Delete' button on the keyboard.

Then we will add a new JScrollPane to the existing panel and expand it to fill horizontally and fill vertically from top till about 80%. Then we add two buttons called 'addButton' and 'removeButton'. We rename this JScrollPane to 'graphPane'. This graph pane will hold the graph. After all this, BookGraphPanel will look like this:

Let's switch to 'Source' view. There will be lot of red wavy lines (error lines) everywhere in the file, because we removed all original UI fields. Remove all lines which have the error lines. Below is the BookGraphPanel code, with all error lines removed:

 


package org.netbeans.modules.bookmultiview;
import org.netbeans.modules.bookmultiview.bookmodel.Book;
import org.netbeans.modules.xml.multiview.ui.SectionInnerPanel;
import org.netbeans.modules.xml.multiview.ui.SectionView;

public class BookGraphPanel extends SectionInnerPanel {
Book book;
BookDataObject dObj;
/** Creates new form BookGraphPanel */
public BookGraphPanel(SectionView view, BookDataObject dObj,
Book book) {
super(view);
this.dObj=dObj;
this.book=book;
initComponents();

}

public void setValue(javax.swing.JComponent source, Object value) {
}

public void documentChanged(javax.swing.text.JTextComponent comp,
String value) {
}

public void rollbackValue(javax.swing.text.JTextComponent source) {
}

protected void signalUIChange() {
dObj.modelUpdatedFromUI();
}

public void linkButtonPressed(Object ddBean, String ddProperty) {
}

public javax.swing.JComponent getErrorComponent(String errorId) {
return null;
}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">
// </editor-fold>


// Variables declaration - do not modify
private javax.swing.JButton addButton;
private javax.swing.JScrollPane graphPane;
private javax.swing.JButton removeButton;
// End of variables declaration

}

Greating Visual Library 2.0 graph involves creating a Scene and Widgets. The Scene is the container which holds the other Widgets. There are many types of Widgets available, for example, a LabelWidget can display a line of text and an ImageWidget can show an image. We create a Scene and attach a layout to it and add Widgets, much like the same we do in Swing, we create a container such as JPanel and attach the layout optionally and add components on top of it. For more information on this, refer the Visual Library 2.0 tutorial.

Let's start by creating a Scene and setting a layout to it. We will declare the following instance variables.

Scene scene = new Scene();
int x = 20, y = 130;
LayerWidget mainLayerWidget;
LayerWidget connectionLayerWidget;
LabelWidget mainWidget;

The mainLayerWidget will hold the widgets and connectionLayerWidget will hold the connection widgets that we will create to connect chapters to the book. the mainWidget will represent the Book. By adding widgets to one layer and connections to another layer, we achieve separation and each layer can be manipulated independent of each other. The Scene represents the scene (canvas area) on which the graph will be rendered. The 'x' and 'y' variables are used for placement of the chapter widgets on the scene as we see shortly. The next block of code shows how to initialize these instance variables:

scene.setLayout(LayoutFactory.createAbsoluteLayout());
mainLayerWidget = new LayerWidget(scene);
scene.addChild(mainLayerWidget);
connectionLayerWidget = new LayerWidget(scene);
scene.addChild(connectionLayerWidget);

For this tutorial, we will use the Absolute layout, but there are many other layouts available in the Visual Library 2.0 API that readily place the widgets on the screen in a systematic manner.

Next we will create the mainWidget, which will represent the Book on the graph. Note that this example uses LabelWidget only, although there are many widgets available such as ImageWidget for use. You will get more information on this here : tutorial.

try {

mainWidget = new LabelWidget(scene,
dObj.getBook().getTitle());
mainWidget.setAlignment(LabelWidget.Alignment.CENTER);
mainWidget.setBorder(new RoundedBorder(
10,10,9,9, new Color(153,204,250), Color.BLACK)
);
mainWidget.setPreferredLocation(
new Point(80, 10)
);
mainWidget.getActions().addAction(
ActionFactory.createMoveAction()
);

mainLayerWidget.addChild(mainWidget);

Chapter chapters[] = dObj.getBook().getChapter();
for(int i = 0; i < chapters.length ; i++) {
addChapterToScene(mainWidget, chapters[i],
connectionLayerWidget, mainLayerWidget);
}


JComponent sceneView = scene.createView();
graphPane.setViewportView(sceneView);

} catch (IOException ex) {
ex.printStackTrace();
}

mainWidget.setPreferredLocation()    is used to fix the location of this book widget on the screen. Remember we are using the Absolute layout and as in the case of swing layouts, here too we need to manage the widget placement, if we use this layout. The createMoveAction() will enable moving of this widget on the screen. Note we add this action to the widget and not to the scene. We get the chapters from the Book data objects and create widgets for each of them, in a separate method : addChapterToScene().  We will see this method shortly. Note at the end of the above code block that the scene actually gives us a JComponent, that we can embed on top of JPanel or JFrame. Here we embed that in the scrollpane by calling setViewportView()

The following code block shows addChapterToScene() method.

 

private Widget addChapterToScene(final LabelWidget mainWidget,
final Chapter chapter,
final LayerWidget connectionLayerWidget,
final LayerWidget mainLayerWidget) {
LabelWidget chapterWidget = new LabelWidget(scene,
chapter.getTitle());
chapterWidget.setPreferredLocation(new Point(x, y));

chapterWidget.setBorder(new RoundedBorder(10, 10, 3, 3,
new Color(255,255,153), Color.BLACK));
chapterWidget.getActions().addAction(
ActionFactory.createMoveAction()
);

mainLayerWidget.addChild(chapterWidget);

ConnectionWidget connectionWidget =
new ConnectionWidget(scene);
connectionWidget.setSourceAnchor(
AnchorFactory.createRectangularAnchor(
chapterWidget
)
);
connectionWidget.setTargetAnchor(
AnchorFactory.createRectangularAnchor(mainWidget)
);
connectionLayerWidget.addChild(connectionWidget);

x+= 150; y+= 20;
if(x > 400 && x <= 590) {
x+= 150;
}
if(x >= 670) {
y+= 80;
x = 20;
}
return chapterWidget;
}

This method will create a widget for each chapter that is passed into it on the screen. It uses the 'x' and 'y' instance variables to place the widgets appropriately on the screen so that the widgets wont clutter up. This method also creates a connector (its called 'Anchor'). We create anchors on the connection layer. The connection widget represents this anchor.Here 'RounderBorder' is used, which essentially goes right visually with a label widget. But there are a many borders available.

So let's run the example now and see how it looks. When the new IDE opens up, you can open the same module project inside it and expand the project node, Source Packages and org.netbeans.modules nodes in that order. Double click on the abc.story file and it opens the multiview editor on the right side. Click on the 'Graph' multiview and you can see the graph as shown below:

 

Its so easy to create a graph, and add cool things like move, just write one line and the move functionality will be added. The same way, we can add the zoom functionality even. Just call

scene.getActions().addAction(ActionFactory.createZoomAction());

and you have the zoom functionality!! Use the scroll wheel on the mouse to zoom in and zoom out :) 

Add synchronization logic
 

Ok, lets connect the graph to the XML file. This is the only place which graph view is missing from the 'design' view, or the original multiview. As you would have observed earlier, there are two buttons on the graph view to add and remove chapters from the XML file.

First let's code for 'Add chapter'. We will create a simple panel which will ask the details about the new chapter. Right click on the org.netbeans.modules.bookmutliview and choose 'New/File/folder'. On the wizard panel that comes up, expand Java GUI Forms and choose JPanel on the right. Name the panel as 'AddChapterPanel' click 'Finish'.

Design the panel as shown above. Add label and textfield (named chapterTitleTextField) for the chapter title. Add another label and text area (named paragraphTextArea) for collecting paragraph data. Note that although we can add multiple paragraphs, only one will be considered here.

We will use this panel to collect information about the new chapter. We will show this panel using the Netbeans Dialogs API. For this we will add new dependency for Dialogs API. We will add event handles for the two buttons as follows:

We will start with 'Add New Chapter'. Add an action listener event to the button by right clicking on it in the design view and choosing 'Events/Action/actionPerformed'. In this event handling, we will call a utility method called 'addNewChapter()'.

 

  private void addNewChapter() {
AddChapterPanel addNewChapterPanel =
new AddChapterPanel();
DialogDescriptor addDialog = new DialogDescriptor(
addNewChapterPanel, "Add New Chapter"
);
Object returnValue = DialogDisplayer.getDefault().notify(addDialog);
if(returnValue != null && returnValue.equals(new Integer(0))) {
Chapter newChapter = new Chapter();

newChapter.setTitle(addNewChapterPanel.getChapterTitle());
newChapter.setParagraph(new String[]{
addNewChapterPanel.getParagraph()}
);

try {
dObj.getBook().addChapter(newChapter);
} catch (IOException ex) {
ex.printStackTrace();
}
dObj.modelUpdatedFromUI();

// add a new widget for this new chapter
Widget newWidget = addChapterToScene(mainWidget, newChapter,
connectionLayerWidget, mainLayerWidget);
scene.validate();
newWidget.repaint();
}
}

private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {
addNewChapter();
}

Note, how the AddChapterPanel is embedded inside the Netbeans Dialog Descriptor. Examining the return values of dialon displayer is little tricky, although using static constants this can be simplified. We will create a new Chapter object, assign its title and a paragraph and set it back to the BookDataObject. Once we alter the model object (BookDataObject, in our case), we need to notify the XML multiview system that data has changed so that it can re-sync. The notification call is

dObj.modelUpdatedFromUI();

After this, we will create a new widget for this new chapter and place it on the screen and refresh the screen. Viola! we have the new chapter added to the graph and the XML file too at the same time!.

 

 

You will see the new chapter added to the graph highlighted in the image below: 

 

At the same time, if you switch to the XML view, you can see that there is a new chapter added at the end of the XML file.

Note, here that, we are handling the two way synchronization ourselves at least by part. We are calling the notification method to tell the XML multiview system when to do sync, while in the normal XML multiview, this is handled for you.

Now let's implement 'Remove Chapter'. 

private void removeButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
Widget selectedWidget = scene.getFocusedWidget();
if(selectedWidget instanceof LabelWidget) {
String chapterName = ((LabelWidget)selectedWidget).getLabel();
Chapter selectedChapter = book.getChapterByTitle(chapterName);
if(selectedChapter != null) {
book.removeChapter(selectedChapter);
dObj.modelUpdatedFromUI();
mainLayerWidget.removeChild(selectedWidget);
// Remove the connection widget.
for(Widget widget : connectionLayerWidget.getChildren()) {
ConnectionWidget connectionWidget =
(ConnectionWidget)widget;
if(connectionWidget
.getSourceAnchor()
.getRelatedWidget().equals(selectedWidget)) {
connectionLayerWidget.removeChild(connectionWidget);
break;
}
}
scene.validate();
scene.repaint();
}
} else System.out.println("innerWidget is not an instance of label widget");

Again, add a action listener to the remove button and implement the event handling as above. Here we look for the selected widget from the scene and find out the related Chapter object and remove both widget (from scene) and chapter (from the model data object). Again here, we notify the XML multiview system that the data has changed by calling

dObj.modelUpdatedFromUI();

Note that we remove the widget as well as the connection widget attached to it. Finally we refresh the scene.

For this to work, we need to add 'SelectAction' to every widget (chapter) on the scene. Let's add that in the

addChapterToScene()

method as follows. This code is just after the new widget creation.

 

chapterWidget.getActions().addAction(
ActionFactory.createSelectAction(
new SelectProvider() {
public boolean isAimingAllowed(Widget widget, Point point, boolean b) {
return true;
}
public boolean isSelectionAllowed(Widget widget, Point point,
boolean b) {
return true;
}
public void select(Widget widget, Point point, boolean b) {
System.out.println("selected : " + widget);
widget.setBorder(new RoundedBorder(10, 10, 3, 3,
new Color(153,153,255), Color.BLACK));
scene.setFocusedWidget(widget);
widget.repaint();
}

}));

For me the select action is working only when the widget is double clicked. We change the border of the widget to make it visible when we select it. We also set the focused widget to the currently selected widget for the scene.

Here is remove in action: The chapter is selected for removal and its highlighted with blue color.

Here the chapter is gone after hitting 'Remove Chapter' button.

If you switch back to the XML view, before and after clicking 'Remove Chapter', you would notice that the XML file will always reflect the current status as of the graph. For example, if we add a chapter, a new chapter tag will be created in the XML file and if we select a chapter node on the graph and click 'Remove Chapter' button, the chapter tag will be gone. This is so easy right?

 

OK! we are done. We successfully integrated the Visual Library 2.0 with XML multiview with full functionalities from both APIs.

This is the end of a bit long 3 part series which showed you how to create the XML multiview with the Book example (Part 1 and 2) and this last part (Part 3) will showed you how to add Visual Library 2.0 support into the multiview. Go and download the sources for the final product from here.

See you soon with another adventure with Netbeans, XML multiview in particular!


Disclaimer: The modules described here are purely experimental so no guaranties. Use at your own risk.

 

[2] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg

Coming soon - Part 3 - XML Multiview + Visual Library

Tuesday Mar 06, 2007

Part 3 of the XML multiview + Visual LIbrary 2.0 series is coming soon. I have been busy with other things so please bear with me. Here is the screenshot of what Part 3 does to the Book Example: Right now, it does not look colorful and we will definitely going to add more colors to it. Stay tuned!

 

By the way how is this new eco theme looks?  

[5] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg

Part 2 - XML Multiview + Visual Library

Monday Feb 26, 2007

In the previous part, we explored the XML multiview, Book example and looking at how the sample works by starting off with DataObject. This part continues the discussion about the Book example to show how it provides custom views for the multiviews. So if you have not seen Part 1, you may want to go through  it now.

Contents 

  1. Introduction
  2. Toolbar view
  3. Constructing the node tree
  4. Section inner panels
  5. Panel Factory

 

Introduction

Each multiview elements can have any arbitrary swing UI. In XML multiview, the UI is divided into sections to enable displaying of sections according to the XML tags. Construction of UI is in two parts: one: provide the node structure of the entire view, two: implement the individual sections. The node structure holds up the whole UI.

Toolbar view

We will look at the  BookToolBarMVElement class. This class essentially implements the  node structure, creates and attaches section panels to the nodes.

The class extends ToolBarMultiviewElement, which extends  AbstractMultiViewElement of Multiview API to provide support for XmlMultiViewDataObject which is the dataobject here. The ToolBarDesignEditor is a special class which inherits the TopComponent indirectly. The ToolBarDesignEditor also implements ExplorerManager.Provider and co-ordinates Node selection. The ToolBarDesignEditor is divided into two: contentView, where the section panel goes and errorPane where the validation errors are shown. The PanelFactory belongs to XML Multiview and is a factory for creating different section panels for different XML tags.

Constructing the node tree

Each section view as the one above, constructs the Node structure. The Nodes API is used to build the structure with sections, section containers attached to them. There is a root node at line 48, which has children nodes. These root children carry the actual nodes as you can see at line 62. The Book Node and the Chapter Nodes are inner classes in this BookToolBarMVElement class and they extend AbstractNode. You basically build the sections out of section panels or section containers. Section panels hold the content for individual nodes (like for Book, in above figure) and Section containers hold many section panels, which in turn hold contents from single nodes (for Chapter, in above figure). So there are two things here: build your own node structure and add them to the root node and construct section panels and section containers and attach them to the node. The actual visual which should be displayed as part of each section, comes from SectionInnerPanels, which we will see next. Before that let's look at the remaining part of the puzzle: Attaching the view to the ToolBarDesignEditor and opening a section.

While opening the SectionPanel note that we need to supply the proper element (Book, in this case) so that the SectionInnerPanel gets this object for processing. You can override the validateView() method to implement the validation while creating new tags and editing the existing ones here. But you can also supply validation in the section inner panel, which would give more control over the individual UI fields as they are local and accessible there.

Section inner panels

The section inner panels are the actual UI elements that are shown on the multiview element. 

 

The BookPanel extends from the SectionInnerPanel, which is essentially extends from JPanel. So you could create a JPanel and create all UI elements using GUI builder and later change the super class to SectionInnerPanel. Note the constructor of BookPanel. It accepts the SectionView associated with this panel from the previous  ToolBarDesignEditor.  It also accepts the data object and the tag object, which is specific to the current panel. These are actually supplied by PanelFactory (which we will see next). So the ToolBarMVElement creates node structure and attaches the section panels and section containers, and Panel Factory invokes a particular SectionInner Panel upon getting a particular XML Tag object (which is basically a model object). In Section inner panel, you can set a particular validate method to your UI fields such as textfields and radio buttons. The line 44 shows one such example. XML multiview supports validation handling of most of the component except for most notably components : JList and JTable. For these components, we need to implement the validation and hook it to the existing validation mechanism. It us upto us, how we parse the data from the given data object and assign the data to the UI fields. This is done in the constructor. 

The setValue() method is a callback method and is called by the XML Multiview API. This callback is invoked on the focus lost event handling of the UI fields after validating them. So this way, user edited data gets stored in the model. We need to provide the implementation for this method if we want to use two way sync. The documentChanged()  method handles the validation error display task. For each UI field, we can specify the validation messages upon checking what failed. For example, at line 61, the emptyness of Book title field is checked and appropriate error message is picked up from the bundle and is shown using the methods provided by SectionView.

How this view (section inner panel) notifies the data object that view has changed? Through *UIChange() methods. There are startUIChange(), signalUIChange() and endUIChange() methods. Out of these, only endUIChange() is useful most of the time, we want to notify when user completes his udpates and the validation is succeeded. You may also note that the signalUIChange() method is deprecated. So this calls the modelUpdatedFromUI() method on the data object which will trigger the whole synchronization process as discussed in Part 1.

Panel Factory

This is the last piece (finally :) ). 

Factory design pattern is used here to create and return various section inner panels depending upon what key is passed. The key is one element in the model (or it is a XML tag). We need to create this panel factory to create and return our inner panels depending upon the model keys and Section view correctly embeds them and shows them.

This is end of Part 2 of this series. In this, we discussed how to provide custom UI sections to embed in the multiview elements. The next part shows how to add another multiview element and show a graph view of book and chapters using Visual Library 2.0.  See you soon!

Disclaimer: The modules described here are purely experimental so no guaranties. Use at your own risk.

Like this post? del.icio.us | furl | slashdot | technorati | digg

Part 1 - XML Multiview + Visual Library

Sunday Feb 25, 2007

 Updated for NetBeans 6.7

This part of XML multiview + Visual library tutorial describes how to start creating a XML multiview for a XML file. If you know how to create XML multiview and/or you have already followed example described in Geertjan's blog, wait for part 3 which shows how to integrate Visual Library into a multiview.

Contents

  1. Introduction
  2. Still not a standard, but..
  3. Book example
  4. Dig deep into the example
    1. How to set dependency
    2. Multiview code
    3. Customizing the DataObject
    4. Parse Document
    5. Construct MVDescription
    6. Attach multiview section to a XML element
    7. Model Synchronization
    8. Connect model synchronizer to the rest

Introduction

In NetBeans, to represent a file visually, you can use Multiview API to have multiple sectioned views. XML multiview builds top of this API and adds support for XML based data model. You can see the XML multiview in action while editing the web.xml in any Web application created inside NetBeans. Here is the screenshot:

Still not a standard API

XML multiview API is not a standard API now, but in future it may be included as a standard API.
Please note that both these APIs are still under development and they are subjected to change until they are published as stable APIs.

Please vote to the issue #107858 if you are using XML multiview in your development. More votes, more is the chance that one day XML multiview will become a stable API and be part of the official NetBeans platform.

If we want to use these APIs now, then we need to depend on the current implementation version. (You might know that every NetBeans module API specifies two kinds of version information: specification version and the implementation version.) If we depend upon the implementation version rather than the published specification version, there is quite a good chance that we will face some issues during deployment and providing updates.

Book example

With that let's get started. For this we will use the Geertjan's book example and extend it to integrate the Visual library to display Book and its chapters in a graph. First, download the Book sample project from his update center, or download it here. Unzip and open the project in NetBeans 5.5 IDE. When you run the project and open the abc.story file (from file menu), you can see the multiview in action.

Update : The Book multiview example is updated to run on the latest NetBeans 6.7 IDE. Please download it from here. Thanks to 'JS' from the comment section from this blog.

This book example, is part of the NetBeans source and it is a test project in automated unit tests for XML multiview module. This example was used by Geertjan to illustrate how the multiview looks.  If you have the 5.5 sources, you can find this project under <sources_root>/xml/multiview/test/unit. So this project contains the necessary support files and some additional code for the unit tests.

Digging deep into the example 

The following picture shows the BookMVEditorSample project view with relevant files highlighted.
In this sample,  the file abc.story is the XML file for which the XML multiview editor is written.  A new filetype support is added to this file to make NetBeans understand this file as some XML file. The advantage is getting XML editing support and DataObject support. There is a tutorial which explains how to add filetype support for an arbitrary file such as jar manifest file. BookDataLoader.java, BookDataNode.java, BookDataObject.java, BookResolver.java and layer.xml files are created when the new filetype support is added. The dataloader is a loader which creates dataobject from the FileObject. The datanode provides the view for the file. For example, you can set an icon for the file to show in the treeview, when this file is displayed in the explorer view such as Project view. The resolver file adds a MIME type support. the layer.xml file is the metafile for the whole module and it has the registration for the new filetype as below:


Book.java and Chapter.java are model classes. These hold the marshalled XML data. In the original example, these classes are generated using schema2beans library. Because of the loose coupling between XML multiview and the model used, we can use JAXB stubs too in place of these.

Setting dependency for the project 

As I mentioned earlier, the XML multiview extends the basic Multiview API. And as XML multiview API is not a standard API, the Book sample depends on the implementation version of it. You can do that for your project by adding a dependency and choosing the implementation version in the 'Edit dependency' dialog as below: 

XML multiview provides two types of built-in views : Toolbar view and TreePanelView. And there are base classes to implement these views. BookToolBarMVElement implements the toolbar view, while BookTreePanelMVElement implements a treepanel view. (refer the above projectview figure). The web.xml view is a toolbar view. The book example has code for both toolbar view and treepanelview (as you can see both files,BookToolBarMVElement and  BookTreePanelMVElement are present). We will use toolbar view, which will enable us to implement a visual graph in one of the multiview.

MultiView code

The BookDataObject is the starting point. The new filetype wizard will generate this file. For adding multiview support, this file extended to have multiview specific code. Specifically, the dataobject is changed to extend the XMLMultiViewDataObject, instead of UniDataObject as shown below.

Customizing the DataObject

Extend the constructor


Parse Document

Model synchronizer is the one which takes care of two way synchronization between the view and the XML file. We add CheckXMLCookie and ValidateXMLCookie to the existing list of cookies to make use of the XML support provided by the NetBeans platform. The parseDocument() method is of current interest.

Construct MultiView Description

As this example uses schema2beans library for generating the model, you see it used here in this method shown above. The Book is the  root tag. The call to Book.createGraph(), will result in parsing the whole XML file and constructing  a XML graph objects with the data filled in. If we plan to use some other library to generate the model, then here is the first place to edit. 


Next, the DesignView is the multiview descriptor class which constructs a multiview. We can see that this class constructs a toolbar view by returning BookToolBarMVElement at line 110. Note how the dataobject (model, in our case) is passed to the actual MVElement.


Now, return this DesignView in the overridden method 'getMultiViewDesc()' as above. Note that if we want, the TreePanelView instead of the toolbar view, here is the place to change.

Attach multiview section to a XML element

By default, the XML Multiview API provides the XML file view. We need to tell the API to open up a particular multiview section for particular XML tag. The showElement() does this.


Model Synchronization

The model synchronization is handled by extending the ModelSynchronizer class as follows:


You need to return true from mayUpdateData() to tell the API that you need synchronization and this model synchronization should be called to do that. The updateDataFromModel() method does the actual sync. It takes the model object and calls the write() method of BaseBean, which is part of schema2beans. Again, this is the place to change if we want to use other derived model classes such as from JAXB.  In the original book example, there are two errors: first, the call to the datacache is commented out at line 173, which turns off the sync altogether and second, the updateDataFromModel() is overridden wrongly. The filelock object is of the type: org.openide.filesystems.FileLock and not java.nio.channels.FileLock. If you have downloaded this example from my blog link, you have these errors fixed already.

Connect model synchronizer to the rest

The above code calls the model synchronizer when needed. This call usually originates from the view.

This is the end of Part 1 of this series. In this, we explored what is XML multiview and how to start implementing one for a custom XML file. The next part shows how we can actually provide the UI in the multiview elements.

Disclaimer: The modules described here are purely experimental so no guaranties. Use at your own risk.

[18] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg

XML Multiview + Visual Library

Friday Feb 23, 2007

I have merged XML Multiview with Visual Library to come up with a Netbeans module. Here is the screenshot (scrambled one). Will post the tutorial and code in the next blog entry.

 

 

[0] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg

Visual Library 2.0 javadoc in usual way

Sunday Feb 11, 2007

The existing link for the Visual Library (graph module) javadoc is kind of different than the usual one. Also, this is outdated, it does not list the new classes for example: TwoStateHoverProvider. Here are the zip files generated from the sources. org.netbeans.visual and org.netbeans.util

If you want to use, ust download them to the local folder and unzip or import them to the IDE itself so that when you place the cursor on any related class, and chose Tools/Javadoc Index Search, the javadoc browser shows the javadoc inside the IDE itself.



[0] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg