com.borland.primetime.ui
package contains many support classes
that can be useful not only in building your user interfaces but also
help you to duplicate the Look & Feel of JBuilder within your own wizards. The
majority of these classes are public yet they aren't fully documented as part of
the OpenTools API. All of the classes are Swing-based. These are the ones discussed here:
ButtonStrip
ButtonStripConstrained
CheckTree
CheckTreeNode
ColorCombo
ColorPanel
CompositeIcon
DefaultDialog
DialogValidator
EdgeBorder
ImageListIcon
LazyTreeNode
ListPanel
MergeTreeNode
SearchTree
Splitter
VerticalFlowLayout
ButtonStrip
extends JPanel
and uses a FlowLayout
to display a single row of buttons either vertically or horizontally. The default
layout is horizontal with right-alignment and a horizontal/vertical gap of
5 pixels, but you can change this with constructor parameters
or by using either the setAlignment()
or setOrientation()
methods
after constructing ButtonStrip
:
ButtonStrip buttonStrip = new ButtonStrip(FlowLayout.RIGHT, 5); buttonStrip.add(okButton); buttonStrip.add(cancelButton);Unfortunately, the
FlowLayout
used by ButtonStrip
doesn't display one or more buttons when there is insufficient space to draw them completely.
For that reason there is the alternative ButtonStripConstrained
class which uses GridBagLayout
(although it only supports a horizontal
layout with right-alignment of the components at this time). This layout makes better use of the available space and clips buttons that don't fit completely.
ButtonStripConstrained buttonStrip = new ButtonStripConstrained(); buttonStrip.add(okButton); buttonStrip.add(cancelButton);
CheckTreeCellEditor
,
CheckTreeCellRenderer
, CheckCellRenderer
,
and CheckBoxCellRenderer
) provide an
implementation of a JTree
where each CheckTreeNode
includes
a checkbox. An ItemListener
event fires when a checkbox is
checked or unchecked by the user.
public class MyPropertyPanel extends JPanel { public class OptionTreeNode extends CheckTreeNode { public OptionTreeNode(String label, boolean isCheckable, OptionTreeNode parent) { super(label, isCheckable); setText(label); if (parent != null) { parent.add(this); } } } JTree optionTree = new CheckTree(); OptionTreeNode root = new OptionTreeNode("Root", false, null); OptionTreeNode optionsGroup = new OptionTreeNode("Options", false, root); OptionTreeNode showErrorsOption = new OptionTreeNode("Show error messages", true, optionsGroup); DefaultTreeModel model = new DefaultTreeModel(root); public MyPropertyPanel() { try { jbInit(); } catch(Exception ex) { ex.printStackTrace(); } } public void writeProperties() { optionTree.stopEditing(); System.out.println("Show error messages " + showErrorsOption.isChecked()); } public void readProperties() { optionTree.stopEditing(); showErrorsOption.setChecked(false); } void jbInit() throws Exception { // build UI optionTree.setModel(model); } }
ColorPanel
extends JPanel
. It displays sixteen colors in a
grid on the left, and eight custom colors (optionally passed as parameters)
in a grid to the right. The number of displayed rows defaults
to 2.
ColorCombo
extends JComboBox
. It displays the currently
selected color in its edit field and displays a ColorPanel
as its drop-down box when it's clicked. The control fires ActionListener
events when a value is selected. It doesn't fire ItemListener
events.
ColorCombo cc = new ColorCombo(null, 2, ColorCombo.RIGHT); cc.setSelectedColor(Color.red); cc.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Color color = cc.getSelectedColor(); } });
CompositeIcon
takes an array of icons and displays them in a single row. The row height
is that of the tallest icon in the array and the icons are centered
vertically in the row.
DefaultDialog
extends JDialog
. It provides some convenient mechanisms to support your
dialog design. These include auto-centering, frame finding, default button handling,
and minimum size enforcement. When the user closes the dialog by choosing the OK button and if you provided an optional component using the
DialogValidator
interface in your showModalDialog()
call, the DefaultDialog
invokes that interface, permitting it to decide if the dialog can be closed.
DefaultDialog
can provide a shell to display your panel:
MyPanel panel = new MyPanel(); if (DefaultDialog.showModalDialog(Browser.getActiveBrowser(), "My Title", panel, null, null)) { System.out.println("pressed OK"); }
Or you can extend it for more convenient access to its features:
MyDialog dlg = new MyDialog(Browser.getActiveBrowser(), "My Title", true); dlg.show(); public class MyDialog extends DefaultDialog { public SelectWindowDialog(Component owner, String title, boolean modal) { super(owner, title, modal); setAutoCenter(true); } }
EdgeBorder
class implements the Border
interface, providing a two-pixel inset where shadow and highlight 3D effects are drawn.
statusPanel.setBorder(new EdgeBorder(EdgeBorder.EDGE_TOP));
ImageListIcon
allows you to extract a single Icon
from a horizontal strip.
Each bitmap is square and each has the same dimensions.
public static Image IMAGE_ACTIONS = ImageLoader.loadFromResource("icons16x16.gif", BrowserIcons.class); public static ImageListIcon ICON_CUT = new ImageListIcon(IMAGE_ACTIONS, 16, 0); public static ImageListIcon ICON_COPY = new ImageListIcon(IMAGE_ACTIONS, 16, 1); public static ImageListIcon ICON_PASTE = new ImageListIcon(IMAGE_ACTIONS, 16, 2);
javax.swing.tree.TreeNode
. You should use
LazyTreeNode
when, for performance or other reasons, you want to
provide access only to your children when the user asks for them. When that happens,
your implementation of the createNodes()
method is called to
produce them. For example,
setRoot(new LazyTreeNode() { public TreeNode[] createNodes() { if (myList == null) { return EMPTY_NODES; } TreeNode[] nodes = new TreeNode[myList.size()]; for (int index = 0; index < nodes.length; index++) { nodes[index] = (TreeNode)myList.get(index); } return nodes; } );
ListPanel
is an abstract class and that extends JPanel
.
It provides a scrolling JList
next to five buttons which are by default labeled
"Add...", "Edit...", "Remove", "Move Up", and "Move Down."
You can change the first three labels by using the appropriate constructor to construct the class. If you override the getListCellRendererComponent()
method, you
can do custom rendering of the list elements, such as supplying a different
color for particular elements.
public class MyPanel extends ListPanel { public MyPanel() { } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { defaultListCellRenderer.getListCellRendererComponent(list, getElementName(value), index, isSelected, cellHasFocus); if (value instanceof JPanel) { defaultListCellRenderer.setForeground(Color.gray); } return defaultListCellRenderer; } // Called on Add... protected Object promptForElement() { return null; } // Called on Edit... protected Object editElement(Object listElement) { return null; } }
MergeTreeNode
extends javax.swing.tree.DefaultMutableTreeNode
. Use MergeTreeNode
if you have a JTree
that you need
to update while still preserving any node expansions. Usually you do this in implementations that provide structure pane content. First build a second instance of the tree
and then invoke the mergeChildren()
method to update the original tree.
public class MyTextStructure extends TextStructure { public MyTextStructure() { treeModel.setRoot(new MyMergeTreeNode(null)); } class MyMergeTreeNode extends MergeTreeNode { public MyMergeTreeNode(Object userObject) { super(userObject); } public void sortChildren() { MergeTreeNode[] array = getChildrenArray(); if (array == null) return; Arrays.sort(array, new Comparator() { public int compare(Object o1, Object o2) { // Do comparison here between two tree nodes return 0; } }); children = new Vector(Arrays.asList(array)); sortDescendants(); } public void sortDescendants() { if (children != null) { Enumeration e = children.elements(); while (e.hasMoreElements()) { ((MyMergeTreeNode)e.nextElement()).sortChildren(); } } } } public void updateStructure(Document doc) { final MyMergeTreeNode newRoot = new MyMergeTreeNode(null); try { // Build a new structure tree using newRoot here // Prepare an object that updates the model Runnable update = new Runnable() { public void run() { MyMergeTreeNode root = (MyMergeTreeNode)treeModel.getRoot(); // Merge the new model into the old so that expansion paths can be // preserved root.mergeChildren(newRoot); // Sort everything including the top level of the structure tree root.sortChildren(); // Update the display treeModel.nodeStructureChanged(root); } }; // Update the model on the main swing thread... if (SwingUtilities.isEventDispatchThread()) update.run(); else SwingUtilities.invokeLater(update); } catch (java.io.IOException ex) { } } }
SearchTree
extends JTree
. When you enter a number or letter
or one of the supported regular expression characters, it displays
a JTextField
with the text "Search for:" followed by the input.
With each character entered, it attempts to match the entire string
with a visible node causing the first matched node to be selected. If the
selected node has children, entering a period while depressing Ctrl expands the node. The Enter or Esc key cancels the window. For this to work, each
TreeNode must supply the same text in its toString()
method as it is
displaying in the tree.
public class MyPanel extends JPanel { BorderLayout layout = new BorderLayout(); JScrollPane scroller = new JScrollPane(); SearchTree tree = new SearchTree() { public void updateUI() { super.updateUI(); unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_A, Event.CTRL_MASK)); } }; public MyPanel() { try { jbInit(); } catch (Exception ex) { ex.printStackTrace(); } } private void jbInit() throws Exception { this.setLayout(layout); this.add(scroller, BorderLayout.CENTER); tree.setPreserveExpansion(true); scroller.getViewport().add(tree, null); } }
Splitter
extends JPanel
. It is intended to
hold one or two JComponent
objects. If there is more than one, a draggable bar that is used to adjust the allocation of space between components appears. The components can be oriented vertically or horizontally.
SplitterTestFrame f = new SplitterTestFrame(); f.setBounds(100, 100, 500, 500); f.show(); public class SplitterTestFrame extends JFrame { Splitter splitterA = new Splitter(true, Splitter.PROPORTIONAL, 0.8f); Splitter splitterB = new Splitter(false, Splitter.PROPORTIONAL, 0.2f); Splitter splitterC = new Splitter(true, Splitter.PROPORTIONAL, 0.5f); JButton pv = new JButton("ProjectView"); JButton sv = new JButton("StructureView"); JButton cv = new JButton("ContentView"); JButton mv = new JButton("MessageView"); public SplitterTestFrame() { try { jbInit(); } catch (Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { getContentPane().setLayout(new BorderLayout()); getContentPane().add(splitterA, BorderLayout.CENTER); splitterA.setFirstComponent(splitterB); splitterA.setSecondComponent(mv); splitterA.setProportion(0.8f); pv.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { mv.setVisible(!mv.isVisible()); splitterA.validate(); } }); sv.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { splitterB.setDividerSize(splitterB.getDividerSize() > 4 ? 4 : 10); } }); cv.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { pv.setVisible(!pv.isVisible()); splitterB.validate(); } }); mv.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { sv.setVisible(!sv.isVisible()); splitterB.validate(); } }); splitterB.setFirstComponent(splitterC); splitterB.setSecondComponent(cv); splitterB.setProportion(0.2f); splitterC.setFirstComponent(pv); splitterC.setSecondComponent(sv); splitterC.setProportion(0.5f); } }
FlowLayout
, but layouts out components vertically
instead of horizontally.
int hGap = 5; int vGap = 5; int hFill = true; int vFill = false; setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, hGap, vGap, hFill, vFill));