/*
fieldoption_editor.java
TreeTable GUI component dialog used by the client to present and
edit synchronization options for the new Ganymede 2.0 sync
channels.
Created: 2 February 2005
Module By: Deepak Giridharagopal
-----------------------------------------------------------------------
Ganymede Directory Management System
Copyright (C) 1996-2013
The University of Texas at Austin
Ganymede is a registered trademark of The University of Texas at Austin
Contact information
Web site: http://www.arlut.utexas.edu/gash2
Author Email: ganymede_author@arlut.utexas.edu
Email mailing list: ganymede@arlut.utexas.edu
US Mail:
Computer Science Division
Applied Research Laboratories
The University of Texas at Austin
PO Box 8029, Austin TX 78713-8029
Telephone: (512) 835-3200
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package arlut.csd.ganymede.client;
import java.awt.*;
import java.awt.event.*;
import java.rmi.RemoteException;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.table.*;
import javax.swing.border.*;
import arlut.csd.Util.PackageResources;
import arlut.csd.Util.TranslationService;
import arlut.csd.ganymede.common.*;
import arlut.csd.ganymede.rmi.*;
/*------------------------------------------------------------------------------
class
fieldoption_editor
------------------------------------------------------------------------------*/
/**
* This class displays the client "widget" that allows a user to edit the
* field options for a particular builder task. It's modeled loosely after
* the permissions editor widget, and uses the same TreeTable component.
*/
public class fieldoption_editor extends JFrame
{
/**
* TranslationService object for handling string localization in
* the Ganymede system.
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.ganymede.client.fieldoption_editor");
public static boolean debug = false;
// ---
/**
* Flag that indicated whether or not the widget it currently being
* displayed or not.
*/
private boolean isActive = true;
/**
* Reference to the actual field option data store this widget represents
*/
private field_option_field opField;
/**
* Should the widget be displayed read-only or not?
*/
private boolean editable;
/**
* Are we presenting a full-state sync channel or an incremental?
*/
private boolean fullstate;
/**
* The root of our tree, which is in column 0 of our tree table
*/
private DefaultMutableTreeNode rowRootNode;
/**
* Reference to the main client class
*/
private gclient gc;
/* Layout components */
private JButton OkButton = new JButton (ts.l("global.okButton")); // "Ok"
private JButton CancelButton = new JButton(ts.l("global.cancelButton")); // "Cancel"
private JButton ExpandButton = new JButton (ts.l("global.expandButton")); // "Expand All"
private JButton CollapseButton = new JButton(ts.l("global.collapseButton")); // "Collapse All"
private JScrollPane edit_pane;
private JPanel
Base_Panel,
Bordered_Panel,
Choice_Buttons,
Expansion_Buttons,
All_Buttons;
private Frame parent = null;
JTreeTable treeTable;
JTree tree;
/* -- */
/**
* @param opField The server-side field_option_field RMI reference this fieldoption_editor is to manipulate.
* @param editable If false, this fieldoption_editor will be display-only.
* @param fullstate If true, the 'when changed' option will not be present, and the choices will devolve to 'always' and 'never'.
* @param gc The gclient that connects us to the client-side schema caches
* @param parent The frame we are attaching this dialog to
* @param DialogTitle The title for this dialog box
*/
public fieldoption_editor(field_option_field opField,
boolean editable,
boolean fullstate,
gclient gc,
Frame parent,
String DialogTitle)
{
super(DialogTitle);
this.parent = parent;
this.opField = opField;
this.editable = editable;
this.fullstate = fullstate;
this.gc = gc;
if (!debug)
{
this.debug = gc.debug;
}
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
myshow(false);
}
});
/* Change the images to match the client */
UIManager.put("Tree.leafIcon", new ImageIcon(PackageResources.getImageResource(this, "i043.gif", getClass())));
UIManager.put("Tree.openIcon", new ImageIcon(PackageResources.getImageResource(this, "openfolder.gif", getClass())));
UIManager.put("Tree.closedIcon", new ImageIcon(PackageResources.getImageResource(this, "folder.gif", getClass())));
UIManager.put("Tree.expandedIcon", new ImageIcon(PackageResources.getImageResource(this, "minus.gif", getClass())));
UIManager.put("Tree.collapsedIcon", new ImageIcon(PackageResources.getImageResource(this, "plus.gif", getClass())));
/* Group the OK and Cancel buttons together */
Choice_Buttons = new JPanel();
if (editable)
{
Choice_Buttons.setLayout(new GridLayout(1,2));
Choice_Buttons.setBorder(new EmptyBorder(new Insets(5,5,5,5)));
if (glogin.isRunningOnMac())
{
Choice_Buttons.add(CancelButton);
Choice_Buttons.add(OkButton);
}
else
{
Choice_Buttons.add(OkButton);
Choice_Buttons.add(CancelButton);
}
}
else
{
Choice_Buttons.setLayout(new GridLayout(1,1));
Choice_Buttons.setBorder(new EmptyBorder(new Insets(5,5,5,5)));
OkButton.setText(ts.l("global.closeButton")); // "Close"
Choice_Buttons.add(OkButton);
}
/* Group the Expand and Collapse buttons together */
Expansion_Buttons = new JPanel();
Expansion_Buttons.setBorder(new EmptyBorder(new Insets(5,5,5,5)));
Expansion_Buttons.setLayout(new GridLayout(1,2));
Expansion_Buttons.add(ExpandButton);
Expansion_Buttons.add(CollapseButton);
/* Now take *those* groups and group them together */
All_Buttons = new JPanel();
All_Buttons.setLayout(new BorderLayout());
All_Buttons.add("West", Expansion_Buttons);
All_Buttons.add("East", Choice_Buttons);
/* Setup the tree table */
try
{
rowRootNode = initRowTree();
if (rowRootNode == null)
{
this.dispose();
return;
}
}
catch (Exception ex)
{
gc.processExceptionRethrow(ex);
}
TreeTableModel model = new FieldOptionModel(rowRootNode, this);
treeTable = new JTreeTable(model);
tree = treeTable.getTree();
tree.setCellRenderer(new FieldOptionTreeRenderer(this));
/* Set the correct initial states of all the object base nodes in the tree */
fixObjectBaseNodes(rowRootNode, (FieldOptionModel)model);
treeTable.setDefaultRenderer(Integer.class,
new DelegateRenderer((TreeTableModelAdapter)treeTable.getModel(), editable, treeTable));
treeTable.setDefaultEditor(Integer.class,
new DelegateEditor((TreeTableModelAdapter)treeTable.getModel(), editable, treeTable));
/* Expand only nodes with non-default values */
collapseAllNodes();
smartExpandNodes();
edit_pane = new JScrollPane(treeTable);
edit_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
edit_pane.getViewport().setOpaque(true);
edit_pane.getViewport().setBackground(Color.white);
Base_Panel = new JPanel();
Base_Panel.setLayout(new BorderLayout());
Base_Panel.add("Center", edit_pane);
Base_Panel.add("South", All_Buttons);
Bordered_Panel = new JPanel();
Bordered_Panel.setLayout(new BorderLayout());
Bordered_Panel.add("Center", Base_Panel);
getContentPane().setLayout(new BorderLayout());
getContentPane().add("Center", Bordered_Panel);
/* Register event handlers */
if (editable)
{
OkButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
commitChanges();
}
});
}
else
{
OkButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
myshow(false);
}
});
}
CancelButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
myshow(false);
}
});
ExpandButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
expandAllNodes();
}
});
CollapseButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
collapseAllNodes();
}
});
gc.setWaitCursor();
setSize(550,550);
this.setLocationRelativeTo(parent);
myshow(true);
gc.setNormalCursor();
}
/**
* This method will create a tree of row info that will be used
* to store the field option values for the base and basefields.
* It returns a DefaultMutableTreeNode as the root of the tree structure
*/
private DefaultMutableTreeNode initRowTree() throws RemoteException
{
FieldOptionMatrix matrix;
BaseDump base;
FieldTemplate template;
SyncPrefEnum basevalue, fieldvalue;
SyncPrefEnum entry;
Vector<FieldTemplate> fields;
short id;
String name;
DefaultMutableTreeNode rootNode, baseNode, fieldNode;
matrix = opField.getMatrix();
rootNode = new DefaultMutableTreeNode(new FieldOptionRow(null, null, SyncPrefEnum.NEVER));
/* Get a list of base types from the gclient
* (we really just care about their names and id's)
*/
for (Base baseRef: gc.getBaseList())
{
base = (BaseDump) baseRef;
id = base.getTypeID();
name = base.getName();
/*
* We'll go ahead and hard-code the option value of this object base as
* "0", aka "Never". A future call to the fixObjectBaseNodes() method
* will make sure that the checkbox for this object base has the
* appropriate state.
*
* We do this because if we go ahead and set the value of this object
* base to anything other than 0, the GUI will automatically flip all
* of its constituent fields to "1", aka "When changed". We don't want
* this, so we'll skip reading the object base's value from the db.
*/
basevalue = SyncPrefEnum.NEVER;
baseNode = new DefaultMutableTreeNode(new FieldOptionRow(base, null, basevalue));
rootNode.add(baseNode);
/* Now add all of the fields for this base as child nodes */
fields = (Vector<FieldTemplate>) gc.getTemplateVector(id);
for (int j=0; fields != null && (j < fields.size()); j++)
{
/* get the field options for this field */
template = fields.get(j);
if (base.isEmbedded() && template.getID() == SchemaConstants.ContainerField)
{
continue; // the container field is always implicit for embedded objects
}
entry = matrix.getOption(id, template.getID());
if (entry == null)
{
/* if no option is explicitly recorded for this
* field, use the record for the base */
fieldvalue = basevalue;
}
else
{
/* FIXME: This assumes that the field options are stored in
* the database as Strings containing integers. At the moment,
* this is a totally hard-coded assumption. Presumably, this
* will be remedied with an interface that declares some
* constants or the like. */
fieldvalue = entry;
}
/* Create a new child node for this field and add it to the node
* for the DBObjectBase. */
fieldNode = new DefaultMutableTreeNode(new FieldOptionRow(base, template, fieldvalue));
baseNode.add(fieldNode);
if (debug)
{
System.err.println("Reading " + name + "." + template.getName() + " from db, value of " + basevalue);
}
}
}
return rootNode;
}
/**
* Loops over every object base node in the tree, setting its check-box state
* to what it should be based on the values of its fields
*/
public void fixObjectBaseNodes(DefaultMutableTreeNode root, FieldOptionModel model)
{
for (Enumeration e = root.children(); e.hasMoreElements();)
{
DefaultMutableTreeNode child = (DefaultMutableTreeNode) e.nextElement();
FieldOptionRow myRow = (FieldOptionRow)child.getUserObject();
if (myRow.isBase())
{
model.fixObjectBaseNode(child);
}
}
}
/**
* Method to pop-up/pop-down the editor
*/
public void myshow(boolean truth_value)
{
if (truth_value)
{
setVisible(true);
}
else
{
isActive = false;
cleanUp();
}
}
/**
* Am I currently being displayed or not?
*/
public boolean isActiveEditor()
{
return isActive;
}
public boolean isEditable()
{
return this.editable;
}
public boolean isFullState()
{
return this.fullstate;
}
/**
* Expands all of the nodes in the JTree
*/
private void expandAllNodes()
{
for (Enumeration e = (rowRootNode.children()); e.hasMoreElements();)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.nextElement();
FieldOptionRow myRow = (FieldOptionRow)node.getUserObject();
TreePath path = new TreePath(node.getPath());
tree.expandPath(path);
}
}
/**
* Collapses all of the nodes in the JTree
*/
private void collapseAllNodes()
{
for (Enumeration en = (rowRootNode.children()); en.hasMoreElements();)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode)en.nextElement();
TreePath path = new TreePath(node.getPath());
tree.collapsePath(path);
}
}
/**
* Expands the nodes for object bases that contain fields with non-default
* options.
*/
private void smartExpandNodes()
{
for (Enumeration en = (rowRootNode.children()); en.hasMoreElements();)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode)en.nextElement();
FieldOptionRow row = (FieldOptionRow)node.getUserObject();
if (row.isBase() && (row.getOptionValue() != SyncPrefEnum.NEVER))
{
TreePath path = new TreePath(node.getPath());
tree.expandPath(path);
}
}
}
/**
* Writes all changes the user has made back to the data store.
*/
public void commitChanges()
{
FieldOptionRow ref;
short baseid;
BaseDump bd;
String baseName, templateName;
FieldTemplate template;
SyncPrefEnum value;
if (debug)
{
System.out.println("Ok was pushed");
}
Enumeration en = rowRootNode.preorderEnumeration();
while (en.hasMoreElements())
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode)en.nextElement();
ref = (FieldOptionRow)node.getUserObject();
if (ref.isChanged())
{
gc.somethingChanged();
if (ref.isBase())
{
bd = (BaseDump) ref.getReference();
value = ref.getOptionValue();
try
{
if (debug)
{
System.err.println("Setting " + bd.getName() + " to " + value);
}
gc.handleReturnVal(opField.setOption(bd.getTypeID(), value));
}
catch (Exception ex)
{
gc.processExceptionRethrow(ex);
}
}
else
{
template = (FieldTemplate) ref.getReference();
templateName = template.getName();
value = ref.getOptionValue();
if (debug)
{
System.err.println("Setting " + templateName + " to " + value);
}
try
{
gc.handleReturnVal(opField.setOption(template.getBaseID(), template.getID(), value));
}
catch (Exception ex)
{
gc.processExceptionRethrow(ex);
}
}
}
}
myshow(false);
return;
}
/**
* This method pops down the widget and does some variable
* clearing to assit in garbage collection.
*/
public void cleanUp()
{
setVisible(false);
gc = null;
OkButton = null;
CancelButton = null;
ExpandButton = null;
CollapseButton = null;
parent = null;
}
}
/*------------------------------------------------------------------------------
class
FieldOptionRow
------------------------------------------------------------------------------*/
/**
* An instance of this class is attached to each node in the JTree. The hooks
* in this class allow us to refer to the DBObjectBase / DBObjectBaseField
* that the corresponding node in the tree represents. It also holds the value
* of the options for each node in the tree.
*/
class FieldOptionRow
{
/**
* Either a BaseDump or a FieldTemplate object associated with this
* node in the tree
*/
private Object reference;
/**
* The actual field option value
*/
private SyncPrefEnum opValue;
/**
* True if we've edited the contained field option value
*/
private boolean changed;
/**
* Title of the tree node
*/
private String name;
/**
* Is this row representing a built-in field or not?
*/
private boolean builtin = false;
/* -- */
public FieldOptionRow(BaseDump base, FieldTemplate field, SyncPrefEnum opValue)
{
this.opValue = opValue;
if (base == null)
{
reference = null;
name = "Root";
}
else
{
if (field == null)
{
reference = base;
name = base.getName();
}
else
{
reference = field;
name = field.getName();
builtin = field.isBuiltIn();
}
}
}
public String toString()
{
return name;
}
public boolean isChanged()
{
return changed;
}
public void setChanged(boolean value)
{
changed = value;
}
public boolean isBuiltIn()
{
return builtin;
}
public void setBuiltIn(boolean builtin)
{
this.builtin = builtin;
}
public SyncPrefEnum getOptionValue()
{
return opValue;
}
public void setOptionValue(SyncPrefEnum newVal)
{
this.opValue = newVal;
}
/**
* Returns the current base or field object
*/
public Object getReference()
{
return reference;
}
/**
* Returns if the current row is dealing w/ base or field
*/
public boolean isBase()
{
if (reference instanceof BaseDump)
{
return true;
}
else
{
return false;
}
}
}
/*------------------------------------------------------------------------------
class
FieldOptionModel
------------------------------------------------------------------------------*/
/**
* Custom TreeTableModel model for use with the Ganymede client's
* {@link arlut.csd.ganymede.client.fieldoption_editor fieldoption_editor}
* field options editor dialog.
*/
class FieldOptionModel extends AbstractTreeTableModel implements TreeTableModel
{
/**
* TranslationService object for handling string localization in
* the Ganymede system.
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.ganymede.client.FieldOptionModel");
static protected Class[] cTypes = {TreeTableModel.class, Integer.class};
static protected String[] cNames = {ts.l("global.name"), ts.l("global.when")};
fieldoption_editor foe = null;
/* -- */
public FieldOptionModel(DefaultMutableTreeNode root, fieldoption_editor foe)
{
super(root);
this.foe = foe;
}
//
// The TreeModel interface
//
public int getChildCount(Object node)
{
return ((DefaultMutableTreeNode)node).getChildCount();
}
public Object getChild(Object node, int i)
{
return ((DefaultMutableTreeNode)node).getChildAt(i);
}
public boolean isLeaf(Object node)
{
return ((DefaultMutableTreeNode)node).isLeaf();
}
//
// The TreeTableNode interface.
//
public int getColumnCount()
{
return cNames.length;
}
public String getColumnName(int column)
{
return cNames[column];
}
public Class getColumnClass(int column)
{
return cTypes[column];
}
public Object getValueAt(Object node, int column)
{
FieldOptionRow myRow = (FieldOptionRow)((DefaultMutableTreeNode)node).getUserObject();
switch(column)
{
case 1:
return myRow.getOptionValue();
}
return null;
}
public boolean isCellEditable(Object node, int col)
{
// the tree column must not be editable, since the tree can't edit
// into the table properly. We'll depend on the JTreeTable's
// editCellAt() method to handle the tree's expansion and collapse
// as necessary without doing any 'editing'.
return !foe.treeTable.isHierarchical(col);
}
public void setValueAt(Object value, Object node, int col)
{
FieldOptionRow myRow = (FieldOptionRow)((DefaultMutableTreeNode)node).getUserObject();
if (fieldoption_editor.debug)
{
System.err.println("SETVALUE: " + myRow.toString() + " :: " + value.toString());
}
SyncPrefEnum newVal;
newVal = SyncPrefEnum.find((String) value);
if (col == 1 && foe.isFullState())
{
if (newVal == SyncPrefEnum.WHENCHANGED)
{
// we map the 2nd item in the full state list {never,
// always} to the third on the server, so as to handle the
// differing list of options applicable.
newVal = SyncPrefEnum.ALWAYS;
}
}
/* If we're not changing anything, then bail out */
if (myRow.getOptionValue() == newVal)
{
return;
}
switch (col)
{
/* case: 0 represents the column the tree is held in. You can't set that
* value, so we'll ignore it */
case 1:
myRow.setOptionValue(newVal);
myRow.setChanged(true);
if (myRow.isBase())
{
setBaseChildren((DefaultMutableTreeNode)node, newVal);
if (newVal == SyncPrefEnum.WHENCHANGED)
{
TreePath path = new TreePath(((DefaultMutableTreeNode)node).getPath());
foe.tree.expandPath(path);
}
else
{
TreePath path = new TreePath(((DefaultMutableTreeNode)node).getPath());
foe.tree.collapsePath(path);
}
}
else
{
fixObjectBaseNode((DefaultMutableTreeNode)((DefaultMutableTreeNode)node).getParent());
}
break;
}
/* Update table to reflect these changes */
TreeNode[] path = ((DefaultMutableTreeNode)node).getPath();
fireTreeNodesChanged(FieldOptionModel.this, path, null, null);
}
/**
* Make sure that the object base tree node ('node') has a state
* that reflects the options of all of its constituent fields.
*/
public void fixObjectBaseNode(DefaultMutableTreeNode node)
{
/* Is this node checked or not? */
boolean checked;
if (((SyncPrefEnum) getValueAt(node, 1)) == SyncPrefEnum.NEVER)
{
checked = false;
}
else
{
checked = true;
}
/* How many nodes have values other than 0? */
int numNonZeroNodes = 0;
for (Enumeration e = ((DefaultMutableTreeNode)node).children(); e.hasMoreElements();)
{
if (((SyncPrefEnum) getValueAt(e.nextElement(), 1)) != SyncPrefEnum.NEVER)
{
numNonZeroNodes++;
}
}
if (checked && (numNonZeroNodes == 0))
{
FieldOptionRow myRow = (FieldOptionRow)((DefaultMutableTreeNode)node).getUserObject();
myRow.setOptionValue(SyncPrefEnum.NEVER);
myRow.setChanged(true);
}
else if (!checked && (numNonZeroNodes > 0))
{
FieldOptionRow myRow = (FieldOptionRow)((DefaultMutableTreeNode)node).getUserObject();
myRow.setOptionValue(SyncPrefEnum.WHENCHANGED);
myRow.setChanged(true);
}
/* Update table to reflect these changes */
TreeNode[] path = ((DefaultMutableTreeNode)node).getPath();
fireTreeNodesChanged(FieldOptionModel.this, path, null, null);
}
/**
* Give the children of 'node' the value of 'value
*/
public void setBaseChildren(DefaultMutableTreeNode node, SyncPrefEnum value)
{
for (Enumeration e = node.children(); e.hasMoreElements();)
{
FieldOptionRow myRow = (FieldOptionRow)((DefaultMutableTreeNode)e.nextElement()).getUserObject();
myRow.setOptionValue(value);
myRow.setChanged(true);
}
}
}
/*------------------------------------------------------------------------------
class
DelegateRenderer
------------------------------------------------------------------------------*/
/**
* A cell renderer that delegates rendering functions to one of 2 classes.
* ObjectBases are rendered with a checkbox, and Fields are rendered with
* a combo box.
*/
class DelegateRenderer implements TableCellRenderer
{
/**
* Is the renderer read-only?
*/
boolean editable;
/**
* Hook for the backing store of the JTree. This gives us a way to access
* our FieldOptionRow objects attached to various nodes in the tree.
*/
TreeTableModelAdapter model;
/**
* Reference to the main treetable
*/
JTreeTable treetable;
/* -- */
public DelegateRenderer(TreeTableModelAdapter model, boolean editable, JTreeTable treetable)
{
this.editable = editable;
this.model = model;
this.treetable = treetable;
}
/* This implements the TableCellRenderer interface */
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) (model.nodeForRow(row));
FieldOptionRow qrow = (FieldOptionRow) (node.getUserObject());
SyncPrefEnum opvalue = qrow.getOptionValue();
/* ObjectBases are rendered as checkboxes */
if (qrow.isBase())
{
return new CheckBoxRenderer(editable, this.treetable, opvalue);
}
else
{
if (this.editable)
{
if (((FieldOptionModel) this.model.getTreeModel()).foe.isFullState())
{
return new FullStateComboRenderer(editable, this.treetable, opvalue);
}
else
{
return new ComboRenderer(editable, this.treetable, opvalue);
}
}
else
{
return new JLabel(opvalue.toString());
}
}
}
}
/*------------------------------------------------------------------------------
class
DelegateEditor
------------------------------------------------------------------------------*/
/**
* A cell editor that delegates editing functions to one of 2 classes.
* ObjectBases are handled with a checkbox, and Fields are handled with
* a combo box.
*/
class DelegateEditor extends javax.swing.AbstractCellEditor implements TableCellEditor
{
/**
* Is the renderer read-only?
*/
boolean editable;
/**
* Hook for the backing store of the JTree. This gives us a way to access
* our FieldOptionRow objects attached to various nodes in the tree.
*/
TreeTableModelAdapter model;
/**
* The component that's actually represents the edited cell
*/
Component delegate;
/**
* Quick-access reference to our main JTreeTable
*/
JTreeTable treetable;
/* -- */
public DelegateEditor(TreeTableModelAdapter model, boolean editable, JTreeTable treetable)
{
this.editable = editable;
this.model = model;
this.treetable = treetable;
}
/**
* Implementing the TableCellEditor interface
*/
public Object getCellEditorValue()
{
if (this.delegate instanceof JCheckBox)
{
if (((JCheckBox)this.delegate).isSelected())
{
return SyncPrefEnum.WHENCHANGED.toString();
}
else
{
return SyncPrefEnum.NEVER.toString();
}
}
else
{
return ((JComboBox)this.delegate).getSelectedItem();
}
}
public Component getTableCellEditorComponent(JTable table,
Object value,
boolean isSelected,
int row,
int column)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) (model.nodeForRow(row));
FieldOptionRow qrow = (FieldOptionRow) (node.getUserObject());
SyncPrefEnum opvalue = qrow.getOptionValue();
if (qrow.isBase())
{
CheckBoxRenderer cb = new CheckBoxRenderer(editable, this.treetable, opvalue);
this.delegate = cb;
return cb;
}
else
{
Component cr = null;
if (((FieldOptionModel) this.model.getTreeModel()).foe.isFullState())
{
cr = new FullStateComboRenderer(editable, this.treetable, opvalue);
}
else
{
cr = new ComboRenderer(editable, this.treetable, opvalue);
}
this.delegate = cr;
return cr;
}
}
}
/*------------------------------------------------------------------------------
class
CheckBoxRenderer
------------------------------------------------------------------------------*/
/**
* Renders the field options for an ObjectBase.
*/
class CheckBoxRenderer extends JCheckBox implements TableCellRenderer, ActionListener
{
/**
* Reference to the main JTreeTable
*/
JTreeTable treetable;
/* -- */
public CheckBoxRenderer(boolean editable, JTreeTable treetable, SyncPrefEnum onOrOff)
{
this.treetable = treetable;
setEnabled(editable);
/* Set the inital state of the checkbox */
if (onOrOff == SyncPrefEnum.NEVER)
{
setSelected(false);
}
else
{
setSelected(true);
}
this.setBackground(treetable.getTree().getBackground());
this.addActionListener(this);
}
/**
* Takes an Integer of value 0 or 1 and returns the corresponding
* boolean truth value.
*/
public boolean convertValueToBoolean(Object value)
{
int i = ((Integer)value).intValue();
if (i == 0)
{
return false;
}
else
{
return true;
}
}
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
{
setSelected(convertValueToBoolean(value));
return this;
}
public void actionPerformed(ActionEvent e)
{
/* Tell the tree table to call "setValueAt", since we've been edited */
this.treetable.editingStopped(new ChangeEvent(this));
}
}
/*------------------------------------------------------------------------------
class
ComboRenderer
------------------------------------------------------------------------------*/
/**
* Renders the field options for a DBObjectBaseField
*/
class ComboRenderer extends JComboBox implements TableCellRenderer, ItemListener
{
/**
* Reference to the main JTreeTable
*/
JTable treetable;
/**
* Sort of a hack; used to hold onto the previous selection index
*/
int selindex;
/* -- */
public ComboRenderer(boolean editable, JTreeTable treetable, SyncPrefEnum selectionIndex)
{
super(SyncPrefEnum.labels);
this.treetable = treetable;
this.selindex = selectionIndex.ord();
setEnabled(editable);
setEditable(false);
addItemListener(this);
setBackground(treetable.getTree().getBackground());
setSelectedIndex(selectionIndex.ord());
}
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
{
int labelIndex = ((SyncPrefEnum)value).ord();
setSelectedIndex(labelIndex);
this.selindex = labelIndex;
return this;
}
public void itemStateChanged(ItemEvent e)
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
/*
* Tell the tree table to call "setValueAt", since we've been edited.
* Now, we'll only do this if the new item we've selected is different
* from the originally selected item. Swing fires this even either way,
* but we need to be more discriminating.
*/
if (this.selindex != getSelectedIndex())
{
this.treetable.editingStopped(new ChangeEvent(this));
this.selindex = getSelectedIndex();
}
}
}
}
/*------------------------------------------------------------------------------
class
FullStateComboRenderer
------------------------------------------------------------------------------*/
/**
* Renders the field options for a DBObjectBaseField in a Full State Sync Channel
*/
class FullStateComboRenderer extends JComboBox implements TableCellRenderer, ItemListener
{
/**
* Reference to the main JTreeTable
*/
JTable treetable;
/**
* Sort of a hack; used to hold onto the previous selection index
*/
int selindex;
/* -- */
public FullStateComboRenderer(boolean editable, JTreeTable treetable, SyncPrefEnum selectionIndex)
{
super(SyncPrefEnum.fullStateLabels);
this.treetable = treetable;
this.selindex = selectionIndex.ord();
if (this.selindex == 2)
{
this.selindex = 1;
}
setEnabled(editable);
setEditable(false);
addItemListener(this);
setBackground(treetable.getTree().getBackground());
setSelectedIndex(this.selindex);
}
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
{
int labelIndex = ((SyncPrefEnum)value).ord();
if (labelIndex == 2)
{
// the full state mode only has two labels, not three, let's
// fold it down into the available label.
labelIndex = 1;
}
setSelectedIndex(labelIndex);
this.selindex = labelIndex;
return this;
}
public void itemStateChanged(ItemEvent e)
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
/*
* Tell the tree table to call "setValueAt", since we've been edited.
* Now, we'll only do this if the new item we've selected is different
* from the originally selected item. Swing fires this even either way,
* but we need to be more discriminating.
*/
if (this.selindex != getSelectedIndex())
{
this.treetable.editingStopped(new ChangeEvent(this));
this.selindex = getSelectedIndex();
}
}
}
}
/*------------------------------------------------------------------------------
class
FieldOptionTreeRenderer
------------------------------------------------------------------------------*/
/**
* Custom tree renderer that will give built-in fields a different icon.
*/
class FieldOptionTreeRenderer extends DefaultTreeCellRenderer
{
ImageIcon builtInIcon;
ImageIcon standardIcon;
public FieldOptionTreeRenderer(fieldoption_editor parent)
{
builtInIcon = new ImageIcon(PackageResources.getImageResource(parent, "list.gif", getClass()));
standardIcon = new ImageIcon(PackageResources.getImageResource(parent, "i043.gif", getClass()));
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus)
{
if (leaf)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
FieldOptionRow myRow = (FieldOptionRow) node.getUserObject();
if (myRow.isBuiltIn())
{
setLeafIcon(builtInIcon);
}
else
{
setLeafIcon(standardIcon);
}
}
return super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
}
}