Pages

Thursday, August 31, 2006

New Datatype support and how to add more

I figured it's about time I blog something again. Sue has been covering a bunch of 1.1 features that we've been doing. I'll cover a couple more and how they are implemented and what that means to people writing custom extension.


The general idea is to make datatypes extensible such that anyone can write a presentation for one with very little code. There's a new interface which is registered by the class you want to render such as this first example which is for XMLType.


The call to register would look like this.




CellEditingFactory.registerCellRenderer(OPAQUE.class, new XMLTypeEditor());


Then the class XMLTypeEditor simply implements the new interface which is pretty leaves things open enough to do anything. There's 2 methods in it. The first getComponent this will put in the place of the cell in the grid what ever is returned and it override the default. For this example there's a JPanel created which is just the xml with a button. Since the button is part of the component, it can do anything which in this case pops up a modal with more space to view the data.

The second method is called when the first returns null.  The idea behind this is that someone may not need or want to build out a Component to render but simply want to create the text representation of the data.  This will use the text returned and put it into a normal table cell.

public interface ICellEditor {
public abstract Component getComponent(JTable table, Object value, int row, int column);
public abstract String getText(JTable table, Object value,int row, int column);
}





Here's the what it takes to handle XMLType


public class XMLTypeEditor implements ICellEditor {



public Component getComponent(final JTable table, Object value, int row, int column) {
XMLType xml = null;
final JPanel pnl = new JPanel(new BorderLayout());
// convert value to xml for convience
try {
if (value instanceof XMLType) {
xml = (XMLType) value;
} else if (value instanceof OPAQUE && ((OPAQUE) value).getSQLTypeName().equals("SYS.XMLTYPE")) {
xml = XMLType.createXML((OPAQUE) value);
}
}
catch (SQLException se) {}
// must not be interested
// return null and let someone else render it
if (xml == null) {
return null;
}
try {
// create the returned component
JLabel lbl = new JLabel();
// set the text
lbl.setText(xml.getStringVal());
// set the tooltop
lbl.setToolTipText(xml.getStringVal());
// add the label to the center of the panel
pnl.add(lbl, BorderLayout.CENTER);
// create the ... button
final JButton btn = new JButton(IdeIcons.getIcon(IdeIcons.EDIT_ICON));
btn.setMargin(new Insets(2, 2, 2, 2));
// add the button to the panel
pnl.add(btn, BorderLayout.EAST);

// make a final so the action listener can access the xml
final XMLType xmlForPopup = xml;
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
// create editor pane
BasicEditorPane codeArea = EditorFactory.createIdeEditorPane();
// Force the editor to highlight with xml
codeArea.setDocument(new BasicDocument("foo.xml")); // NORES
// Add a line highlight plugin to highlight the caret
// position line
try {
codeArea.setText(xmlForPopup.getStringVal());
}
catch (SQLException e) {
e.printStackTrace();
}
// add the highlighter
codeArea.installPlugin(new LineHighlightPlugin());
// non- editable
codeArea.setEditable(false);
// add the gutter for line numbers
LineGutterPlugin sqlGutter = new LineGutterPlugin();
codeArea.installPlugin(sqlGutter);
// put the editor into a scrollpane
JScrollPane sp = new JScrollPane(codeArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
sp.setRowHeaderView(sqlGutter);
JPanel popupPanel = new JPanel(new BorderLayout());
popupPanel.add(sp, BorderLayout.CENTER);
popupPanel.setPreferredSize(new Dimension(200, 200));
// helper class to show the panel int a popup
UIUtils.showPanelAsDialog(popupPanel, "XML Data", null, JEWTDialog.BUTTON_CLOSE);
// grab the focus back to the table so <TAB> keeps working
table.requestFocusInWindow();
}
});
}
catch (SQLException e) {
// Guess we didn't care if and exception was thrown
// so return null so the next one can try and render
return null;
}
return pnl;
}
public String getText(JTable table, Object value, int row, int column) {
return null;
}


Here's another screen shot of an implementation for a cursor




Hopefully this is easy enough so that anyone can create renders for datatype which have not made it yet.


[posted with ecto]

2 comments:

Anonymous said...

This is really helpful, but I think I am missing something. Where do you make the call to registerCellRenderer()? Are you editing the source for sql developer, or are you building a new jar and adding it to the classpath?

Kris Rice said...

Take a look at this http://krisrice.blogspot.com/2006/11/getting-started-with-extension.html

It shows how to get an started with extending.

-kris